Click here to Skip to main content
Click here to Skip to main content

CRUD operation using Backbone.js

, 3 Jul 2014
Rate this:
Please Sign up or sign in to vote.
Learning backbone.js using basic CRUD operations

Introduction

Learning backbone.js for basic create, read, update and delete operations. We will create a basic application which would help us understanding how to code using backbone.js.

What is Backbone.js

When ever we are working with javascript application we tend to put huge effort structuring the code and putting the right peice at right place. Which actually may or may not be very maintainable or scalable. From the backbone official site -

It's all too easy to create JavaScript applications that end up as tangled piles of jQuery selectors and callbacks, all trying frantically to keep data in sync between the HTML UI, your JavaScript logic, and the database on your server. For rich client-side applications, a more structured approach is often helpful.

Now there comes Backbone.js. It is a very light framework which allows you to structure your application in MV* fashion. MV stands for the basic Model and Views what we have in general terms. But whats important is *. Frankly, I am not the developers behind Backbone so its hard to assume that it fits closely towards MVC framework. I assume it to be Controller that in this case allows you to save the state of your javascript application via a hashtag url. More Details from Stackoverflow

Example

The very first example which strikes me of is Gmail. Where huge chunks of data is being downloaded one you are logging in. Then things remains sweet and simple and the background processes takes the charge. There is never a whole page refresh that takes place. Awesome!

Note

I will be ignoring the backed code as of now. Assuming we have ready api for Create/Read/Update/Delete.

In this tutorial, I have used ASP.NET MVC API for the CRUD operations and toastr for notification's. I am a huge fan of this lib, it's just awesome.

What we will do

We will try to create an application that will allow Creation of user, Displaying list of users, Editing specific user and Deleting specific user.

Setting up the environment

Lets start:

  1. Create a new ASP.NET MVC project. (e.g. UserManagement)
  2. Select Internet as your template.
  3. Backbone.js is not present by default. So we need to right click on References and select Manage Nuget Packages and search for Backbone.
  4. Install Backbone.js from the list of Packages.
  5. Goto BundleConfig.cs and add a new bundle and reference it in _Layout.cshtml or you could side line it and add your backbone.js in an existing bundle which is being referenced in _layout.cshtml.

When you add a new bundle:

public static void RegisterBundles(BundleCollection bundles)
{
    // existing code

    bundles.Add(new ScriptBundle("~/bundles/backbone").Include(
                "~/Scripts/underscore.js",
                "~/Scripts/backbone.js"));
    // existing code
}

Run your application to check if throws any error in console.

Setting up the User Model(class)

Once your done with the above step, lets start with creating new model class for our User Management application.

public class User
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
}

Assuming the above class and we will proceed with client side scripting. You can create your CRUD api's.

Displaying list of Users

Creating Route

var Router = Backbone.Router.extend({
    routes: {
        '': 'home',//Default route, when no hash tag is available
        'create': 'create',// Create User, when user clicks on Create button
        'edit/:id': 'edit'
    }
});

Now, we are creating routes for Listing/Edit/Create of a User. When our url is:

http://localhost/#

We will be displaying list of users.

http://localhost/#create

We will be displaying user details screen which will be used for creating a user i.e. we need to display Create page for user.

http://localhost/#edit

We will be displaying user details screen with populated data which will be used for editing/updating a user.

Defining the Routes

var route = new Router();

// When hash tag has localhost# register the below route
route.on('route:home', function () {

    // Todo: code to render create page
    console.log("Display Create Page");
});

// When hash tag has localhost#create register the below route
route.on('route:create', function () {

    // Todo: code to render create page
    console.log("Display Create Page");
});

//When hash tag has localhost#edit/1 register the below route
route.on('route:edit', function () {

    // Todo: code to render edit page and render user details
    console.log("Display Edit Page");
});

Now, we define the routes as above, what it will do is, when the hashtag changes in the url the corresponding route defination will get triggered. We will come back to this section very soon.

Run your application to check if throws any error in console. Change the url to /# or /#create or/#edit/1, you should see corresponding console statements being printed.

Creating Model

What the official's say:

Models are the heart of any JavaScript application, containing the interactive data as well as a large part of the logic surrounding it: conversions, validations, computed properties, and access control. You extend Backbone.Model with your domain-specific methods, and Model provides a basic set of functionality for managing changes.

So without wasting much time, let's create a User Model for our Backbone.

var userModel = Backbone.Model.extend({
        defaults: {
            Id: null,
            FirstName: null,
            LastName: null,
            Age: null,
            Email: null,
            Phone: null
        },
        initialize: function () {
           // Do stuff's which you want at the time of model creation
        }
    });

 

Creating Collection

What the official's say:

Collections are ordered sets of models. You can bind "change" events to be notified when any model in the collection has been modified, listen for "add" and "remove" events, fetch the collection from the server, and use a full suite of Underscore.js methods.

Any event that is triggered on a model in a collection will also be triggered on the collection directly, for convenience. This allows you to listen for changes to specific attributes in any model in a collection, for example: documents.on("change:selected", ...)

Backbone collection are collection of model's, no doubt it has much more in it. It has more or less than 28  Underscore methods which support it and allows you keep a watch on them and do necessary operations when something gets modified or changed. Along with that, its supports RESTful API's . Awesome!! 

var listOfUsers = Backbone.Collection.extend({
    model: userModel,
    url: '/api/User'
});

Simple and sweet code. We provide our our previously created model and default API which is ready to return List<User> .  you could even try and hardcode you data in case your feeling lazy to create a backend. All is fine.

Creating View

What the official's say:

Backbone views are almost more convention than they are code — they don't determine anything about your HTML or CSS for you, and can be used with any JavaScript templating library. The general idea is to organize your interface into logical views, backed by models, each of which can be updated independently when the model changes, without having to redraw the page. Instead of digging into a JSON object, looking up an element in the DOM, and updating the HTML by hand, you can bind your view's render function to the model's "change" event — and now everywhere that model data is displayed in the UI, it is always immediately up to date.

Sound's good and informative. More details.

Before we jump for creting a view, lets first decide our el element. The "el" property references the DOM object created in the browser. Every Backbone.js view has an "el" property, and if it not defined, Backbone.js will construct its own, which is an empty div element.

Goto the page where you want to render your user listing page and add a div tag specifying the class name or id. In my case I have removed the Home/Index.cshtml code and added the below line:

@{
    ViewBag.Title = "Home Page";
}

<div class="user-management"></div>

We are done setting up the element. Let's come back to creating a view which will render the data.

var listOfUsersView = Backbone.View.extend({
    el: '.user-management',// The element we defined in HTML
    render: function () {
        var self = this;// Saving the scope object
        var _userList = new listOfUsers();
        _userList.fetch({
            success: function (data) {
                    
                var _userTemplate = _.template($('#user-list-template').html(), { users: data.models });
                self.$el.html(_userTemplate);
            }
        });
    }
});

What I am doing is, I have created a Backbone View having an 'el' property which would hold the reference to the DOM object created. Secondly, the "render()" function will load our template into the view's "el" property using jQuery.  This method is responsible to fetch the data and create a template with the data fetched from server(render method should not have the logic for fetching data from server, we have ways to refactor it, as of now we can live with that). Now the question comes what is:

_.template($('#user-list-template').html(), { users: data.models });

This is Backbone.js is dependent on Underscore.js, which includes its own templating solution. Compiles JavaScript templates into functions that can be evaluated for rendering. Useful for rendering complicated bits of HTML from JSON data sources. Read more about undersore templates.

Creating Template

Now creating a template would be straight forward, if you have worked with Knockout or Angular then you would be able to relate it well. In case you have not... No worries... Have a look at the template:

<script type="text/template" id="user-list-template">
   <h3>Following are the user's:</h3>
    <form class="list-group-form">
        <a href="/#create" class="btn btn-default ">Create User</a>
        <table class="table striped">
            <thead>
                <tr>
                    <th></th>
                    <th>Id</th>
                    <th>FirstName</th>
                    <th>LastName</th>
                    <th>Age</th>
                    <th>Actions</th>
                </tr>
            </thead>
            <% _.each(users,function(user){%>
                <tr>
                    <td>
                        <input type="checkbox" /></td>
                    <td><%= user.get('Id')%></td>
                    <td><%= user.get('FirstName')%></td>
                    <td><%= user.get('LastName')%></td>
                    <td><%= user.get('Age')%></td>
                    <td>
                        <a href="/#edit/<%= user.get('Id')%>" class="btn btn-primary">Edit</a></td>
                </tr>
            <%});%>
        </table>
    </form>
</script>

The underscore template's is a <script> tag with type as "text/template", which actually prevents the browser from rendering it at first shot. It is not a script that the browser can understand, and so the browser will simply ignore it. You can put anything here which can be used as a template. You can use many other libraries like Handlebar/Mustache etc.

<script type="text/template" id="user-list-template">

</script>

We are almost set with creating a User Model, then creating a User Collection of it. Then we have created a view which would create an object of User Collecton and fire a fetch request. When the API return's the data, we are getting our template and binding the data to it. Finally, display it.

Lastly, we need to call our render method from the /# route(our default route). So let jump back a little where we had:

// When hash tag has localhost# register the below route
route.on('route:home', function () {
        
    // Todo: code to render create page
    console.log("Display Create Page");
});

Now, what we need to do is , calling our view's render method.

// When hash tag has localhost# register the below route
route.on('route:home', function () {
    var objList = new listOfUsersView();
    objList.render();
});

Run your application to check if throws any error in console. If all is fine the you should be able to see the list of user's from the database.

Note: In case you are not then open the network tab and check the response you'r getting from the api. Check the fields which the api is returning and match the properties with what is defined in the template.

 

Creation of Users

Now, when we are all set to display list of user's then lets actually create a user and update the database. Come back to the listing application and check whether user got created or now.

When you click on the link Create User, you would see the /#create getting appended tho the url and console would give you back "Display Create Page". So we need to start writing our code there. Before that we need a Create View which would be responsible to display our user model.

Creating the template and basic view

Let's start with creating our user view. We would be using the same view for Create and Update.

var specificUserView = Backbone.View.extend({
        el: '.user-management',// The element we defined in HTML
        render: function() {
            
        }
    });

Okay. Now what!!

Let's not jump straight away to form fields , what I would love to do is create one more template with simpe text written. We would display that when user clicks on Create User hyperlink.

<script type="text/template" id="user-detail-template">
    <h3>User Details:</h3>
    @* Todo: Add the form tag and more *@
</script>

I would love to render the template and see what happens when I click on Create User Hyperlink.

   var specificUserView = Backbone.View.extend({
        el: '.user-management',// The element we defined in HTML
        render: function() {
            var _userDetailTemplate = _.template($('#user-detail-template').html());
            this.$el.html(_userDetailTemplate);
        }
   });

Now, over here I am simply rendering the template and not doing anything else. Call this view when url changes to /#Create. So for that we go back to the place where we have defined our create route and its time we call the render method of specificUserView.

// When hash tag has localhost#create register the below route
route.on('route:create', function () {
    var _objUser = new specificUserView();
    _objUser.render();
});

Run your application to check if throws any error in console. If all is fine the you should be able to see our text being printed. Lets add our form elements ready and create our form which would accept the required properties. When we add our input fields the form would look quiet similar to:

<script type="text/template" id="user-detail-template">
    <h3>User Details</h3>
    <table>
        <tr>
            <td>First Name</td>
            <td>:</td>
            <td>
                <input type="text" class="first-name" /></td>
        </tr>
        <tr>
            <td>Last Name</td>
            <td>:</td>
            <td>
                <input type="text" class="last-name" /></td>
        </tr>
        <tr>
            <td>Age</td>
            <td>:</td>
            <td>
                <input type="text" class="age" /></td>
        </tr>
    </table>

    <input type="button" value="Save" id="saveUserDetails"/>
</script>

Creating Save Event

Every Backbone View allow us to define event's. For e.g.:

var DocumentView = Backbone.View.extend({
    events: {
        "dblclick": "open",
        "click .icon.doc": "select"
    }
});

delegateEvents takes the events: { ... } declaration for your view instance, and binds the specified events to the specified DOM elements, with the specified callback methods to handle the events.

We need to have a submit button click. So we create a event for this in our

specificUserView 

as below:

 

var specificUserView = Backbone.View.extend({
    el: '.user-management',// The element we defined in HTML
    events: {
        'click #saveUserDetails': 'saveUserDetails'
    },
    render: function() {
        var _userDetailTemplate = _.template($('#user-detail-template').html());
        this.$el.html(_userDetailTemplate);
    },
    saveUserDetails: function() {
        console.log("Save user details");
    }
});

Run your application to check if throws any error in console. Click on the Save button to check if the console message is reflecting in F12 console window.

Now, we need work on saveUserDetails method. This method is responsible to create a model object with current filled properties and saving the details to the server.

I am trying to keep it simple as:

saveUserDetails: function () {

    // Create a user model to fill the form details
    var model = new userModel({
        id: null,
        FirstName: $('.first-name').val(),
        LastName: $('.last-name').val(),
        Age: $('.age').val()
    });

    model.save({}, {
        success: function () {
            console.log('Data Saved');
            route.navigate('', { trigger: true });// Navigate back to listing page
        }
    });

}

 

Editing a user

Now, here come's the maximum challenge. We would be using our create form to display the edit information also. For that we need to make some changes to the code.

Routing

//When hash tag has localhost# register the below route
route.on('route:edit', function (userId) {
    var _objUserEdit = new specificUserView();
    _objUserEdit.render(userId);
});

Our routing would accept Id as parameter and it will be passed to the render method.

View & Template

Now, we need to change our view to accept our User Id and fetch data with respect to the User Id. Also, we need to update our view which would display value when provided. So, for that I changed my specific user View var specificUserView as :

render: function (userId) {
        var userDetails=null;
        if (userId) {
            var self = this;
             
            // User update. We need to fetch user details from server and
            // render the template.
            var _userModel = new userModel({ id: userId });
           
            _userModel.fetch({
                data: {id:userId},
                success: function (data) {
                    var _userDetailTemplate = _.template($('#user-detail-template').html(),
                        { user: data });
                    self.$el.html(_userDetailTemplate);
                }
            });
        }
        else
        {
            // User is created
            var _userDetailTemplate = _.template($('#user-detail-template').html(),
                { user: null });
            this.$el.html(_userDetailTemplate);
        }

    }

Similarly, we bind our HTML with the model data.

<tr>
    <td>First Name</td>
    <td>:</td>
    <td>
        <input type="text" class="first-name" value="<%= user ? user.get('FirstName') : '' %>"/></td>
</tr>
<tr>
    <td>Last Name</td>
    <td>:</td>
    <td>
        <input type="text" class="last-name" value="<%= user ? user.get('LastName') : '' %>"/></td>
</tr>
<tr>
    <td>Age</td>
    <td>:</td>
    <td>
        <input type="text" class="age" value="<%= user ? user.get('Age') : '' %>"/></td>
</tr>

So, if you can see now all the input elements are binded with the value :

user.get('FirstName')

It is not esential to keep your template file like the way it has been done above, you have alternatives like handlebar.js and more which would allow you to create template in different file. Compile it prehand or at run time. More Details about Handlebar's.

Areas of Refactoring

The above code is very raw version of using Backbone.js. We would need refactoring on the code mentioned above. Like when you save a user we should navigate back to the listing page to display list of user's. e.g.

route.navigate('', { trigger: true });

Secondly, one important thing would be Destroying the View,

kill: function() {

    // COMPLETELY UNBIND THE VIEW
    this.undelegateEvents();

    this.$el.removeData().unbind(); 

    // Remove view from DOM
    this.remove();  
    Backbone.View.prototype.remove.call(this);

}

Would try to write more on memory leak.

Feedback and Comments for Improvement

Being my first post, I heartly welcome your suggestions for improvement and mistakes. I may have lost patience composing this page, so please feel free for criticism.

License

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

About the Author

Shubh Dasgupta
Software Developer (Senior)
India India
No Biography provided

Comments and Discussions

 
BugImage missing PinprofessionalDebabrata_Das3-Jul-14 2:54 
GeneralRe: Image missing [modified] PinmemberShubh Dasgupta3-Jul-14 2:58 
GeneralRe: Image missing PinprofessionalDebabrata_Das3-Jul-14 3:11 
GeneralRe: Image missing PinmemberShubh Dasgupta3-Jul-14 3:16 
GeneralRe: Image missing PinprofessionalDebabrata_Das3-Jul-14 5:13 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140718.1 | Last Updated 3 Jul 2014
Article Copyright 2014 by Shubh Dasgupta
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid