I am new to AngularJS, and I am a little confused of how I can use angular-"ui-router" in the following scenario:
I am building a web application which consists of two sections. The first section is the homepage with its login and signup views, and the second section is the dashboard (after successful login).
I have created an index.html
for the home section with its angular app and ui-router
config to handle /login
and /signup
views,
and there is another file dashboard.html
for the dashboard section with its app and ui-router
config to handle many sub views.
Now I finished the dashboard section and don't know how to combine the two sections with their different angular apps. How could I tell the home app to redirect to the dashboard app?
The easiest solution is to use
$stateChangeStart
andevent.preventDefault()
to cancel the state change when the user is not authenticated and redirect him to the auth state that is the login page.I'm in the process of making a nicer demo as well as cleaning up some of these services into a usable module, but here's what I've come up with. This is a complex process to work around some caveats, so hang in there. You'll need to break this down into several pieces.
Take a look at this plunk.
First, you need a service to store the user's identity. I call this
principal
. It can be checked to see if the user is logged in, and upon request, it can resolve an object that represents the essential information about the user's identity. This can be whatever you need, but the essentials would be a display name, a username, possibly an email, and the roles a user belongs to (if this applies to your app). Principal also has methods to do role checks.Second, you need a service that checks the state the user wants to go to, makes sure they're logged in (if necessary; not necessary for signin, password reset, etc.), and then does a role check (if your app needs this). If they are not authenticated, send them to the sign-in page. If they are authenticated, but fail a role check, send them to an access denied page. I call this service
authorization
.Now all you need to do is listen in on
ui-router
's$stateChangeStart
. This gives you a chance to examine the current state, the state they want to go to, and insert your authorization check. If it fails, you can cancel the route transition, or change to a different route.The tricky part about tracking a user's identity is looking it up if you've already authenticated (say, you're visiting the page after a previous session, and saved an auth token in a cookie, or maybe you hard refreshed a page, or dropped onto a URL from a link). Because of the way
ui-router
works, you need to do your identity resolve once, before your auth checks. You can do this using theresolve
option in your state config. I have one parent state for the site that all states inherit from, which forces the principal to be resolved before anything else happens.There's another problem here...
resolve
only gets called once. Once your promise for identity lookup completes, it won't run the resolve delegate again. So we have to do your auth checks in two places: once pursuant to your identity promise resolving inresolve
, which covers the first time your app loads, and once in$stateChangeStart
if the resolution has been done, which covers any time you navigate around states.OK, so what have we done so far?
Where do we go from here? Well, you can organize your states into regions that require sign in. You can require authenticated/authorized users by adding
data
withroles
to these states (or a parent of them, if you want to use inheritance). Here, we restrict a resource to Admins:Now you can control state-by-state what users can access a route. Any other concerns? Maybe varying only part of a view based on whether or not they are logged in? No problem. Use the
principal.isAuthenticated()
or evenprincipal.isInRole()
with any of the numerous ways you can conditionally display a template or an element.First, inject
principal
into a controller or whatever, and stick it to the scope so you can use it easily in your view:Show or hide an element:
Etc., so on, so forth. Anyways, in your example app, you would have a state for home page that would let unauthenticated users drop by. They could have links to the sign-in or sign-up states, or have those forms built into that page. Whatever suits you.
The dashboard pages could all inherit from a state that requires the users to be logged in and, say, be a
User
role member. All the authorization stuff we've discussed would flow from there.Here is how we got out of the infinite routing loop and still used
$state.go
instead of$location.path
Use $http Interceptor
By using an $http interceptor you can send headers to Back-end or the other way around and do your checks that way.
Great article on $http interceptors
Example:
Put this in your .config or .run function.