(function () { 'use strict'; var app = angular.module('sysMgrApp') .directive('smNavTabs', ['$rootScope', '$location', '$state', '$stateParams', navTabViews]) .directive('smNavTab', ['$rootScope', '$location', '$state', '$stateParams', navTabView]); function navTabViews($rootScope, $location, $state, $stateParams) { return { restrict: 'E', transclude: true, replace: true, scope: {}, controller: function ($scope) { $rootScope.$state = $state; $rootScope.$stateParams = $stateParams; $rootScope.stateChangeAborted = false; var urlProtocolAndPath = $location.protocol() + '://' + $location.host() + ($location.port() != NaN ? ':' + $location.port() : ''); var panes = $scope.panes = []; $scope.select = function (uiView) { // call hook before changing state uiView.onActivating(uiView.name); // use $rootScope to make call to change the state $rootScope.$state.go(uiView.name, uiView.viewSettings.params, uiView.viewSettings.options); // set first call from 'addPane' function // to default tab if (panes.length === 0 && uiView.isVisible != false) { uiView.isActive = true; } }; // called from $stateNotFound, $stateChangeError, $stateChangeAborted // and $stateChangeSuccess event listeners $rootScope.setNavTabs = function (viewName) { var i = 0; for (i = 0; i < panes.length; i++) { var pane = panes[i]; // set to 'false' all but matching name pane.isActive = false; if ((pane.name == viewName)) { // recall hook if stateChangeAborted = true if ($rootScope.stateChangeAborted == true) pane.onActivating(viewName); pane.isActive = true; } } } //onStateChangeStart $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) { // reset stateChangeAborted flag $rootScope.stateChangeAborted = false; }) //onStateNotFound $rootScope.$on('$stateNotFound', function (event, unfoundState, fromState, fromParams) { $rootScope.stateChangeAborted = true; $rootScope.setNavTabs(fromState.name); }) //onStateChangeError' $rootScope.$on('$stateChangeError', function (event, toState, toParams, fromState, fromParams) { $rootScope.stateChangeAborted = true; $rootScope.setNavTabs(fromState.name); }) //onStateChangeAborted $rootScope.$on('$stateChangeAborted', function (event, toState, toParams, fromState, fromParams) { $rootScope.stateChangeAborted = true; $rootScope.setNavTabs(fromState.name); }) //onStateChangeSuccess $rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) { $rootScope.stateChangeAborted = false; $rootScope.setNavTabs(toState.name); }) this.addPane = function (pane) { if (panes.length === 0) { $scope.select(pane); } panes.push(pane); }; }, templateUrl: 'app/templates/smNavTabs' }; }; function navTabView($rootScope, $state, $stateParams) { return { require: '^smNavTabs', restrict: 'E', transclude: false, replace: true, scope: { name: '@', title: '@', viewSettings: '@?', onActivating: '&?', isVisible: '=?' }, link: function (scope, element, attrs, tabsCtrl) { //default settings and functions var defaultViewSettings = function () { var params, options; params = {}; options = { location: true, inherit: true, relative: $state.$current, notify: true, reload: false }; return { params: params, options: options }; }; var defaultOnActivating = function () { }; scope.isVisible = scope.isVisible || true; scope.viewSettings = scope.viewSettings || defaultViewSettings(); scope.onActivating = scope.onActivating || defaultOnActivating(); tabsCtrl.addPane(scope); }, }; }; })()with the template
<div class="tabbable"> <ul class="nav nav-pills nav-stacked"> <li ng-repeat="pane in panes" ng-class="{active:pane.isActive}" ng-show="pane.isVisible"> <a href="" ng-click="select(pane)" >{{pane.title}}</a> </li> </ul> <div class="tab-content" ng-transclude></div> </div>There are, unfortunately, two problems which I don't know how to solve.
ng-class="{true: 'invalid-tab', false: ''}[form.UserDefinedFields.$invalid]">More importantly, the invalid tab is not propagating to the whole form and I don't know how to make it work. Say, the first page will show several required columns in red, the Add (Save) button will be disabled. Switching to different tab activates that Add / Save button as the invalid state is not carried over.