PDA

View Full Version : Changing hash before launch causes action to be called twice



Ballsacian1
6 Jun 2014, 11:41 AM
Example controller which attempts to change the hash before launch occurs and routing is called.



/**
* The main application controller. This is a good place to handle things like routes.
*/
Ext.define('app.controller.Root', {
extend: 'Ext.app.Controller',
requires: [
'Ext.util.History'
],


init: function() {
var history = Ext.util.History,
currentHash = history.getHash();
if (app.model.Member.isLoggedIn()) {

} else if (currentHash.indexOf('login') !== 0) {
// Both of these result in a double call because of the hashchange event.
// history.setHash('login/' + history.getHash());
this.getApplication().redirectTo('login/' + history.getHash());
}
}
});


Fix for Ext.util.History.handleStateChange


/**
* Fix for handleStateChange to chekcing if the currentToken is different than token before proceeding.
* Added a 'force' property as well so that handleStateChange could still continue if 'force' === true.
*/
Ext.define('app.util.History', {
override: 'Ext.util.History',
/**
* Handles when the hash in the URL has been updated. Will also fired the change event.
*
* @param {String} token The token that was changed to
* @private
*/
handleStateChange: function(token, force) {
if (this.currentToken !== token || force === true) {
this.callParent(arguments);
}
}
});

mitchellsimoens
11 Jun 2014, 4:40 AM
This is because in Ext.app.Application it triggers the Router to react to the current hash but you also are changing it so you are causing the reflow. Instead of how you are doing it, this is an example of how I would do user auth:


Ext.define('app.controller.Root', {
extend : 'Ext.app.Controller',

routes : {
'login/:id' : 'onLogin',
'blah' : {
action : 'onBlah',
before : 'checkUser'
}
},

checkUser : function() {
var args = Ext.Array.slice(arguments),
action = args.pop();

if (app.model.Member.isLoggedIn()) {
action.resume();
} else {
action.stop(true);

this.redirectTo('login/' + Ext.util.History.getHash());
}
},

onBlah : function() {
var body = Ext.getBody();

body.update(body.getHtml() + '<div>blah</div>');
},

onLogin : function(id) {
var body = Ext.getBody();

body.update(body.getHtml() + '<div>login - ' + id + '</div>');
}
});

Now if you go to http://example.com/#blah it will stop the route and any other routes in other controllers thanks to the action.stop(true) and then redirect to the login route. This will also work every time someone goes to the #blah hash and not just at application start as your way was insecure.

froskie
26 Apr 2015, 5:48 PM
Sorry to bring this question to life again, but your solution, although effective, is for one route only. Now imagine doing that 'app wise', I mean, for ANY routes that enters the app I want to validate if the user is logged in. I'd have to write the before to all of them?


This is because in Ext.app.Application it triggers the Router to react to the current hash but you also are changing it so you are causing the reflow. Instead of how you are doing it, this is an example of how I would do user auth:


Ext.define('app.controller.Root', {
extend : 'Ext.app.Controller',

routes : {
'login/:id' : 'onLogin',
'blah' : {
action : 'onBlah',
before : 'checkUser'
}
},

checkUser : function() {
var args = Ext.Array.slice(arguments),
action = args.pop();

if (app.model.Member.isLoggedIn()) {
action.resume();
} else {
action.stop(true);

this.redirectTo('login/' + Ext.util.History.getHash());
}
},

onBlah : function() {
var body = Ext.getBody();

body.update(body.getHtml() + '<div>blah</div>');
},

onLogin : function(id) {
var body = Ext.getBody();

body.update(body.getHtml() + '<div>login - ' + id + '</div>');
}
});

Now if you go to http://example.com/#blah it will stop the route and any other routes in other controllers thanks to the action.stop(true) and then redirect to the login route. This will also work every time someone goes to the #blah hash and not just at application start as your way was insecure.