AngularJS UI-Router + State help needed

2019-09-13 19:38发布

问题:

I have html code (code snippet 1) & JS code (code snippet 2) as below. In html I am including the partials and building a page that contains a ui-view which dynamically changes the html when the state changes. This is working fine.The page contains a header , a footer , a left side menu and the dynamically changing content based on the state. Now I need to include a Login page for this application. However we don't need header , footer, menu for the login screen. Currently the login screen appears in the single ui-view defined along with header,footer and menu.

I was thinking if I could define two divs one for login alone and show that div if the state is login. If the state is not login then show the div shown in the html code below (snippet 1). Please advise if this is a feasible idea. If so please advise how to access the state in html. I tried to access the state using code (snippet 3) below but could not please advise.

html (code snippet 1) :

<code>
    <div class="grandContainer">    
    <div ng-include="'${header}'"></div>
    <div class="abc">
            <div ng-include="'${status}'"></div>
            <div ng-include="'${menu}'"></div>
    </div>      
    <div class="mainContainer">
    <div ui-view></div>
    </div>

    <p ng-include="'${footer}'"></p> 
</div>
</code>  

JS (code snippet 2):

app.config(['$stateProvider','$urlRouterProvider',function($stateProvider,   $urlRouterProvider) {  
    $urlRouterProvider.otherwise('/login');
    $stateProvider.state('grid', {
    url: '/grid',
    templateUrl: 'resources/html/grid.html',
    controller: 'gridCtrl'
    }).state('chDetail', {
     url: "/chDetail/:chId",
     templateUrl: "resources/html/chDetail.html",
     controller: 'chDetailCtrl'
     }).state('login', { 
     url: "/login",
     templateUrl: "resources/html/login.html",
     controller: 'loginCtrl'
     });
     }]).controller('gridCtrl', ['$scope', function($scope){
     $scope.grid =[];
     }]);

</code>

code snippet 3:-

<code>

<div ng-show="{$state.is('login')}">Item</div>
</code>

回答1:

If I've understood it correctly you want to display the login view with-out the other views like header, footer & side menu.

Then you could create an abstract state for the layout with your views header, footer, side-menu etc. and add a separate state for your login view.

The tricky part of the demo is the views definition in the abstract state:

abstract: true,
views: {
    '': {
        templateUrl: 'layout.html',
    },
    'header@root': {
        template: 'content for header'
    },
    'footer@root': {...}
}

What is the abstract state doing? The abstract state will load the layout template in the unnamed view in your main template and loads the other views that you'd like to share with the child states.

Each child state can then define the content view as needed and re-use the other views.

Please have a look at the demo below or this fiddle.

Note: The login in the demo is of course not secure and only there to show the view redirect handling. In your app you have to work with $http and with sessionStorage and tokens.

angular.module('demoApp', ['ui.router'])
	.run(function($rootScope, $state) {
            $rootScope.$on('$stateChangeError', function(evt, to, toParams, from, fromParams, error) {
          if (error.redirectTo) {
            $state.go(error.redirectTo);
          } else {
            $state.go('error', {status: error.status})
          }
        })
    })
	.factory('userService', userService)
	.config(routes);

function userService() {
	var usersMock = {
    	'testUser': {
        	username: 'testUser',
            password: '1234'
        },
        'testUser2': {
        	username: 'testUser2',
            password: '1234'
        }
    };
	var userService = {
    	user: undefined,
    	login: function(userCredentials) {
        	// later --> $http.post('auth', userCredentials).then(...)
            // for demo use local data
            var user = usersMock[userCredentials.username]
            userService.user = ( user && ( user.password == userCredentials.password ) ) ? 
            	user : undefined;
            return user;
        },
        logout: function() {
        	userService.user = undefined;
        }
    }
    
    return userService;
}
function routes($urlRouterProvider, $stateProvider) {

	$urlRouterProvider.otherwise('/');
    
    $stateProvider
    	.state('root', {
        	url: '',
        	abstract:true,
            resolve: {
            	'user': function(userService) {
                	return userService.user; // would be async in a real app
                }
            },
            views: {
            	'': {
                	templateUrl: 'layout.html',
                },
            	'header@root': {
                	template: '<h1>header View<span ng-if="user"><button ng-click="logout()">logout</button></span><span ng-if="!user"><button ng-click="login()">login</button></span></h1>',
                    controller: function($scope, $state, user, userService) {
                    	$scope.user = user;
                        $scope.login = function() {
                        	$state.go('login');
                        };
                        $scope.logout = function() {
                        	userService.logout();
                            $state.go('root.home', {}, {reload: true});
                        };
                    }
            	},
                'footer@root': {
                    template: '<p>footer view</p>'
                }
            }
        })
    	.state('root.home', {
            url: '/',
            views: {
                'content': {
                    template: 'Hello at home'
                }
            }
        })
        .state('root.about', {
            url: '/about',
            views: {
                'content': {
                    template: 'about view'
                }
            }
    	})
        .state('root.restricted', {
            url: '/restricted',
            resolve: {
            	auth: function(userService, $q, $timeout) {
                
                	var deferred = $q.defer();
                	/* //with an async
                    return UserService.load().then(function(user){
                      if (permissionService.can(user, {goTo: state})) {
                        return deferred.resolve({});
                      } else {
                        return deferred.reject({redirectTo: 'some_other_state'});
                      }
                    }*/
                    
                   $timeout(function() {
                        if ( angular.isUndefined(userService.user) ) {
                            return deferred.reject({redirectTo: 'login'});
                        }
                        else {
                            return deferred.resolve(userService.user);
                        }
                    });
                    
                    return deferred.promise;
                }
            },
            views: {
                'content': {
                    template: 'this is only visible after login. Hello {{user}}!',
                    controller: function($scope, auth) {
                    	$scope.user = auth.username;
                    }
                }
            }
    	})
        .state('login', {
            url: '/login',
            templateUrl: 'views/login.html',
            controller: function($scope, $state, userService) {
            	$scope.login = function(cred) {
                	var user = userService.login(cred);
                    
                    if (angular.isUndefined(user)) {
                    	alert('username or password incorrect.')
                    }
                    else {
                    	$state.go('root.restricted');
                    }
                };
            }
        });
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.18/angular-ui-router.js"></script>

<div ng-app="demoApp">
    <a ui-sref="root.home" href="#">home</a>
    <a ui-sref="root.about" href="#">about</a>
    <a ui-sref="root.restricted" href="#">restricted page</a>
    
    <div ui-view>
    </div>
    
    <script type="text/ng-template" id="views/login.html">
    	<h1>Login</h1>
        <p>Try testUser and 1234 as credentials</p>
        <label>Username</label><input ng-model="userCred.username"/>
        <label>Password</label><input type="password" ng-model="userCred.password"/>
        <button ng-click="login(userCred)">login</button>
    </script>
    
    <script type="text/ng-template" id="layout.html">
    	<div>
            <div ui-view="header"></div>
            <div ui-view="content"></div>
            <div ui-view="footer"></div>
    	</div>
    </script>
</div>



回答2:

app.config(['$stateProvider','$urlRouterProvider',
  function($stateProvider,   $urlRouterProvider) {  
    $urlRouterProvider.otherwise('/login');
    $stateProvider.state('grid', {
      url: '/grid',
      templateUrl: 'resources/html/grid.html',
      controller: 'gridCtrl'
    })
    .state('chDetail', {
      url: "/chDetail/:chId",
      templateUrl: "resources/html/chDetail.html",
      controller: 'chDetailCtrl'
    })
    .state('login', { 
      url: "/login",
      controller: 'loginCtrl',
      views: {
        '': {
          templateUrl: 'resources/html/login.html'
        },
        'login-only@login': {
          templateUrl: 'resources/html/other-html.html'
        }
      }
    });
  }
])
.controller('gridCtrl', ['$scope', function($scope){
  $scope.grid =[];
}]);

I've altered it slightly. To get what you want, use views.

Then your extra bit of code would just be another ui-view, just a named one:

<div ui-view="login-only"></div>

It would only appear on the 'login' state, since you specifically target it via it's name (login-only@login).

You can read more about it at the Multiple Named Views on the ui-router wiki help pages at github.



回答3:

very basically you can use $state for get current state like:

module.exports = function Controller($state, $scope)

$scope.currentState = $state.current;

then in your html

<p ng-if="currentState !='login'" ng-include="'${footer}'"></p>

but nested ui-router logic could be more useful.