I want a setup/teardown method to be called before and after a route is fired in my Backbone.js router, respectively. Has anyone created an elegant way of doing this?
_.wrap is not a solution, if You have for example 20 routes you have to wrap them all.
But you can do this with metaprogramming
class Backbone.FlexRouter extends Backbone.Router
route: (route, name, handler) ->
super route, name, ->
#trigger "route:before"
handler()
#trigger "route:after"
UPD: I believe in JS it should be something like this (but I didn't tested it)
var rp = Backbone.Router.prototype
rp.routeWithoutEvents = rp.route
rp.route = function(route, name, handler){
var that = this
this.routeWithoutEvents(route, name, function(){
that.trigger("route:before")
handler()
that.trigger("route:after")
})
}
Have you considered _.wrap?
Here is the simple one, overriding the Backbone.Router itself
(function () {
_.extend(Backbone.Router.prototype, Backbone.Events, {
route: function (route, name, callback) {
if (!_.isRegExp(route)) route = this._routeToRegExp(route);
if (!callback) callback = this[name];
Backbone.history.route(route, _.bind(function (fragment) {
var args = this._extractParameters(route, fragment);
if (this.before && _.isFunction(this.before)) {
this.before(fragment);
}
callback && callback.apply(this, args);
this.trigger.apply(this, ['route:' + name].concat(args));
if (this.after && _.isFunction(this.after)) {
this.after(fragment);
}
Backbone.history.trigger('route', this, name, args);
}, this));
return this;
}
});
}).call(this);
Focus on the lines
if (this.before && _.isFunction(this.before)) {
this.before(fragment);
}
AND
if (this.after && _.isFunction(this.after)) {
this.after(fragment);
}
You can modify the lines according to your needs
And here is the client code using the new Backbone.Router class
var appRouter = Backbone.Router.extend({
routes: {},
before: function(){
//your code here
return true;
}
});
Alexey's answer is almost right, but there are a few subtle things that are missing.
class ApplicationRouter extends Backbone.Router
route: (route, name, callback = null) ->
callback = #[name] if ! callback
super route, name, ->
#trigger 'route:before'
result = callback && callback.apply(#, arguments)
#trigger 'route:after'
return result
This plugin does what you want. It works with 0.5.3. I'm not certain if it works with 0.9.1 yet or not.
https://github.com/angelo0000/backbone_filters
I came across this problem earlier and I thought I would share my solution for inserting "middleware" into the Backbone routing flow. The goal was to reroute users to various flows depending on some condition, e.g., feature flags, session handling, etc..
Backbone.ProtectedRouter = Backbone.Router.extend({
/*
* Subclass of Router that monkeypatches route in order to protect certain
* routes.
*
* If you want to add a protected route, add it to the protectedRoutes
* object in this form:
* route: { method: fn, assertion: fn, args: [args..] }
*
* * method => the method to call if the assertion is true (the route should
* be protected in the given scenario)
*
* * assertion => the function that decides whether or not the route
* should be rendered
*
* * args => the arguments to be passed to method
*/
route: function(route, name, handler) {
var _this = this;
Backbone.Router.prototype.route(route, name, function(){
var boundHandler = _.bind(handler, _this),
attrs, method, args, dfd;
attrs = _.has(_this.protectedRoutes, route) ? _this.protectedRoutes[route] : null;
if ( attrs && !attrs.assertion() ) {
// In this scenario my flows all return Deferreds
// you can make this event based as well.
dfd = _this[attrs.method].apply(_this, attrs.args.concat([route]));
dfd.then(boundHandler);
} else
boundHandler.apply(_this, arguments);
});
}
});
From there you can simply extend the Backbone.ProtectedRouter with a protectedRoutes hash as so:
var router = Backbone.ProtectedRouter.extend({
protectedRoutes: {
'home': {
assertion: function() { return is_logged_in; },
method: 'renderLogin',
args: ['some_arg']
}
},
routes: {
'home': 'renderHome'
},
...
});
In this scenario, if a request is made for the home route and is_logged_in is false, the renderLogin method is invoked and passed 'some_arg'. After the flow, renderLogin would return a resolved Deferred that causes the original handler (renderHome) to be invoked.
I hope this helps. I'm very open to suggestions as well! :)
I came across this need recently (to check user is authenticated). Unfortunately Backbone doesn't give us a before/after event, so you will need to overwrite or extend the Router.route. Doesn't feel very clean since you have to copy from source code and edit there, but is the only way I found. Below Backbone default code (1.0.0) and marked my custom code:
Backbone.Router.prototype.route = function(route, name, callback) {
if (!_.isRegExp(route)) route = this._routeToRegExp(route);
if (_.isFunction(name)) {
callback = name;
name = '';
}
if (!callback) callback = this[name];
// here my custom code
callback = _.wrap(callback, _.bind(function(cb) {
if (name == 'login' || sessionModel.authenticated()) {
_.bind(cb, this)();
} else {
this.navigate('login', {trigger: true});
}
}, this));
// finish my custom code
var router = this;
Backbone.history.route(route, function(fragment) {
var args = router._extractParameters(route, fragment);
callback && callback.apply(router, args);
router.trigger.apply(router, ['route:' + name].concat(args));
router.trigger('route', name, args);
Backbone.history.trigger('route', router, name, args);
});
return this;
};
Notice _.wrap and _.bind so this is the one that you would expect when using the router. Otherwise I was getting a "this is undefined" error.
ethnagnawl and Alexey are both correct; _.wrap is the right solution but if you have a bunch of routes and write them in the normal backbone fashion it'll be a pain. I realized you can do this:
var Pages = {}
Pages.loginPage = function(){ ... }
Pages.mainPage = function(){ ... }
Instead of defining your route handlers directly in Router.extend, load them into an object and then do this:
_.map(Pages,function(func,name){
Pages[name] = _.wrap(func,function(funky){
// Save original arguments
var args = Array.prototype.slice.call(arguments,1);
// Do stuff before the route
funky(args);
// Do stuff after the route
});
});
This also makes it pretty easy to check for the function name if you need to treat a subset of them differently or something. Then, because it's just an object, you can do this:
var myRouter = Backbone.Router.extend({
routes: ... /* as usual */
}).extend(Pages);
And you're done.
One nice advantage of this is it involves no messing with the Backbone prototypes, so even if a version update changes something it won't bite you.
after doing lot more manipulations. i came to a solution that i have given below......
Here ur original root function ...
route: function(route, name, callback) {
if (!_.isRegExp(route)) route = this._routeToRegExp(route);
if (_.isFunction(name)) {
callback = name;
name = '';
}
if (!callback) callback = this[name];
var router = this;
Backbone.history.route(route, function(fragment) {
var args = router._extractParameters(route, fragment);
callback && callback.apply(router, args);
router.trigger.apply(router, ['route:' + name].concat(args));
router.trigger('route', name, args);
Backbone.history.trigger('route', router, name, args);
});
return this;
}
Now look at this code & change the "route" function to your original Backbone.js...
route: function(route, name, callback) {
if (!_.isRegExp(route)) route = this._routeToRegExp(route);
if (_.isFunction(name)) {
callback = name;
name = '';
}
if (!callback) callback = this[name];
var router = this;
Backbone.history.route(route, function(fragment) {
// takes matched route & fragment as like 'route1'
var args = router._extractParameters(route, fragment);
// extracts arguments if exists
// here yours self invoking function or other function starts....
(function(){
// do something
if ( true ) // condition satisfies then route to the given Route
{
callback && callback.apply(router, args);
}
else{
name='route2'; // change name of route
window.location.hash = 'route2';
callback= function(){
// optional callback if u want
}
callback && callback.apply(router, args); // route to ur custome Route
}
})();
});
return this;
}
----- Thank You --------
Love 2 write Dirty Codes !
#xy....
Here is a JavaScript version that works with what I've got;
var rp = Backbone.Router.prototype;
rp.routeWithoutEvents = rp.route;
rp.route = function(route, name, callback) {
if (!callback) callback = this[name];
this.routeWithoutEvents(route, name, function() {
this.before.apply(this);
callback.apply(this,arguments);
this.after.apply(this);
});
};
It's based on Alexey Petrushin's and Jonathan Tran's solutions.
I could not find an easy way to intercept the routing event before the route handler is called.
My solution is to extend the Router component, adding a registerBeforeRouting method and editing the route method (I took it from the Backbone 1.0 and it worked, YMMV with different Backbone versions).
Before the router is created:
var rp = Backbone.Router.prototype;
rp.registerBeforeRouting = function (callback) {
this._beforeRoutingCallback = callback;
};
rp.route = function (route, name, callback) {
if (!_.isRegExp(route)) route = this._routeToRegExp(route);
if (_.isFunction(name)) {
callback = name;
name = '';
}
if (!callback) callback = this[name];
var router = this;
Backbone.history.route(route, function(fragment) {
var args = router._extractParameters(route, fragment);
// Edit starts here
// This will trigger the callback previously set
if (typeof router._beforeRoutingCallback === 'function') {
router._beforeRoutingCallback();
}
// Edit stops here.
callback && callback.apply(router, args);
router.trigger.apply(router, ['route:' + name].concat(args));
router.trigger('route', name, args);
Backbone.history.trigger('route', router, name, args);
});
return this;
}
Then, during router initialization:
this.registerBeforeRouting(function() {
console.log("Hello world");
});
I tried the aforementioned approaches, and they somehow just didn't work for me (probably for my lack of in depth understanding of neither backbone, nor javascript in general).
I did manage to do the trick in some other manner, if that's of any interest to anyone out there:
What I actually end up doing was simply extending the View and overriding the render function just one time.
MyApp.BindedView = Backbone.View.extend({
_realRender : null,
initialize : function(){
//validating user is logged in:
if(Backbone.history.fragment != 'login' && !myUser.authenticated())
{
console.log('not authorized, redirecting');
var self = this;
this._realRender = this.render;
this.render = function(route,name,callback){
appRouter.navigate('login');
self.render = self._realRender;
}
return;
}
this.delegateEvents();
}
});
The execute method was added to be overridden for this purpose. See this example extracted from the backbonejs homepage:
var Router = Backbone.Router.extend({
execute: function(callback, args, name) {
if (!loggedIn) {
goToLogin();
return false;
}
args.push(parseQueryString(args.pop()));
if (callback) callback.apply(this, args);
}
});
Related
I want to override "_render: function ()" function from pivot_renderer.js file in web but not working in custom module. Here is the code i am implementing in my custom module:-
odoo.define('MY_CUSTOM_MODULE_NAME.renderer', function (require) {
"use strict";
var PivotRenderer = require('web.PivotRenderer');
var field_utils = require('web.field_utils');
var core = require('web.core');
var _t = core._t;
PivotRenderer.include({
init: function(parent, state, params) {
this._super.apply(this, arguments);
},
_render: function () {
if (!this._hasContent()) {
// display the nocontent helper
this.replaceElement(QWeb.render('PivotView.nodata'));
return this._super.apply(this, arguments);
}
if (!this.$el.is('table')) {
// coming from the no content helper, so the root element has to be
// re-rendered before rendering and appending its content
this.renderElement();
}
var $fragment = $(document.createDocumentFragment());
var $table = $('<table>').appendTo($fragment);
var $thead = $('<thead>').appendTo($table).addClass("CLASS_NAME");
var $tbody = $('<tbody>').appendTo($table);
var nbr_measures = this.state.measures.length;
var nbrCols = (this.state.mainColWidth === 1) ?
nbr_measures :
(this.state.mainColWidth + 1) * nbr_measures;
for (var i=0; i < nbrCols + 1; i++) {
$table.prepend($('<col>'));
}
this._renderHeaders($thead, this.state.headers);
this._renderRows($tbody, this.state.rows);
// todo: make sure the next line does something
$table.find('.o_pivot_header_cell_opened,.o_pivot_header_cell_closed').tooltip();
this.$el.html($table.contents());
return this._super.apply(this, arguments);
},
});
});
In the above, i want to add a class in the header for calling my custom css "var $thead = $('').appendTo($table).addClass("CLASS_NAME");" with this syntax but it is not reflecting in my custom module. Although, for testing, I have implemented same class in default web module and it is working fine. The issue is in custom module.
So how to solve this issue? Is there any other way for calling class or i am doing it in a wrong way?
var $thead = $('').addClass("CLASS_NAME").appendTo($table);
This will work in my case. You can try it.
This question already has answers here:
How to implement chained method calls like jQuery?
(4 answers)
Closed 5 years ago.
I think this equals to
var module = angular.module(...);
module.controller(...);
module.directive(...);
But I'm not sure. And I don't know what happens in angular and why I could write code this way.
I try to debug and trace it but it's so confused.
This is called a fluent API.
Each method will return the module instance, so that another method may be called.
To illustrate we can create a class that does something similar.
class Module {
controller() {
console.log('controller');
return this;
}
directive() {
console.log('directive');
return this;
}
}
When each method has finished, it will return the module instance this so that another method can be chained.
So now we can use this class and chain the methods like this:
new Module().controller().directive();
Or
const module = new Module();
module.controller();
module.directive();
What happens when I use angular.module(…).controller(…).directive(…)?And why?
Short answer
This is good way to write your code in one file.
If you want to split Angular project to different files, use 2nd approach:
var app = angular.module(...);
app.controller(...);
app.directive(...);
Long answer
Also take a look on this angular code snippets (took from https://code.angularjs.org/1.5.6/angular.js):
You can see controller, directive, module, filter, factory, value, provider, decorator, animation, config, component ,run returns moduleInstance
function setupModuleLoader(window) {
var $injectorMinErr = minErr('$injector');
var ngMinErr = minErr('ng');
function ensure(obj, name, factory) {
return obj[name] || (obj[name] = factory());
}
var angular = ensure(window, 'angular', Object);
// We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
angular.$$minErr = angular.$$minErr || minErr;
return ensure(angular, 'module', function() {
/** #type {Object.<string, angular.Module>} */
var modules = {};
return function module(name, requires, configFn) {
var assertNotHasOwnProperty = function(name, context) {
if (name === 'hasOwnProperty') {
throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
}
};
assertNotHasOwnProperty(name, 'module');
if (requires && modules.hasOwnProperty(name)) {
modules[name] = null;
}
return ensure(modules, name, function() {
if (!requires) {
throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
"the module name or forgot to load it. If registering a module ensure that you " +
"specify the dependencies as the second argument.", name);
}
/** #type {!Array.<Array.<*>>} */
var invokeQueue = [];
/** #type {!Array.<Function>} */
var configBlocks = [];
/** #type {!Array.<Function>} */
var runBlocks = [];
var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
/** #type {angular.Module} */
var moduleInstance = {
// Private state
_invokeQueue: invokeQueue,
_configBlocks: configBlocks,
_runBlocks: runBlocks,
requires: requires,
name: name,
provider: invokeLaterAndSetModuleName('$provide', 'provider'),
factory: invokeLaterAndSetModuleName('$provide', 'factory'),
service: invokeLaterAndSetModuleName('$provide', 'service'),
value: invokeLater('$provide', 'value'),
constant: invokeLater('$provide', 'constant', 'unshift'),
decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
config: config,
run: function(block) {
runBlocks.push(block);
return this;
}
};
if (configFn) {
config(configFn);
}
return moduleInstance;
function invokeLater(provider, method, insertMethod, queue) {
if (!queue) queue = invokeQueue;
return function() {
queue[insertMethod || 'push']([provider, method, arguments]);
return moduleInstance;
};
}
function invokeLaterAndSetModuleName(provider, method) {
return function(recipeName, factoryFunction) {
if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
invokeQueue.push([provider, method, arguments]);
return moduleInstance;
};
}
});
};
});
}
Why is it better?
Both approaches do the same so developer will decide what is better for his project structure
for efficiency?
There is no efficiency value measurement, both has same efficiency. No performance penalty.
for what?
In project I want to write each directive each controller each ... in separate file so I use app.controller(...);, app.service(...); ,...
However common directives I want to put in one file so I use:
app.directive(…).directive(…).directive(…).directive(…).directive(…)
Hope it will spread the light on your understanding :)
I have this code in my appController. The code sets the value of $scope.cursorWait to true when there's an HTTP in progress:
$scope.$on('cfpLoadingBar:started', function (event, data) {
$scope.cursorWait = true;
});
$scope.$on('cfpLoadingBar:completed', function (event, data) {
$scope.cursorWait = false;
});
I also have this in my connect service. The functions get called when the internet is disconnected:
isConnectedHandler = (): void => {
var self = this;
self.$rootScope.connected = true;
self.$rootScope.disconnected = false;
self.connectMessage = null;
self.minutes = 0;
}
isNotConnectedHandler = (): void => {
var retry = 0;
var self = this;
self.$rootScope.connected = false;
self.$rootScope.disconnected = true;
How could I monitor the value of $rootScope.disconnected and cursorWait to then set the value of a rootScope variable waiting to true if either $rootScope.disconnected or cursorWait were true?
Assuming your controller has several ViewModels it would like to monitor, let's say cursorWait and connected for example. In this case, Angular provide you with the watchGroup ability. This way you can monitor several variables and in the case one of them change, you may react accordingaly.
Sample Code (Using Typescript for demonstration)
$scope.$watchGroup([()=> { return this.cursorWait }, ()=> { return this.connected}],
(oldValues, newValues)=> {
/* The callback gets an array of `oldValues` and an array of `newValues`,
the index according to the variables you were watching */
});
For more information, refer to Angular documentation.
I have looked at subrouting ideas like those in BackboneMVC and Backbone Marionette.
I think I'm looking for something a little different.
Has anyone come up with good patterns for, not subroutes, but additive routes?
Say, for example, you have a large profile lightbox that could display on any screen. You'd like to add it to browser history and have the url be able to recreate it. So you might have these urls:
'dashboard/lightbox/profile'
'game/3/lightbox/profile'
In the first route, it should do all of the behaviors for the dashboard route, then apply behaviors for an additive lightbox/profile route. This way the lighbox opens and the dashboard is in the background.
In the second case, it should similarly do all the behaviors for the game/3 route, then open the lightbox on top of that.
Is this a pattern anyone has heard of or implemented?
I can't think of a way to do this without using splats, like this:
routes: {
'dashboard/*path': 'showDashboard',
'game/:id/*path': 'showGame'
},
showDashboard: function(path) {
//Show the dash
this._checkIfLightboxShouldOpen(path)
},
showGame: function(id, path) {
//Show the correct game based on id
this._checkIfLightboxShouldOpen(path)
},
_checkIfLightboxShouldOpen(path) {
// Parse the path string for lightbox behaviors
}
Is there a better way?
I needed this for a recent project, I plan to release this code as open source at some point, but you can do something like this:
Create a global router to handle all routing:
App.GlobalRouter = Backbone.Router.extend({
initialize: function(){
this._routes = {};
},
registerRoute: function(route, rootRoute){
var rootName;
if(rootRoute) {
route = rootRoute + '/' + route;
rootName = this.registerRoute(rootRoute);
}
if(!_.isRegExp(route))
route = this._routeToRegExp(route);
var name = this._routes[route] ? this._routes[route] : _.uniqueId('r');
this._routes[route] = name;
this.route(route, name, function(){});
if(rootName) {
this.on('route:'+name, function(){
var args = slice(arguments);
this.trigger.apply(this, ['route:' + rootName].concat(args));
}.bind(this));
}
return name;
}
});
Then create a single one:
App.globalRouter = new App.GlobalRouter();
Then create a modified router to extend all your routers from:
App.Router = Backbone.Router.extend({
constructor: function (options){
options = options || {};
if(options.root) this.root = options.root;
this.globalRouter = App.globalRouter;
Backbone.Router.apply(this, [options]);
},
route: function(route, name, callback, root){
if(!App.globalRouter) return false;
// If callback is root param
if(callback && !_.isFunction(callback)) {
root = callback;
callback = null;
}
// If no name is callback param.
if(_.isFunction(name)) {
callback = name;
name = '';
}
if(!callback)
callback = this[name];
var router = this;
var roots = root || this.root;
if(roots && !_.isArray(roots)) roots = [roots];
if(roots) {
_.each(roots, function(root){
var globalName = App.globalRouter.registerRoute(route, root);
router.listenTo(App.globalRouter, 'route:'+globalName, function(){
var args = slice(arguments);
var callbackArgs = args.slice(callback && -callback.length || 0);
callback && callback.apply(router, callbackArgs);
router.trigger.apply(router, ['route:' + name].concat(callbackArgs));
router.trigger('route', name, callbackArgs);
});
});
} else {
var globalName = App.globalRouter.registerRoute(route);
router.listenTo(App.globalRouter, 'route:'+globalName, function(){
var args = slice(arguments);
var callbackArgs = args.slice(callback && -callback.length || 0);
callback && callback.apply(router, callbackArgs);
router.trigger.apply(router, ['route:'+name].concat(callbackArgs));
router.trigger('route', name, callbackArgs);
});
}
return this;
}
});
From here you can create as many routers that are required and register them on the same route, also you can create a router that has route routes to listen on, so in your case you would have probably 2 or 3 routers, here is an example of what you could do:
var defaultRouter = App.Router.extend({
routes: {
'dashboard': 'showDashboard',
'game/:id': 'showGame'
},
showDashboard: function() {},
showGame: function(id) {},
});
var profilerRouter = App.Router.extend({
root: [
'dashboard',
'game/:id'
],
routes: {'profile', 'showProfile'},
showProfile: function(){//Show lightbox}
});
This will listen for /dashboard or /game/:id and call that funciton on defaultRouter that is listening. Then if the /profile is on the end of the url for either of the routes is will catch that and run the showProfile function on the profileRouter.
NOTE: I've quickly modified the code take from my project to change some of the name/namespace issues, so you might need to check that I haven't missed anything, but the code should be right otherwise
Updated Example:
If the user navigates to /game/:id it will call the defaultRouter > showGame with param :id.
If the user navigates to /game/:id/profile it will call the defaultRouter > showGame with param :id. It will also call profileRouter > showProfile but with no params (ie. it doesn't send the :id from the /game/:id root).
Will Backbone.Router.navigate set test to true:
var test = false;
var Router = Backbone.Router.extend({
routes: {
'posts': 'showPosts'
},
showPosts: function () {
test = true;
}
});
router = new Router();
Backbone.history.start();
router.navigate('posts?foo=3', {trigger: true});
assert.ok(test);
Eg, will posts?foo=3 fragment will match the posts route by default, or do I have to set another route for that, for example: posts?*querystring?
Thank you
PS: I know there exist the backbone-query-parameters but I want to know just for backbone.
You need to add another route with that expecting parameter :
routes: {
'posts?foo=:foo' : 'showPosts',
'posts': 'showPosts'
},
showPosts: function (foo) {
if(typeof foo != 'undefined'){
// foo parameters was passed
}
test = true;
}
update
You could define the general route to return all the query string and then parse it in the handler :
routes: {
'posts': 'showPosts',
'posts?*queryString' : 'showPosts'
},
showPosts: function (queryString) {
var params = parseQueryString(queryString);
if(params.foo){
// foo parameters was passed
}
}
...
// and the function that parses the query string can be something like :
function parseQueryString(queryString){
var params = {};
if(queryString){
_.each(
_.map(decodeURI(queryString).split(/&/g),function(el,i){
var aux = el.split('='), o = {};
if(aux.length >= 1){
var val = undefined;
if(aux.length == 2)
val = aux[1];
o[aux[0]] = val;
}
return o;
}),
function(o){
_.extend(params,o);
}
);
}
return params;
}
update 2
Here's a live demo to see the code in action.
Just to complement the previous answers, instead of defining two routes that have the same callback, like:
routes: {
'posts': 'showPosts',
'posts?*querystring': 'showPosts'
}
You could have only one route to keep the code cleaner:
routes: {
'posts(?*querystring)': 'showPosts'
}
Backbone docs:
Routes can contain parameter parts, :param, which match a single URL component between slashes; and splat parts *splat, which can match any number of URL components.
If you still want to keep the functionality without the matching you can define two routes
routes: {
'posts': 'showPosts',
'posts?*querystring': 'showPosts'
}
showPosts: function(querystring) {
if (querystring) {
// here you can parse your querystring, for your case the querystring variable is
// 'foo=3'
}
//here you'll show posts according to the querystring (or lack thereof)
}
Here's another take, still using lodash (underscore). Removed the _.map, added a bit of verbosity to the variables, and stripped out the starting '?' if present:
function parseQueryString(queryString)
{
if (!_.isString(queryString))
return
queryString = queryString.substring( queryString.indexOf('?') + 1 )
var params = {}
var queryParts = decodeURI(queryString).split(/&/g)
_.each(queryParts, function(val)
{
var parts = val.split('=')
if (parts.length >= 1)
{
var val = undefined
if (parts.length == 2)
val = parts[1]
params[parts[0]] = val
}
})
return params
}
RFC 3986 "syntax for URIs" states that query parameters shold come before hash fragment.
In URIs a hashmark # introduces the optional fragment near the end of the URL. The generic RFC 3986 syntax for URIs also allows an optional query part introduced by a question mark ?. In URIs with a query and a fragment, the fragment follows the query.
I have this issue handling a redirect I am getting from the server i.e. "http://foo.com/main.html?error=errormessage#site". I would like to route on the query but can't see a way to write the backbone route expression to handle this url. For now I just route on the hash and check for a query by parsing location.search.