Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

BackBone Tutorial – Part 7: Understanding Backbone.js Routes and History

4.92/5 (16 votes)
23 Feb 2015CPOL6 min read 64.7K   1.4K  
In this article, we will try to understand how routes can be useful in a large scale single page applications and how we can use routes to perform action based on requested URL.

Introduction

In this article, we will try to look at Routes in Backbone.js. We will try to understand how routes can be useful in a large scale single page applications and how we can use routes to perform action based on requested URL.

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 the pages back and forth.

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:

  1. BackBone Tutorial – Part 1: Introduction to Backbone.Js[^]
  2. BackBone Tutorial – Part 2: Understanding the basics of Backbone Models[^]
  3. BackBone Tutorial – Part 3: More about Backbone Models[^]
  4. BackBone Tutorial – Part 4: CRUD Operations on BackboneJs Models using HTTP REST Service[^]
  5. BackBone Tutorial – Part 5: Understanding Backbone.js Collections[^]
  6. BackBone Tutorial – Part 6: Understanding Backbone.js Views[^]
  7. BackBone Tutorial – Part 7: Understanding Backbone.js Routes and History[^]
  8. BackBone Tutorial – 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. Let's create three simple views and these views will be rendered in the same area on our application based on user selection. Let's create 3 very simple views.

JavaScript
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.

JavaScript
var ContainerView = Backbone.View.extend({
     myChildView: null,
     
     render: function() {
        this.$el.html("Greeting Area"); 

        this.$el.append(this.myChildView.$el); 
        return this;
    }
});

Now, let's create a simple div on the UI which will be used as el to this ContainerView. We will then position three buttons on the UI which will let the user change the view. The 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.

JavaScript
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 let's run the application and see the results.

Image 1

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 it's 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 routes and History. So let's try to first look at what backbone routes are.

Backbone routes are simple objects that handle the incoming route value from the URL and then invoke any function. Let's create a very simple route class for our application.

JavaScript
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, let's create a simple route where only the URL is present. This usually is the starting page of our application. For our application, let's 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.

JavaScript
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 letting the backbone start listening to routes and manage history.

JavaScript
$(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.

  1. 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');
  2. Route Request: User enters the fully qualified URL (this will work seamlessly)

Let us run the application and see the result.

Image 2

Passing Parameters in the Routes

We can also pass parameters in the route. Let's 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".

JavaScript
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.

Image 3

Having Optional Parameters in Routes

We can also pass optional parameters in the routes. Let's try to pass a simple parameter in the above defined route and see how it works. Optional parameters can be defined as "route(/:param)".

JavaScript
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.

Image 4

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.

History

  • 1st August, 2014: First version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)