I have a complex, multi level inherited app and i wanted to use Backbone.Router for navigation but it dont work as i expected.
The address of the application is not under root directory
Like this:
http://www.domain.com/App
and I wanted to use the BB's routing
Here is some code:
$(function () {
var SayfaController = Backbone.Router.extend({
routes: {
"": "home",
"sayfa/:sayfaNo": "sayfa"
},
initialize: function () {
console.log('THIS WORKS');
},
home: function () {
console.log('THIS DONT FIRE');
},
sayfa: function (sayfa) {
console.log("NEITHER THIS FIRES");
console.log(sayfa);
}
});
var sayfaController = new SayfaController();
Backbone.history.start({pushState: true});
....
});
initialize method works but
The events wont fire when i click a link like this:
Click
or change the browser navigation bar
What am I doing wrong
From a glance, it looks like you need to specify the root of your app, as it's not served from the root of your domain. See Backbone docs RE: Backbone.history:
If your application is not being served from the root url / of your
domain, be sure to tell History where the root really is, as an
option: Backbone.history.start({pushState: true, root:
"/public/search/"})
try Backbone.history.start({pushState: true, root: "/App/"});
I am not sure of how to get it work for urls of kind
http://www.domain.com/App/#/sayfa/6
But for kind below it works.
http://www.domain.com/App#sayfa/6
http://www.domain.com/App
Use
Backbone.history.start({
root : "/App"
});
Here is the jsfiddle http://jsfiddle.net/nEmeL/4/
Related
Having a route like 'dogs': 'process', I need to rewrite it to 'animals': 'process'.
Now, I need the router to recognize both routes, but always display the url like /animals, it is sort of aliasing, but could not find any info on how to solve this without placing an url redirect in 'process' handler.
I'm assuming that the real need for aliases is different than dogs to animals, so I'll answer regardless of if the use-case here is good or not. But if you don't want to change the hash but want to trigger different behaviors in the app, using the router is probably not the route to go.
Route aliases don't really exist in Backbone, other than defining different routes using the same callback. Depending on your exact use-case, there are multiple ways to handle similar routes.
Replace the hash
To display the same hash for a generic route coming from different routes, use the replace option of the navigate function.
routes: {
'lions': 'animalsRoute',
'animals': 'animalsRoute'
},
animalsRoute: function() {
this.navigate("#/animals", { replace: true });
// or using the global history object:
// Backbone.history.navigate("#/animals", { replace: true });
}
then handle the animals route, regardless of which route was initially used to get in this callback.
Some other answers or tutorials will say to use window.location.hash but don't. Manually resetting the hash will trigger the route regardless and may cause more trouble than it'll help.
Different behaviors but showing the same route
Just use different callbacks, both using the replace trick above.
routes: {
'lions': 'lionsRoute',
'tigers': 'tigersRoute'
},
showGenericRoute: function() {
this.navigate("#/animals", { replace: true });
},
tigersRoute: function() {
this.showGenericRoute();
// handle the tigers route
},
lionsRoute: function() {
this.showGenericRoute();
// handle the lions route
}
Notice the inexistent animalsRoute. You could add the route if there's a generic behavior if no specific animal is chosen.
Use the route params
If you want to know which animal was chosen but still use the same callback and remove the chosen animal from the hash, use the route params.
routes: {
'animals/:animal': 'animalsRoute',
},
animalsRoute: function(animal) {
// removes the animal from the url.
this.navigate("#/animals", { replace: true });
// use the chosen animal
var view = new AnimalView({ type: animal });
}
Redirect to the generic route
If you want a different behavior but always show the same route, use different callbacks, then redirect. This is useful if the generic route is in another router instance.
var Router = Backbone.Router.extend({
routes: {
'animals': 'animalsRoute'
},
animalsRoute: function() {
// handle the generic behavior.
}
});
var PussyRouter = Backbone.Router.extend({
routes: {
'lions': 'lionsRoute'
// ...
},
lionsRoute: function() {
// handle lions, then redirect
this.navigate("#/animals", { trigger: true, replace: true });
}
});
Using the trigger options will call the animalsRoute in the other router and the replace option will avoid making an entry in the history, so pushing the back button won't go to lions to get back to animals and being caught in the animals route.
I'm using flatiron/director to do client-side routing.
I have routes like:
var routes = {
'': function() { loadLandingPage(); },
'/author': function() { author(); }
};
If I go to www.example.com#/author, it triggers the author function. If I go to www.example.com# or www.example.com#/ then my landing page is loaded.
However, I want to define a default route for the root of my site without the #. I want the landing page to load even when we land on www.example.com without the #.
I have looked through the documentation, but for the life of me can not figure it out. I thought that the configuration: notfound method would do what I'm thinking, but it did nothing. I tried the following:
var router = Router(routes).configure({ notfound: loadGlobalPage});
and
var router = Router(routes).configure({ notfound: function () {loadGlobalPage();} });
and also after reading this thread, I tried:
router.notfound = function() {
loadGlobalPage();
};
I also tried a hack unrelated to the router:
function determineIfLandingPage() {
if (window.location.hash == "") {
loadGlobalPage();
window.location.href += "#"
}
}
This works but it feels like a hack.
This must be a somewhat common use case--how do I get the desired functionality from my router?
From the document: redirect method will solve this problem.
route.init(['/']) will redirect to the route / if '/#/' is not found in the URL.
I'm working on a simple web app using Ember. I am rendering a nested resource into the application template rather than it's parent resource.
This works fine except if I press the back button I go back to the parent resource but the parent template is not rendered into the application outlet. I can refresh the page and bingo it renders then.
Router:
Movies.Router.map(function () {
this.resource('list', { path: '/list' }, function() {
this.route('add');
// Nested resource example
this.resource('movies', { path: '/:list_id/movies' }, function() {
});
});
this.route('boxoffice');
});
Movies Route:
Movies.MoviesRoute = Ember.Route.extend({
model: function(params) {
return Movies.List.find(params.list_id);
},
renderTemplate: function() {
this.render('movies', {
// template outlet to render into (will mess up your back btn!)
into: 'application'
});
}
});
Thanks in advance!
By default the ember Router uses the browser's hash to load routes of your application and will keep it in sync. This relies on a hashchange event existing in the browser.
But you can setup ember to use the browser history API instead of hash which is the default. This can be accomplished in different way's. For example like this:
App.Router = Ember.Router.extend({
location : Ember.Location.create({
implementation : 'history' // can be hash, history or none
})
});
Or by a more simpler approach by reopening the router like this:
App.Router.reopen({
location: 'history'
});
This way using the browser back & forward buttons would work as expected.
For more info on the history API see here.
Hope it helps
I am using Backbone's "all" event to catch all route events in my app in order to log the page views. This works well as long as I don't use navigate to manually trigger a route.
In the following example, I forward the user from the dashboard route to the login route. Backbone fires the event AFTER the route callback is executed, leading to the following output:
showDashboard
showLogin
route:showLogin
tracking:/login
route:showDashboard
tracking:/login
Obviously this is not what I want. I know I could call showLogin instead of using navigate to trigger the login route and this is what I am doing right now, but I would like to know why the order of the events is not the same than the order of the triggered callbacks.
Here is my router (shortened):
var AppRouter = Backbone.Router.extend({
routes: {
"/login": "showLogin",
"": "showDashboard",
},
initialize: function() {
return this.on('all', this.trackPageview);
},
trackPageview: function(eventName) {
console.log(eventName);
var url = Backbone.history.getFragment();
console.log('tracking: ' + url);
},
showDashboard: function() {
console.log('showDashboard');
// check if the user is logged in etc.
this.navigate('#/login', { trigger: true });
},
showLogin: function() {
console.log('showLogin');
}
});
Backbone's Router is actually very simple, and if you read the code you'll see the following in it's constructor:
this._bindRoutes();
this.initialize.apply(this, arguments);
_bindRoutes attaches all your routes as you expect, and it does this before your initialize function gets called. So your binding will always fire after Backbone's does.
You're probably going to be better off finding another way to do this.
You could call a before type function yourself in your routes to do stuff like track pageviews/etc. Or maybe you could just override route, track your pageview and then make sure to call Backbone's implementation with something like Backbone.Router.prototype.route.call(arguments);
Currently building an app that runs on mobile phones
not related to the issue at hand, but through a certain event
the app lands in a state, either online or offline (internet available on the phone or not)
the offline app is very limited, only a few screens available)
now stop me if you catch me doing something stupid or something that i could to a lot better,
but my first thought was to have the Router have a dynamic set of routes,
much like you can define a dynamic url property on a collection.
so instead of this:
var router = Backbone.Router.extend({
routes: {
'' : 'homeAction',
'categories' : 'categoriesAction',
...
},
homeAction: function(){ },
categoriesAction: function(){ }
});
i was thinking of this:
var router = Backbone.Router.extend({
initialize: function(){
this.on('app:togglestate', this.toggleRoutes, this);
},
toggleRoutes: function () {
var router = this;
if(App.onlineModus)
router.routes = { /* hash with online routes here */ };
else
router.routes = { /* hash with offline routes here */ };
},
routes: {
'' : 'homeAction',
'categories' : 'categoriesAction',
...
},
homeAction: function(){ },
categoriesAction: function(){ }
});
though that aparently breaks the whole app,
as the Backbone.history.start(); throws an error, cannot call function start from undefined.
leading me to believe i the routes object is somehow used upon initialization and cannot be changed on the fly.
am i possibly thinking to far?
should i achieve this some other way?
other idea's i had were:
having the routes exactly like url, where the routes argument is a function returning a hash, that didn't work either
and now i'm thinking totally differnt, something along the lines of testing if the app is in online or offline modus in every route's Action. though that seems too mutch i'd probably have to relay them all through a single Action, which only passes to the actual action if the route is accessible in offline modus? but i would not really have a clear idea on how to start with such a relay action without writing too mutch boilerplate code...
In order to dynamically update the routes you will need to make a call to _bindRoutes() after updating the routes.
For example:
toggleRoutes: function () {
var router = this;
if(App.onlineModus)
router.routes = { /* hash with online routes here */ };
else
router.routes = { /* hash with offline routes here */ };
// Get rid of previous navigation history
if(Backbone.history){
Backbone.history == null;
}
// Bind the new routes
router._bindRoutes();
}
Note that when you dynamically change the routes the history is no longer valid so you need to delete the previous history. When _bindRoutes is called it automatically instantiates a new Backbone.history when is called this.route.
I had to do something very similar. I don't have the code in front of me, but this should be right around what I did: (Edit: fleshed it out a bit so you can actually run it now)
ApplicationRouter = Backbone.Router.extend({
//some stuff
constructor: function (routes) {
this.routes = routes;
Backbone.Router.prototype.constructor.call(this);
}
});
routeObject = {
"help": "help"
}
ApplicationRouter.instance = function(routes) {
if (!this._instance) {
this._instance = new ApplicationRouter(routes);
}
return this._instance;
}
ApplicationRouter.instance(routeObject);
ApplicationRouter.instance().on('route:help', function() {
console.log('helped');
});
Backbone.history.start();
//now go to page#help - the console will say "helped"
From then on out, I just referenced ApplicationRouter.instance() when I needed access to the application router.