Você está na página 1de 13

CRUD operation using Backbone.

js
Shubh Dasgupta, 3 Jul 2014

4.80 (4 votes)

Rate: vote 1vote 2vote 3vote 4vote 5


Learning backbone.js using basic CRUD operations
 Download UserManagement.zip - 5.2 MB

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:

Hide Copy Code


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.

Hide Copy Code


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
Hide Copy Code
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:

Hide Copy Code


http://localhost/#

We will be displaying list of users.

Hide Copy Code


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.

Hide Copy Code


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


Hide Copy Code
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.

Hide Copy Code


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!!

Hide Copy Code


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:

Hide Copy Code


@{
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.

Hide Copy Code


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:

Hide Copy Code


_.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:

Hide Shrink Copy Code


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

Hide Copy Code


<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:

Hide Copy Code


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

Hide Copy Code


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

Hide Copy Code


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.

Hide Copy Code


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

Hide Copy Code


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.

Hide Copy Code


// 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:

Hide Copy Code


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

Hide Copy Code


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

Hide Copy Code


specificUserView

as below:

Hide Copy Code


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:

Hide Copy Code


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
Hide Copy Code
//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 :

Hide Copy Code


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.

Hide Copy Code


<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 :

Hide Copy Code


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.

Hide Copy Code


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

Secondly, one important thing would be Destroying the View,

Hide Copy Code


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.

Você também pode gostar