Background
We have been using web application for more than 2 decades now. This has made us tuned to some of the functionalities that the websites provide. One such functionality is to be able to copy the URL and use it for the viewing the exact application area that we were viewing before. Another example is the use of browser navigation buttons to navigate back and forth the pages.
When we create single page applications, there is only one page being rendered on the screen. There is no separate URL for each page. The browser is not loading the separate pages for separate screens. So how can we still perform the above mentioned operations even with a single page application. The answer is backbone routes.
Link to complete series:
- Part 1: Introduction to Backbone.Js
- Part 2: Understanding the basics of Backbone Models
- Part 3: More about Backbone Models
- Part 4: CRUD Operations on BackboneJs Models using HTTP REST Service
- Part 5: Understanding Backbone.js Collections
- Part 6: Understanding Backbone.js Views
- Part 7: Understanding Backbone.js Routes and History
- Part 8: Understanding Backbone.js Events
Using the code
Backbone routes and history provides us the mechanism by which we can copy the URLs and use them to reach the exact view. It also enables us to use browser navigation with single page applications. Actually routes facilitate the possibility of having deep copied URLs and history provides the possibility of using the browser navigation.
Life without Router
Let us try to create a simple application that is not using the router. Lets create three simple views and these views will be rendered in the same area on our application based on user selection. Let create 3 very simple views.
var View1 = Backbone.View.extend({ initialize: function() { this.render(); }, render: function() { this.$el.html(this.model.get('Message') + " from the View 1"); return this; } }); var View2 = Backbone.View.extend({ initialize: function() { this.render(); }, render: function() { this.$el.html(this.model.get('Message') + " from the View 2"); return this; } }); var View3 = Backbone.View.extend({ initialize: function() { this.render(); }, render: function() { this.$el.html(this.model.get('Message') + " from the View 3"); return this; } });
Now we need a view that will contain the view and render it whenever the user makes a choice on the screen.
var ContainerView = Backbone.View.extend({ myChildView: null, render: function() { this.$el.html("Greeting Area"); this.$el.append(this.myChildView.$el); return this; } });
Now lets create a simple div on the UI which will be used as elto this ContainerView. We will then position three buttons on the UI which will let the user to change the view. Below code shows the application setup that is creating the container view and the functions that will get invoked when the user selects the view from screen.
var greeting = new GreetModel({ Message: "Hello world" }); var container = new ContainerView({ el: $("#AppContainer"), model: greeting }); var view1 = null; var view2 = null; var view3 = null; function showView1() { if (view1 == null) { view1 = new View1({ model: greeting }); } container.myChildView = view1; container.render(); } function showView2() { if (view2 == null) { view2 = new View2({ model: greeting }); } container.myChildView = view2; container.render(); } function showView3() { if (view3 == null) { view3 = new View3({ model: greeting }); } container.myChildView = view3; container.render(); }
Now lets run the application and see the results.
When we click on the buttons we can see that the actual view is getting changes but the URL is not getting changes. That would mean that there is no way, I can copy a URL and directly go to any view. Also, the second thing to note here is that if we press the browser back button, the application will go away(since its still on the same single page from the browser’s perspective).
Note: Please download and run the sample code to see this in action.
Hello Backbone Routes
Now the above problem can very easily be solved using Backbone routesand History. So lets try to first look at what are backbone routes.
Backbone routes are simple objects that are handle the incoming route value from the URL and the invoke any function. Lets create a very simple route class for our application.
var myRouter = Backbone.Router.extend({ });
In our route class we will have to define the routes that our application will support and how we want to handle them. So first lets create a simple route where only the URL is present. This usually is the starting page of our application. For our application lets just open view1 whenever nothing is present in the route. Then if the request is for any specific view we will simply invoke the function which will take care of rendering the appropriate view.
var myRouter = Backbone.Router.extend({ greeting: null, container: null, view1: null, view2: null, view3: null, initialize: function() { this.greeting = new GreetModel({ Message: "Hello world" }); this.container = new ContainerView({ el: $("#rAppContainer"), model: this.greeting }); }, routes: { "": "handleRoute1", "view1": "handleRoute1", "view2": "handleRoute2", "view3": "handleRoute3" }, handleRoute1: function () { if (this.view1 == null) { this.view1 = new View1({ model: this.greeting }); } this.container.myChildView = this.view1; this.container.render(); }, handleRoute2: function () { if (this.view2 == null) { this.view2 = new View2({ model: this.greeting }); } this.container.myChildView = this.view2; this.container.render(); }, handleRoute3: function () { if (this.view3 == null) { this.view3 = new View3({ model: this.greeting }); } this.container.myChildView = this.view3; this.container.render(); } });
Now this route class contains the complete logic of handling the URL requests and rendering the view accordingly. Not only this, we can see that the code which was written in a global scope earlier i.e. the controller and view creation all that is put inside the route now. This would also mean that routes not only provide us deep copyable URLs but also could provide more options to have better structured code(since we can have multiple route classes and each route class can handle all the respective views for the defined routes).
BackBone History and Instantiating Routes
Backbone history is a global router that will keep track of the history and let us enable the routing in the application. To Instantiate a route and start tracking the navigation history, we need to simply create the router class and call Backbone.history.start for let the backbone start listening to routes and manage history.
$(document).ready(function () { router = new myRouter(); Backbone.history.start(); })
Invoking and requesting Routes
A route can either be invoked from the other parts of the application or it can simply be requested by the user.
Invoking Route: Application wants to navigate to a specific route (this can be done by navigating to a route by calling the navigate function: router.navigate('view1');
Route Request: User Enters the fully qualified URL (this will work seamlessly)
Let us run the application and see the result.
Passing parameters in the Routes
We can also pass parameters in the route. Lets us try to create a new route where the user will request for a view in a parameterized manner. Parameters can be defined as “ route/:param”
var myRouter = Backbone.Router.extend({ greeting: null, container: null, view1: null, view2: null, view3: null, initialize: function () { this.greeting = new GreetModel({ Message: "Hello world" }); this.container = new ContainerView({ el: $("#rAppContainer"), model: this.greeting }); }, routes: { "": "handleRoute1", "view/:viewid": "handleRouteAll" }, handleRouteAll: function (viewid) { if (viewid == 1) { this.handleRoute1(); } else if (viewid == 2) { this.handleRoute2(); } else if (viewid == 3) { this.handleRoute3(); } }, handleRoute1: function () { if (this.view1 == null) { this.view1 = new View1({ model: this.greeting }); } this.container.myChildView = this.view1; this.container.render(); }, handleRoute2: function () { if (this.view2 == null) { this.view2 = new View2({ model: this.greeting }); } this.container.myChildView = this.view2; this.container.render(); }, handleRoute3: function () { if (this.view3 == null) { this.view3 = new View3({ model: this.greeting }); } this.container.myChildView = this.view3; this.container.render(); } });
The above route can be invoked by passing view/2 as URL. the viewId passed to the router will be 2.
Having optional Parameters in Routes
We can also pass optional parameters in the routes, Lets try to pass a simple parameter in the above defined route and see how it works. optional parameters can be defined as “ route(/:param)”.
var myRouter = Backbone.Router.extend({ routes: { "": "handleRoute1", "view1": "handleRoute1", "view2": "handleRoute2", "view3": "handleRoute3", "view/:viewid(/:msg)": "handleRouteAll" }, handleRouteAll: function (viewid, msg) { if (msg) { alert(msg); } } });
In the above code, if we pass the second parameter i.e. view/2/test, the alert will be shown else not.
Note: The route definition can also contain complex regex based patterns if we need one route to handle multiple URLs based on some regular expression.
Point of interest
In this article we saw backbone.js routes. We saw how routes enable us to create bookmarkable URLs and will let the user request a view based on URL. We also looked at how we can use browser navigation by using backbone history. This has been written from a beginner’s perspective. I hope this has been informative.
原文链接: http://rahulrajatsingh.com/2014/08/backbone-tutorial-part-7-understanding-backbone-js-routes-and-history/