1. #1
    Sencha User
    Join Date
    Mar 2007
    Posts
    426
    Vote Rating
    17
    jweber will become famous soon enough

      2  

    Default Using router with optional, named URL arguments

    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:

    Code:
    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:

    Code:
    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:

    Code:
    {
    	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.
    Last edited by jweber; 30 Apr 2012 at 10:47 AM. Reason: change argument order

  2. #2
    Sencha - Senior Software Engineer mitchellsimoens's Avatar
    Join Date
    Mar 2007
    Location
    Gainesville, FL
    Posts
    38,215
    Vote Rating
    1044
    mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute

      0  

    Default

    So it's basically like a data payload type of thing. How does this look in the route handler in the controller?
    Mitchell Simoens @SenchaMitch
    Sencha Inc, Senior Software Engineer
    ________________
    Check out my GitHub, lots of nice things for Ext JS 4 and Sencha Touch 2
    https://github.com/mitchellsimoens

    Think my support is good? Get more personalized support via a support subscription. https://www.sencha.com/store/

    Need more help with your app? Hire Sencha Services services@sencha.com

    Want to learn Sencha Touch 2? Check out Sencha Touch in Action that is in print!

    When posting code, please use BBCode's CODE tags.

  3. #3
    Sencha User
    Join Date
    Mar 2007
    Posts
    426
    Vote Rating
    17
    jweber will become famous soon enough

      0  

    Default

    With the sample URL above, if your route was 'users/:username', your controller function might look like:
    Code:
    routeUser: function(query, username) {
       console.log(query.foo, query.bar, username);
       # 'one', 'two', 'johndoe'
    }

  4. #4
    Sencha User
    Join Date
    Sep 2012
    Location
    Ljubljana
    Posts
    23
    Vote Rating
    0
    TipyTop is on a distinguished road

      0  

    Default

    Thank you so much jweber.

  5. #5
    Sencha User
    Join Date
    Jul 2012
    Posts
    9
    Vote Rating
    1
    ryeo is on a distinguished road

      0  

    Default

    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?

    Code:
    routes: {
                'tab/:tabId': 'gotoTab'}
    
    
    gotoTab: function(query, tabId) {}

  6. #6
    Sencha User
    Join Date
    Mar 2007
    Posts
    426
    Vote Rating
    17
    jweber will become famous soon enough

      0  

    Default

    Sorry, I haven't really used Architect enough to help there.

  7. #7
    Sencha Premium Member
    Join Date
    May 2012
    Posts
    3
    Vote Rating
    0
    fangunxs is on a distinguished road

      0  

    Default

    Thank you very much! It works very well!