I am trying to determine what is the best approach to adopt when a user is already logged in and type /login in the browser URL.
Should I prevent the event to be fired by using $routeChangeStart or $locationChangeStart (which one to chose)
Should I instead redirect to /home ?
Where should I implement the event capture ? In the controller or the app.js
I think you have somewhere a service that tells you if the user is logged in or not. My approach is to use a "resolve" clause/guard in the route/state you want to protect. In this case (login page) you could say something like: are you already logged in? Redirect to /home. Are you not logged in? I leave you load the page and log in. The choice to redirect to /home or elsewhere it depends on your application. You could even redirect to the /my-profile page if you have it.
Related
So I have a login scheme that only needs user login behind specific pages. Currently, I have the page checking if the user exists via the $onInit function, then firing the login (which redirects to a login page via Code Flow in OAuth 2). Then when I've logged in, the page redirects back to the previous page, instead of the page I fired the login even from.
I think this is due to ui-router not registering history with the browser before it redirects, thus the $state object in the OAuth 2 parameters, when decoded shows me the path: "redirectUrl":"http://localhost:3000/reservations" when it should be "redirectUrl":"http://localhost:3000/reservations/purchase".
So my question is two fold.
Is $onInit an appropriate place to be doing this, or should I have some global routechange listener or use resolve? If I use either of those two methods, I think I will still have the route problem.
If I keep using $onInit, is there some way to make sure the url is registered correctly so that it will redirect to the correct location post-login?
$onInit function:
function init() {
var token = Meteor.call('getAccessToken');
if (!token) {
Meteor.loginWithOidc();
}
}
When developing a web application with angular js, a part of time that developers spend is the time for implementing routing.
When using ui-router in a application, there are two "phases" to consider with regards to routing:
user navigates inside application: when click is made on some button, user is transfered to another state by using $state.go("somestate"). Parameters can be send etc. And url is changed accordingly.
user navigates directly via url.
Lets say application has such route:
/mythings/{thingid}/mysubthings/{mysubthingid}
If user navigates to that url directly by pasting it into browser window, application needs to handle it. My question is what's the best practice to do it?
What I'm thinging is: if looking at url example above what needs to be done when user enters that url in browser:
get {thingid} from url (from $stateParams), then get {mysubthingid} also from $stateParams (probably by using resolve (ui-router feature) when defining state), then inject what was resolved to controller and then make a query to api and get data about "subthing" and present view in ui with that data. So that should work with both "types of navigations": when user clicks and is transfered to state, or when user enter url directly into browser. Is this the right path to go?
And I suppose that any url you go to when you click something in application, you should be able to take that url and just paste it into browser and see the same results without being redirected to anywhere else. If application cannot handle every url in such way, maybe application' architecture needs to be reconsidered?
Thanks
In UI-Router, the main component of the routing is the state. The URL is essentially just an address that points to a specific state of the app. I don't think it's necessarily productive to think of the two ways of navigating as separate; they're just two sides of the same coin. There should be no URL that isn't handled by a state. Any URL that doesn't match a state should be caught by the otherwise definition on the $stateProvider and probably redirect to your home page or a 404 page.
In your example, the thing/:thingId/subthing/:subthingId url should map to a predefined state just like any other state. Let's say that state is main.subthing. The process for loading the data, initiating the controller and rendering the UI should be exactly the same whether you get there by calling $state.go('main.subthing', {thing: 123, subthing: 456}) or ui-sref='main.subthing({thing: 123, subthing: 456})' or you paste myapp.com/thing/123/subthing/456 into the browser. They'll all end up at exactly the same place with exactly the same data by calling the exact same logic (most likely loading thing 123 and subthing 456 in resolves and injecting those into a controller).
You're right that if a url can't be handled by the application, that's a sign that something is wrong. Like I said, bad urls should be handled by defining otherwise when setting up states. But pasting a URL into a browser shouldn't require any extra work if your states are defined correctly. URL handling is baked into UI-Router by default.
I partially agree with you. The point where I disagree is when the URL is obtained by a user who is not supposed to access it. Quite often some websites implement authorization code to check if the user who is currently accessing the page is authorized to do so.
Also I think the route could be a bit different. Something like:
{thingid}/{mysubthingid}
This, I think is a cleaner URL and the parameters could be handled in the same way as the one in your example.
I suggest this in order to make it a bit difficult to unauthorized users.
Anyway, it definitely depends on the kind of application you are implementing. If the app's requirement is to be able to access the page by pasting the URL in the browser then I guess your approach is much better.
My use case is very simple and i'm sure many apps share the same one. I need to verify user and need a simple url to retrieve user token from URL, make api call to my backend service and route the user according to the result from the API.
I don't need to render any page but just make API call and parse the response. I see two options to accomplish: Resolve or OnEnter. I also think there might be another simple way.
Here are the steps:
user getting email with link to /verify?token={token}
in app.js I have a state 'verify' and URL '/verify=token'
call API and get "true" or "false"
if false, redirect user to homepage, if true redirect the user the user profile page.
What is the best and simple way to accomplish what I like to do?
Thanks.
btw - I'm using ui-router.
In your case, I think you should use Resolve of ui-router to make sure state won't load until all data were resolved.
To make user feel good, you should show a loading indicator and register event $stateChangeSuccess to hide it once done
Well, I come from an ngRoute background, and I just started to learn uiRouter.
I used to write two different angular app one for login (login.html), and one for the main app (index.html). In he back-end, I just check if the user is authenticated then redirect them to index.html, else to login.html.
Now I want to encapsulate login into the main app. I have a lot of states requiring user to be logged in. In a lot of them some parts like header and footer are fixed. I'm looking for a concrete solution to handle it with these considerations:
I don't want to use resolve property for all of the states which need authentication.
I don't want my routes to be like /some-prefix-for-authed-paths/... for states that require authentication.
If somebody navigates to a route which requires authentication, I want to redirect them to login page and after login I want to redirect them back into where ever they were.
for 4xx errors, I want to display a custom error page(s) (probably route-dependent). For example if someone navigates to /users/3, and there is no user 3, (which will be determined by the resolve property of the state which will be rejected in case of not-existing user) I want to show them error page, but I DON'T want to change route to something like /error or something, because it actually doesn't make sense at all! Plus, I don't want a state push in the browser history.
You could try to implement an httpInterceptor that catches all non 2xx requests and redirect to some sort of error/login page.
Also to check for authentication you could use uiRouter onStateChange event by adding an access level to your states
.state('someState', {
url: '/someUrl',
isPrivate: true
})
$rootScope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams){
event.preventDefault();
// Check if state is private
if(toState.isPrivate) {
// Do something
}
});
Note! This is just somewhat pseudo code
I am writing a small angular js app with different views. Some views are available to all users but few views are available to only logged in user. I have a small login form on top of my main page [note that there is no separate login screen].
Everything was working fine till here,till then I noticed that even though all the views are not available on my screen initially and will get loaded only once the user logs in. But if the user knows the url of the restricted views , he can bypass the login process.
I have found this stackoverflow question as something very similar to my problem Redirecting to a certain route based on condition. Please see the accepted answer.
But the issue here is that I don't have a separate login screen , so I can't use this code snippet given in the accepted answer
if ( next.templateUrl == "partials/login.html" ) {
// already going to #login, no redirect needed
} else {
// not going to #login, we should redirect now
$location.path( "/login" );
}
How can I achieve the same solution with an integrated login form since I don't have a separate url for my login screen?
I am using ui-router, and that provides a resolve attribute for every route. Whenever a route is accessed, the resolve part is executed first. It returns a promise, and the page does not get loaded until it is resolved. Here you can write some server side calls to validate user, and if invalid, just redirect to any valid page. You can write a resolve to every view and validate the user at every step. I used node/express sessions for storing the logged-in user data.