PDA

View Full Version : Using router with optional, named URL arguments



jweber
30 Apr 2012, 10:23 AM
I thought I would share this, in case anyone else needs it. I wanted to use Sencha's routing, but with URLs like #users/johndoe?foo=one&bar=two, so that certain arguments could be optional. This "query string" is separate from the actual query string, since it follows the hash mark. But it's in the same format, so it's easy to read.

Here's what I did:


Ext.define('Ext.app.QueryRouter', {
extend: 'Ext.app.Router',
requires: ['Ext.app.QueryRoute'],

connect: function(url, params) {
params = Ext.apply({url: url}, params || {}, this.getDefaults());
var route = Ext.create('Ext.app.QueryRoute', params);

this.getRoutes().push(route);

return route;
}
});

Ext.define('Ext.app.QueryRoute', {
extend: 'Ext.app.Route',

initialize: function() {
this.callParent(arguments);
this.parseQueryRegex = new RegExp("([^?=&]+)(?:=([^&]*))?", "g");
},

recognize: function(url) {
var action = this.callParent(arguments);
if (action) {
var queryStr = action.args.pop(),
query = {},
match;
if (queryStr[0] === "?") {
queryStr = queryStr.slice(1);
while ((match = this.parseQueryRegex.exec(queryStr)) !== null) {
query[decodeURIComponent(match[1])] = (match[2] === undefined ? undefined : decodeURIComponent(match[2]));
}
}
action.args.unshift(query);
}
return action;
},

createMatcherRegex: function(url) {
var paramsInMatchString = this.paramsInMatchString,
length = paramsInMatchString.length,
i, cond, matcher;

for (i = 0; i < length; i++) {
cond = this.getConditions()[paramsInMatchString[i]];
matcher = Ext.util.Format.format("({0})", cond || "[%a-zA-Z0-9\-\\_\\s,]+");

url = url.replace(new RegExp(paramsInMatchString[i]), matcher);
}

return new RegExp("^" + url + "((?:\\?.*?)?)$");
}
});


Then in my app.js:


Ext.application({
router: {
xclass: 'Ext.app.QueryRouter'
}


Now in my controller classes, I can continue to use Sencha's routing as usual. But the new first argument passed to the action functions will be an object, mapping argument names to their values. For example:


{
foo: "one",
bar: "two"
}

The rest of the arguments will be the same as before. You can leave off the "query string" entirely, and you'll be passed an empty object as the first argument.

mitchellsimoens
2 May 2012, 5:37 AM
So it's basically like a data payload type of thing. How does this look in the route handler in the controller?

jweber
2 May 2012, 6:51 AM
With the sample URL above, if your route was 'users/:username', your controller function might look like:

routeUser: function(query, username) {
console.log(query.foo, query.bar, username);
# 'one', 'two', 'johndoe'
}

TipyTop
16 Sep 2012, 9:49 AM
Thank you so much jweber.

ryeo
8 Dec 2012, 9:38 PM
Thanks jweber!

I almost have this working in Architect.
In Architect how do I change up the controller route action so it can take up the query object?


routes: {
'tab/:tabId': 'gotoTab'}


gotoTab: function(query, tabId) {}

jweber
9 Dec 2012, 11:17 AM
Sorry, I haven't really used Architect enough to help there.

fangunxs
31 Dec 2012, 6:03 PM
Thank you very much! It works very well!

Sergey Chizhov
14 Apr 2016, 7:01 AM
Ext.define('Mobile.overrides.app.Route', {
override: 'Ext.app.Route',


/**
* @inheritdoc
*/
recognize: function(url) {
if (!this.getInitialized()) {
this.initialize();
}


if (this.recognizes(url)) {
return {
controller: this.getController(),
action : this.getAction(),
url : url,
args : [this.matchesFor(url)]
};
}
}
});


In controller i receive param "args" with object