Backbone js prevent url hash change when back/forward - javascript

I am working on a very basic SPA using Backbone.js. My app has few routes. Among them there are 2 that give me issues: the index route ("/#index") and menu route ("/#mainmenu").
A simple workflow in my app is as follows: the user fills a form -> clicks to login -> trigger ajax request -> if login successful go to "/#mainmenu" route. if login failed, remain on "/#index" route.
On "/#mainmenu" if the user clicks on logout -> ajax request -> if logout success go to "/#index". if logout failed remain on "/#mainmenu".
The issues that I am struggling with are:
A clean way to trigger transition to "/#mainmenu" after successful login (I currently use router.navigate("mainmenu", {trigger: true}); but read that should avoid using this approach, in derrick bailey's article https://lostechies.com/derickbailey/2011/08/28/dont-execute-a-backbone-js-route-handler-from-your-code/ )
A clean way to prevent the user to go back to the "/#index" when pressing Back button in the browser from "/#mainmenu" route. I will also would like to preserve the url hash to reflect the current view.
Prevent the user to go forward to "/#mainmenu" after successful logout.
Is that even possible to prevent url hash change when clicking browsers back/forward buttons?
When I say "clean" I refer to "what are the best practices?". I partially solved some issues by saving url hashes and restore the appropriate hash (by router.navigate(currentRoute, {replace: true}); ) but I feel that it's a hacky approach.
Any feedback is welcome and much appreciated.

One way to solve this problem is by applying an async before filter on the routes that require an auth status check before the actual callback route is executed.
For example:
https://github.com/fantactuka/backbone-route-filter
The philosophy of avoiding {trigger: true} is based on the fact that when the router gets triggered with this flag, the entire initialization procedure for that route gets triggered. You will lose the benefit of having previously defined appstates because the app will have to re-initialize all content while this work had alrady been done before.
In practice, I think that it is useful to assess what your web app actually does. If losing appstate isn't an issue because the views you want to render are entirely new, then I don't see a problem with creating a client side redirect that re-inintializes your app.
If, on the other hand, your app has many views already rendered for which you want to maintain the same state as before, you can listen for an auth state event on each component that requires it, and make only those views re-render accordingly if they need to.

I don't think there's anything wrong with triggering routes, have been doing this without any issue for 2+ years. It all boils down to your requirements, read the article looks like a lot of work to me.
There are multiple ways to do this. First, you can disable back/forward buttons using window.history.forward(). Second, my favourite, is to do the processing in Router#execute. A sample might look like :
execute: function(callback, args, name) {
if (!loggedIn) {
goToLogin();
return false; //the privileged route won't trigger
}
if (callback) callback.apply(this, args);
}

Related

Rerun nuxt route middleware if store getter changes without reloading the page?

Given following middleware, whats the best way to rerun the logic when ever store.getters.authenticated changes, and not only on the initial load.
middleware/auth.js
export default function ({ store, redirect }) {
if (!store.getters.authenticated) {
return redirect({ name: "login" })
}
}
You asked how to rerun it inside of the middleware itself, which is the 2nd part of a possible middleware trigger (like if the user is not authenticated anymore while staying on the same page) and without any specific action, like when using polling or websockets I thought.
Meanwhile, the 1st part is the easiest: call the middleware globally (in nuxt.config.js) to trigger it on each page navigation.
If he stays on the same page, you can also move with the router as you did but at this point, checking if there is an error in your axios is maybe more appropriate since it's the initiator.
I also do like to use this.$nuxt.refresh() to trigger all the checks when switching accounts globally, helps re-running all those tasty fetch() hooks.

angular ui-router - how to detect direct navigation to a state

I'm using $transitions to detect state changes and take some action based on the difference between some data defined in the 'from' and 'to' states. This is working great for me, but this code is not hit when the user navigates directly to a state via url. For example, browsing to mysite/search does not trigger this, but navigating within the site from the home page to the search page triggers this.
Is there any way to detect that the state was directly navigated to? That would allow me to execute the logic that I need to.
$transitions.onSuccess({}, trans => {
myFunc(trans.$from().data, trans.$to().data);
return true;
});
My problem was that I didn't register the transaction early enough; it was in a service that wasn't loaded until after transitions were handled for the initial launch of the app.

ReactJS - how to setup login pattern?

I'm sorry if this question is a bit vague, but I'm tackling this problem for the first time and any pointer would be useful.
I am building a web app using ReactJS and I need a login system - first page with two fields username / password and submit button. The server returns a token (1234) and this needs to be used in an auth header (Authorization: Bearer 1234) in order to access the protected area.
How should I handle the login and make the browser update itself with the new content available after login?
As the others have pointed out, it is a good idea to use React-Router.
I think you can use pattern like this: You get user inputs and send them via AJAX (with JQuery, Superagent, whatever you want). If the input is valid and user authenticated, the server sends back token with some user info, which can include his roles or permissions. Based on these received data, you can use React-Router to render other component, e.g. welcome page (by calling replaceState on React-Router history object - in flux action for example).
Additionally, you should save this token in a cookie or into a session/local storage (in order to be able to use it on every subsequent request), and the user info could be stored in a Flux store. After saving this user the store emits change event, which should lead to rerender of your root component with the user information you got.
Then, based on the new user roles or permissions in your store, you can have for example ES7 decorator on some of your components deciding, if it displays the actual component or not.
Hope it helps you a bit.

How to return to Iron Router route after login when login is clicked and a different route?

In my app a user can navigate around anonymously without logging in. There is a dropdown to log in from any anonymous-friendly route. The dropdown has a link to a separate loginPage route and thus changes the route when clicked using Render.go('loginPage'). I'm trying to figure out how to return the user to the previous page they were on after logging in.
I understand the typical onBeforeAction(requireLogin)... method and that works if a page requires a login to access it. It re-evaluates Meteor.user() and then calls this.next().
My issue is when a user clicks to log in and they're taken to the loginPage route, how to then get them back to the previous page. I was trying to call something like Router.render('loginPage') in the click event but you can't call Router.render() on the client like how you can inside a route. Apparently only Router.go() works on the client, thus changing the route.
The only way I can think of is to have every anonymous-friendly route in my app have an onBeforeAction that tests for a Session variable (e.g. loginRequested). If the user clicks on "Sign In", it sets loginRequested which would invalidate the route. Once they sign in I would clear loginRequested and then the onBeforeAction would call this.next() taking them back to the previous page. This would work unless the user hits "Back" in their browser. I would have to listen for that and clear loginRequested so the route would validate. Is there a way to do that in Iron Router?
This just seems really hacky. Is there a better way to do this?
EDIT: I forgot to mention that my loginPage route actually has its own action: if the user goes directly to the login page through the URL it will forward them to a specific route.
Just call history.back(); after a successful log in.
E.g:
Router.route('/login', {
onBeforeAction: function(){
// Do logging stuff
history.back();
}
});
Iron Router is kind of hacky itself, so if you decide to handle logging in the route, you might have to call this.next() somewhere.
So it turns out the best way to solve my above problem is indeed through the use of Session variables. Something like:
if(!Meteor.user()) {
if(Session.get('wantsToLogIn`) == null)
this.next();
else
this.render('loginPage');
}
else
this.next();
So if a user is on some page and decides to log in, you just set a Session variable wantsToLogin. If you set it, it invalidates the onBeforeAction and evaluates to call this.render('loginPage'), once the user logs in the onBeforeAction again invalidates and listens for Meteor.user(), and as soon as the user is logged in the onBeforeAction calls this.next(). Basically everything I said in my question... I don't know why I didn't just do that to begin with.

Ember.js How can the URL represent every possible router state?

On the documentation for routing at emberjs.com, it states
Is the user currently logged in? Are they an admin user? What post are they looking at? Is the settings screen open? Are they editing the current post?
In Ember.js, each of the possible states in your application is represented by a URL.
I can't seem to understand: How is it supposed to happen to have every possible of the above states is represented by a URL?
Explanation:
As far as i can see, the Ember Router is a strictly hierarchical tree structure. That makes perfect sense to me for straightforward URLs, e.g.
user/:user_id/posts/:post_id/comments
which is the locator for the comments of a single post.
Now, where would the login state of the user go? Wouldn't that create multiple URLS for the same resource, or does that not matter?
If your settings screen is a modal that can be accessed from anywhere on the site, how would that be reflected in the url?
I'm not asking for a workaround solution, but just wanted to get some opinions of how this is actually meant in the guides or what the best practises are.
Great question Conrad.
I'll offer some experience from the multiple projects I've done in Ember.
Rarely do I add the user to the route, there's a good chance you aren't going to be sending down anything but the current logged in user (unless of course you're building an admin resource). So defining the user id in the url is probably incorrect, since it doesn't define the current page, it defines the user who was viewing that current page.
The login state would be unrelated to the other resource routes. It would live at the root, and after login you would redirect to an authorized route. In the event that someone navigates directly to an authorized route, there are some good patterns for pausing that transition, navigating to the login route, then upon a valid login redirecting to the authorized route.
Generally you have multiple routes for a single resource.
App.Router.map(function() {
this.resource('login');
this.resource('post', { path: '/post/:post_id' }, function() {
this.route('edit');
this.route('someViewWithTheSameResource');
this.route('someViewWithTheSameResource2');
this.resource('comments', function() {
this.route('new');
this.route('update');
});
});
});
The grand-daddy issue is modals. Originally that was just a don't handle it in the url, there is no nice way to handle this without having to add a million routes under different settings. With the upcoming change of query params you can modify settings at a root level without having to muck up the entire resource tree. This is still in beta, so it may not quite work as expected, but the goal is to be able to handle such a use case.
http://emberjs.com/guides/routing/query-params/

Categories