Introduction
This tutorial is about creating a Single Page Application
using Backbone and performing database CRUD (Create, Read, Update and Delete)
operations using ASP.NET Web API as a RESTful service.
Contents list of this article
- Short description of Single Page Application.
- Short description of Backbone.
- Short description of ASP.NET Web API.
- Sample Single Page Application using Backbone
and Web API.
Section-1: Short description of Single Page Application.
Single Page Application became more popular now
a day. It gives an impression like a Desktop Application. Basically Single Page
Application uses AJAX to communicate with the server, so it takes less time to
update a part of the web page without refreshing the whole page and gives
impression of a Desktop Application.
Section-2: Short description of Backbone.
Backbone is a JavaScript library with APIs to
build a Single Page Application. It provides structure to application with
built in JavaScript Classes Model, View, Collection, Router and Event.
Section-3: Short description of ASP.NET Web API.
In short ASP.NET Web API is a framework developed
by Microsoft and supplied with Visual Studio 2012. It provides all the
functionality of Restful web service specially response on HTTP request call
using GET, POST, PUT and DELETE and reply JSON serialize data.
Section-4: Sample Single Page Application using Backbone and Web API.
Almost every application performs some database
operations to show or create, update or delete data from database. This sample
application shows how to perform CRUD (Create, Read, Update and Delete)
operations from Single Page Application using Backbone and ASP.NET Web API.
This sample application shows a list of Tournaments and can add, edit or delete
a Tournament.
Step 1: First create a Web Application project
using Web API Template shows in the following solution explorer.
Description: This application contains only one
HTML page tournaments.html and every operation is performed in this page. The
Controllers folder contains the Controller classes. Here I am using only one
controller TournamentController to extract, add, edit and delete a tournament.
Other folder contains the images and necessary JavaScript files for Backbone,
Underscore and jQuery. Here BackboneDB is the ADO.NET Entity framework used as
a Data Access Layer.
Step 2: Next create a database containing a
table Tournament. The table structure is shown below.
Step 3: Next create a HTML page
tournaments.html.
This page is the only Single Page which contains
all the html and JavaScript. Single Page Application using Backbone the entire
HTML is generated by Backbone View class. The initial contents of the page body
are shown below.
<body>
<div id="page">
<div id="tournamentAddContainer"></div>
<div id="tournamentlistContainer"></div>
</div>
<div class="loading"><img class="loadingImage" src="/Images/loading.gif" alt="Loading" /></div>
</body>
As we know View object generates pages html. Model
contains application data and is bounded with View to show the information.
Collection object contains a list of same type of Model object and Router
object changes the browser history.
The custom Views are created for this
application is shown below.
var tournamentAddView = Backbone.View.extend({
events: {
'click #btnAddTournament': "addtournament"
},
addtournament: function () {
var tournamentName = this.$el.find("#txtName").val();
var tournament= new tournamentModel({ Name: tournamentName });
tournament.save({}, {
success: function () {
tableVeiw.collection.add(tournament);
},
error: function () { alert('add error'); }
});
},
render: function () {
var templateTemp = $("#tempTournamentAdd").html();
this.$el.html(_.template(templateTemp, { Name: this.model.get('Name') }));
return this;
}
});
Here tournamentAddView
is created by extended the Backbone built in View Class. This View is used to
render an Underscore Template which contains the necessary HTML to add a
Tournament. The Underscore Template is shown below.
<script type="text/html" id="tempTournamentAdd">
<table>
<tr>
<td> <input type="text" id="txtName" value="<%= Name %>"/> </td>
<td> <input type="submit" id="btnAddTournament" value="Add"/> </td>
</tr>
</table>
</script>
Here "<%= Name %>" syntax shows the Underscore code
block and this statement render the value of Name property of the bounded data
Model.
The custom View to render a
table to the browser is shown below
var CustomTableView = Backbone.View.extend({
tagName: 'table',
attributes: { class: "tables" },
initialize: function () {
this.collection.on('add', this.addCollection);
tableviewref = this;
this.$el.html('<tr><th>Tournament Name</th><th></th><th></th></tr>');
},
render: function () {
_(this.collection.models).each(function (tour) {
this.$el.append(new CustomRowView({ model: tour }).render().el);
}, this);
return this;
},
addCollection: function (model, collection) {
tableviewref.$el.append(new CustomRowView({ model: model }).render().el);
}
});
Here tagName property
indicates what kind of HTML element this View is rendering. This View returns a
table. Following list describes few of its properties.
Property
|
Description
|
tagName
|
The
type of HTML element the View will render.
|
attributes
|
Takes
a JavaScript object to set the attributes of the HTML element.
|
initialize
|
This
property contains a reference of a function which is executed when a View is
instantiated.
|
render
|
This
function renders the content of this View.
|
addCollection
|
It
is a callback function and is called when a Model is added in the collection
of the View, which means a row is added to the table.
|
The custom View to render a row into the table
is shown below
var CustomRowView = Backbone.View.extend({
tagName: 'tr',
attributes: { class: "rows" },
initialize: function () {
this.model.on('change', this.render, this);
},
events: {
'click #btnDeleteBook': "deleteTournament",
'click #btnEditBook': "editTournament",
'click #btnSave': "updateTournament",
'click #btnCancel': "cancelTournament"
},
deleteTournament: function () {
var self = this;
this.model.destroy({
success: function () {
tableVeiw.collection.remove(self.model);
self.remove();
self.render();
},
error: function () { alert('delete error'); }
});
},
editTournament: function () {
var templateTemp = $("#tempTournamentEdit").html();
this.$el.html(_.template(templateTemp, { Name: this.model.get('Name') }));
},
updateTournament: function () {
var tournamentNameEdit = this.$el.find("#txtNameEdit").val();
if (tournamentNameEdit != "") {
this.model.save({ Name: tournamentNameEdit }, {
success: function () {
var templateTemp = $("#tempTournamentView").html();
this.$el.html(_.template(templateTemp, { Name: this.model.get('Name') }));
},
error: function () { alert('update error'); }
});
}
},
cancelTournament: function () {
var templateTemp = $("#tempTournamentView").html();
this.$el.html(_.template(templateTemp, { Name: this.model.get('Name') }));
},
render: function () {
var templateTemp = $("#tempTournamentView").html();
this.$el.html(_.template(templateTemp, { Name: this.model.get('Name') }));
return this;
}
});
Description of some of the properties
are shown below
Attributes
|
Description
|
tagName
|
The
type of HTML element the View will render.
|
attributes
|
Takes
a JavaScript object to set the attributes of the HTML element.
|
events
|
Define the events and bind event handler with it.
|
deleteTournament
|
Execute this function when delete event is fired.
|
editTournament
|
Execute this function when edit event is fired.
|
saveTournament
|
Execute this function when save event is fired.
|
cancelTournament
|
Execute this function when cancel the update.
|
The Underscore Template to render a row is shown
below
<script type="text/html" id="tempTournamentView">
<td><%=Name></td>
<td class="tdInput"> <input type="submit" id="btnEditBook" value=""/> </td>
<td class="tdInput"> <input type="submit" id="btnDeleteBook" value=""/> </td>
</script>
The Underscore Template used to render HTML when
edit button is clicked shown below
<script type="text/html" id="tempTournamentEdit">
<td> <input type="text" id="txtNameEdit" value="<%= Name %>"/> </td>
<td class="tdInput"> <input type="submit" id="btnSave" value=""/> </td>
<td class="tdInput"> <input type="submit" id="btnCancel" value=""/> </td>
</script>
The custom Model use to show every row in the
list is shown below
var tournamentModel = Backbone.Model.extend({
idAttribute: 'Id',
urlRoot: '/api/Tournament',
defaults: {
Name: 'default',
Address: 'default'
}
});
Description of some of the properties
are shown below
Attributes
|
Description
|
idAttribute
|
This
property is use to assign a custom name of Model id.
|
urlRoot
|
This
is the server URL to add, edit and delete Tournament from database.
|
defaults
|
Default
value assigned to the new Model object.
|
The custom Collection type is shown below
var CustomCollection = Backbone.Collection.extend({
model: tournamentModel,
url: '/api/Tournament'
});
Description of some of the properties
are shown below
Attributes
|
Description
|
model
|
The
type of the Model this Collection is containing.
|
url
|
This
is the server URL to fetch the Tournament list.
|
The custom Backbone Router type is shown below
var CustomRouter = Backbone.Router.extend({
routes: {
'tournaments': 'tournaments'
},
tournaments: function () {
$('#tournamentlistContainer').html(tableVeiw.render().el);
}
});
Description of some of the properties
are shown below
Attributes
|
Description
|
routes
|
It
contains a list of navigation URL and its triggered function when navigation
occurs.
|
tournaments
|
This
is a callback function and is called when navigation matches the route.
|
Step 4: When browser request a page for the
first time, because of rendering the whole page it may takes few seconds to
load the page. Here I am showing a loading animation when the page is loading
for the first time. The initial requested URL is http://localhost:5334/tournaments.html
and after load the page Backbone convert
it to http://localhost:5334/tournaments,
because of Backbone Routing .The page loading event is shown below.
The JavaScript code to show and hide the loading
effect is shown below
$(".loading").css({ display: 'inline'});
tournamentCollection.fetch({
success: function (bookResponse) {
$(".loading").css({ display: 'none' });
}
});
Before fetching start the animation shows and
after successful fetching it hides.
Step 5: The Tournament list page is shown below.
The JavaScript code to extract the list of
tournament from server is shown below
var tournamentCollection = new CustomCollection();
tournamentCollection.fetch({
success: function (bookResponse) {
Backbone.history.start({ pushState: true });
initialrouter.navigate('tournaments', { trigger: true });
},
silent: true
});
This JavaScript code first
creates a Collection object and then calls the Collection fetch function to get
the list of tournaments. The fetch function takes a JavaScript object as a
parameter and the object contains a success callback function and silent property
to true that means event will not fire.
When fetching the list from
server, Backbone sends a HTTP GET request to the URL 'http://servername/api/Tournament’ which is set in the url property of Collection object.
Server side code to reply to Backbone GET
request is shown below.
public List<Tournament> Get()
{
List<Tournament> tourList;
using (BackboneDBEntities entity = new BackboneDBEntities())
{
tourList = entity.Tournaments.ToList();
entity.SaveChanges();
}
return tourList;
}
When server gets a HTTP GET
request from client it executes the Controller’s Get function. This function
extract tournament list from database using ADO.NET Entity framework and send
the list to the client as a JSON serialize data.
Step 6: The Add tournament page is shown below.
When user clicks on the Add
button then Backbone saves a new tournament in the server database using AJAX
call request and adds the new tournament in the view without refreshing the
whole page.
JavaScript code to add a tournament is shown
below.
addtournament: function () {
var tournamentName = this.$el.find("#txtName").val();
var tournament= new tournamentModel({ Name: tournamentName });
tournament.save({}, {
success: function () {
tableVeiw.collection.add(tournament);
},
error: function () { alert('add error'); }
});
}
This function first read
the tournament name from textbox and then creates a new Model object and then
calls the Model’s save function to save the Model in the database. The URL that
Backbone use to save the Model is provided in the Model’s urlRoot property and the URL
is 'http://servername
/api/Tournament'.
Server side code to add a tournament in database
is shown below.
public Tournament Post(Tournament tour)
{
using (BackboneDBEntities entity = new BackboneDBEntities())
{
entity.Tournaments.Add(tour);
entity.SaveChanges();
}
return tour;
}
When server gets a HTTP
POST request from client it executes the Controller’s Post function. This
function receives a Tournament Entity as a parameter and adds the Tournament
Entity to the database using ADO.NET Entity framework and return the newly
created tournament to the client as a JSON object.
Step 7: The Edit Tournament page is shown below.
After click on edit button of a tournament in
the list, the View is replaced with a textbox and a save and a cancel button. Save
button click update the tournament and Cancel button click cancel the update.
JavaScript code is executed on edit button click
is shown below.
editTournament: function () {
var templateTemp = $("#tempTournamentEdit").html();
this.$el.html(_.template(templateTemp, { Name: this.model.get('Name') }));
}
This function replaces the View with a new
Underscore Template ‘ tempTournamentEdit’.
JavaScript code is executed on Save button click
is shown below.
updateTournament: function () {
var tournamentNameEdit = this.$el.find("#txtNameEdit").val();
if (tournamentNameEdit != "") {
this.model.save({ Name: tournamentNameEdit }, {
success: function () {
var templateTemp = $("#tempTournamentView").html();
this.$el.html(_.template(templateTemp, { Name: this.model.get('Name') }));
},
error: function () { alert('update error'); }
});
}
}
This function read the update value and calls
the Model’s save function with updated value as a parameter. The Backbone sends
a HTTP PUT request to the url 'http://servername
/api/Tournament/5'. Here 5 is the Id of the tournament in the database.
Server side code to update tournament is shown
below.
public void Put(int Id, Tournament tourpara)
{
using (BackboneDBEntities entity = new BackboneDBEntities())
{
Tournament tour= entity.Tournaments.First(t => t.Id == Id);
tour.Name = tourpara.Name;
entity.SaveChanges();
}
}
When server gets a HTTP PUT
request from client it executes the Controller’s Put function. This function
receives an Id number and a Tournament Entity as a parameter and updates the
database record using ADO.NET Entity framework.
JavaScript code to cancel the update is shown
below.
cancelTournament: function () {
var templateTemp = $("#tempTournamentView").html();
this.$el.html(_.template(templateTemp, { Name: this.model.get('Name') }));
}
This function is executed
when Cancel button is clicked. After Cancel button click Backbone cancel the
update and returns to the list View.
Step 8: The delete tournament page is shown
below.
After click on Delete button Backbone send a
HTTP DELETE request to the server to delete the tournament record from database
and after successful delete, Backbone delete the tournament from list view.
JavaScript code to delete a tournament is shown
below.
deleteTournament: function () {
var self = this;
this.model.destroy({
success: function () {
tableVeiw.collection.remove(self.model);
self.remove();
self.render();
},
error: function () { alert('delete error'); }
});
}
This function calls the
Model’s destroy function to delete the tournament from the database. Backbone sends
a HTTP DELETE request to the url 'http://servername /api/Tournament/93'. Here 93 is the Id of the tournament need to
delete from database.
Server side code to delete tournament is shown
below.
public void Delete(int Id)
{
using (BackboneDBEntities entity = new BackboneDBEntities())
{
Tournament tour = entity.Tournaments.First(t => t.Id == Id);
entity.Tournaments.Remove(tour);
entity.SaveChanges();
}
}
When server gets a HTTP DELETE
request from client it executes the Controller’s Delete function. This function
receives the Id number of the Tournament as a parameter and deletes the tournament
from database using ADO.NET Entity framework.
Conclusion
This is all about how to
develop a Single Page Application using Backbone and perform database CRUD
operation using ASP.NET Web API. For full codes please download the zip file
and run it using Visual Studio 2012.
My Published Articles
- http://www.codeproject.com/Articles/661878/Implementation-of-MVC-Patterns-in-ASP-NET-Web-form
- http://www.codeproject.com/Articles/674959/MVC-Patterns-Active-and-Passive-Model-and-its
- http://www.codeproject.com/Articles/691691/Apply-Here-Map-in-Windows-Phone-HTML-Apps
- http://www.codeproject.com/Articles/740703/Easy-way-to-learn-the-Asp-Net-Built-in-Membership