Backbone JS Routing not working as I expect - javascript

I think I'm missing some basics about Backbone's routing functions.
I'm building an app and it looks something like so:
file: app.js
App = {}
App.nav = new Backbone.Router;
require('app/controller');
file: controller.js
App.nav.route('home', 'home', function () {
console.log("Home Activated");
});
App.navigate('home');
At this point the browser changes the URL in the address bar to /home but nothing happens and I don't get the Home Activated console message.
I've tried using my own routing class (i.e. Backbone.Router.extend({})) but I don't really see a point in it as I still need to initialize it, and I want to use a central history/navigation in my app that all modules/controllers add routing to it rather than creating a router for every controller.
What am I doing wrong?

http://documentcloud.github.com/backbone/#Router-navigate
From the documentation:
If you wish to also call the route function, set the trigger option to true.
But as OlliM wrote, you need to activate the history first!
So your answer should be:
Backbone.history.start();
App.nav.navigate('home', {trigger: true});
edit:
forgot to put "nav"

For the routing to work, you need to call Backbone.history.start() after setting up your routes (basically after you've done everything else). See: http://documentcloud.github.com/backbone/#History-start

I just want to point this out as it saved me a world of hurt and heartache.
If you are routing to a custom page such as
Backbone.router.navigate('/some/page'); // does not work
And it appears to not be working. Add a trailing '/'
Backbone.router.navigate('/some/page/'); // works
This cost me a few hours of troubleshooting...

Related

emberjs using multiple paths / urls for one route

In Ember I can use this:
App.Router.map(function() {
this.route('accomodations');
});
so if one goes to /accomodations it will load that view.
I can also add:
App.Router.map(function() {
this.route('accomodations', { path: '/travel' });
});
so if one goes to /travel, it will go to the same view.
I want to be able to have /accomodations and /travel go to the same view? is this possible?
I know that this:
App.Router.map(function() {
this.route('accomodations');
this.route('accomodations', { path: '/travel' });
});
Will do what I'm asking, but if they go to accommodations, it should show that in the url, it always shows travel. I'm not even sure if the final piece of code is best practice.
You can simply interchange the two route-path definition lines:
App.Router.map(function() {
this.route('accomodations', { path: '/travel' });
this.route('accomodations');
});
The last definition takes the precedence for URL display in {{link-to ...'accomodations'}} and Route#transitionTo('accomodations') in-app transitions, though entering the app by '/travel' will leave the URL as is.
(EmberJS 1.11.3, 2.12.2)
Using redirection
In router.js
App.Router.map(function() {
this.route('accomodations');
this.route('travel');
});
In routes/travel.js
App.TravelRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('accomodations');
},
});
You do not have to put these in separate files (that's just where I would put them).
What this does is register two routes with the router. Pick one of them to be the "main" route, and the other the "alias" route. In this case, accomodation is the main route, and travel is its alias. When the user visits /travel, they get redirected to /accomodation.
This would be the default/ standard Ember way of accomplishing this, and if this sounds good to you, go for this.
Another possible solution
If you do not wish to have redirection happen, for some reason, and want the URL seen by the user to stay the same, but still display the same things, and behave in the same way, this is also possible.
In this case, you would create two of every single Ember unit (route, controller, view, template). The smart way would be to create a base class route, and have both App.TravelRoute and App.AccomodationRoute trivially extend it; create a base class controller, and have both App.TravelController and App.AccomodationController trivially extend it; same for views, if you have them.
Templates, OTOH, are a little trickier, because there is not way to extend them (that I know of). SO what you would need to do, is create either a partial or a component (decided which works better for you), and then reuse that partial/ component in both templates/accomodation.hbs and templates/travel.hbs

Marionette router is undefined

I cannot run the code below because router is undefined in my view. However I'm struggling to understand where I actually defined var router = new MyRouter(); my view, controller app start etc?
I'm using marionette and my router seems to automatically match my controllers.
success: function (page) {
id = page.get('id')
router.navigate('page/' + id, {trigger: true});
}
If you didn't declare a router attribute, you don't have one available (it doesn't get created automatically or anything), hence the undefined value.
To navigate to a given page, you can also use Backbone.history.navigate (see for example https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/app.js).
In addition, you might want to reconsider using the trigger: true option, per the reasons explained here (Routing chapter) in the free sample to my book on Marionette.

Backbone Routes - trigger route on page load

This is a simple question, but I am new to routing and haven't been able to find an answer to this.
I have a Marionette Router (if I intimidate you, its really the same thing as a Backbone Router. Very simple).
sys.routes = {
"app/:id": "onAppRoute",
};
sys.Router = new Marionette.AppRouter({
controller: {
onAppRoute: function(route) {
console.log('Called app route!');
},
},
appRoutes: sys.routes,
});
Backbone.history.start({pushState: true})
This works - if you hit the back button my browser, the url will change within my Single Page Application and will call the onAppRoute function.
However, let's say I open a new browser window and paste in my page url to a certain 'app':
http://localhost/app/myApplication
This doesn't call the onAppRoute function. It doesn't even seem like it should, though, but I really don't know.
I want it to.
I don't know if I am doing it wrong, or if I should just manually fire it by grabbing my page url on page load, parsing it, then 'navigating' to that route. Seems hacky.
Contrary to your intuition, backbone's default behaviour is to trigger matching routes on page load! cf. http://backbonejs.org/#Router - look for the option silent: true. You'd have to specify that for the router to IGNORE your matching routes on page load, i.e. not trigger the corresponding callbacks.
So your problem lies somewhere else: your routes do NOT match the url you have stated as an example. Clearly, you require an :id parameter, trailing http://localhost/app/myApplication. Therefore, http://localhost/app/myApplication/213 would cause your callback to be triggered on page load, given you didn't pass silent: true as an option to backbone.history.start().
If you want to match the 'root' url, i.e. no params, you would define the following route:
routes: {
'/': someFunction
}
The :id part is a parameter, which will be extracted by Backbone.Router and sent as an argument to onAppRoute.
But in your URL you don't have any parameters /localhost/app/myApplication

Directly calling url with Backbone

If I navigate to a view by clicking on a link such as 127.0.0.1/#/project/1, the correct view gets displayed. However, if I call this url directly in the browser (or hit refresh), the view won't be displayed. What could be the reason for this behaviour?
The way I set up the Router is as follows:
var AppRouter = Backbone.Router.extend({
routes: { },
initialize:function () { }
});
var app = new AppRouter();
and then in every module (I'm using require.js), a route and handler will be added
app.route("project/:id", "showProject");
Could it be that the routes aren't registered yet and thus the callbacks won't be called?
Make sure that you are calling Backbone.history.start() after all of your routers are loaded/instantiated and routes defined: http://backbonejs.org/#History-start
Alternatively, you could stop the history with Backbone.history.stop(), and start it again. Then the added route(s) will be picked up.
BTW, you can test if the history is currently started with the boolean Backbone.History.started (note the capital 'H' is necessary).

Backbone JS how to render a specific page

I'm just starting to play with Backbone JS as we are starting a very JS intensive application and I want to follow a convention to keep the project from looking horrific in later months and years.
This is probably a simple question but I can't figure it out.
I have the normal RESTful actions/views etc. and have added another call info just to play with. This renders the index page correctly when the respective link is clicked.
:javascript
$(document).ready(function() {
$("#client-clicker").click( function() {
window.router = new Pm.Routers.ClientsRouter({clients: #{#clients.to_json.html_safe}});
Backbone.history.start();
});
});
Now for the questions:
How does this know to go to the index action of the ClientsRouter?
How do I specify the edit, show or info actions?
Thanks in advance!
I have never used a "convention" for js but backbone js just makes sense!
Update
I did find that if I delete this line in the Clients Router, the default to index breaks.
routes:
"/new" : "newClient"
"/index" : "index"
"/:id/edit" : "edit"
"/:id" : "show"
".*" : "index" <----- #deleted and it broke
"/info" : "info"
I'm still not sure how to hit a specific view.
When you call Backbone.history.start Backbone starts watching for changes to the location, either using pushstate or hashchange events or in older browsers polling the location. It also immediately triggers the route for the current location by comparing window.location.pathname with the route table.
In your case it matches on the ".*" route. I am not sure what the location is at the time this happens but this route will match any location so that's why it is triggering the index action.
You can change location and optionally trigger an action by calling the navigate method on your router. e.g.
window.router.navigate('/new', true);
should change the location and (because you are passing true in the second parameter) trigger the newClient action.

Categories

Resources