Introduction

In this article we will create a Contact List with edit, delete and add event features in backbone.js. Here will create the four events "button.edit" , "button.Save", "button.cancle" in the contact View.

"click button.delete": "deletePerson",

"click button.edit": "editPerson",

"change select.type": "addType",

"click button.save": "saveEdits",

"click button.cancel": "cancelEdit"

Use the following procedure to create the application.

Step 1

Add Web Application

Step 2

Now add an HTML page to the project.

Add HTML Page

Change Name

Add the following code:

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8" />

<title>Backbone.js Web App</title>

<link href="style.css" rel="stylesheet" />

</head>

<body>

<div id="persons">

<header>

<div id="filter"><label>Show me:</label></div>

<a id="showForm" href="#">Add new Person</a>

<form id="addPerson" action="#">

<label for="name">Name:</label><input id="name" /><br />

<label for="type">Type:</label><input id="type" /><br />

<label for="address">Address:</label><input id="address" /><br />

<label for="contact">Contact:</label><input id="contact" /><br />

<label for="email">Email_Id:</label><input id="email" /><br />

<button id="add">Add</button>

</form>

</header>

</div>

<script id="personTemplate" type="text/template">

<img src="<%= photo %>" />

<h1><%= name %><span><%= type %></span></h1>

<div><%= address %></div>

<dl>

<dt>Contact:</dt>

<dd><%= contact %></dd>

<dt>Email:</dt>

<dd><a href="mailto:<%= email %>"><%= email %></a></dd>

</dl>

<button class="delete">Delete</button>

<button class="edit">Edit</button>

</script>

Now adding the button for existing template, We can also add an new template that can be used as an editable form.

<script id="personEditTemplate" type="text/template">

<form action="#">

<input class="name" value="<%= name %>" />

<input id="type" type="hidden" value="<%= type %>" />

<input class="address" value="<%= address %>" />

<input class="contact" value="<%= contact %>" />

<input class="email" value="<%= email %>" />

<button class="save">Save</button>

<button class="cancel">Cancel</button>

</form>

</script>

Now we need to add these scripts file.

<script src="js/jquery-1.7.1.min.js"></script>

<script src="js/json2.js"></script>

<script src="js/underscore-min.js"></script>

<script src="js/backbone-min.js"></script>

<script src="js/main.js"></script>

</body>

</html>

Step 3

Now we add a JavaScript file as in the following:

Add JavaScript file

Add the following code:

(function ($) {

//demo data

var persons = [

{ name: "Person 1", address: "Address 1", contact: "0123456789", email: "[email protected]", type: "family" },

{ name: "Person 2", address: "Address 2", contact: "4536782947", email: "[email protected]", type: "family" },

{ name: "Person 3", address: "Address 3", contact: "5647839244", email: "[email protected]", type: "Friend" },

{ name: "Person 4", address: "Address 4", contact: "43890128743", email: "[email protected]", type: "Relative" },

{ name: "Person 5", address: "Address 5", contact: "3456789012", email: "[email protected]", type: "Relative" },

];

//define model

var Person = Backbone.Model.extend({

defaults: {

photo: '/img/winter.jpg',

name: "",

address: "",

contact: "",

email: "",

type: ""

}

});

//define directory collection

var Directory = Backbone.Collection.extend({

model: Person

});

//define person view

var PersonView = Backbone.View.extend({

tagName: "article",

className: "person-container",

template: _.template($("#personTemplate").html()),

editTemplate: _.template($("#personEditTemplate").html()),

render: function () {

this.$el.html(this.template(this.model.toJSON()));

return this;

},

events: {

"click button.delete": "deletePerson",

"click button.edit": "editPerson",

"change select.type": "addType",

"click button.save": "saveEdits",

"click button.cancel": "cancelEdit"

},

//delete a contact

deletePerson: function () {

var removedType = this.model.get("type").toLowerCase();

//remove model

this.model.destroy();

//remove view from page

this.remove();

//re-render select if no more of deleted type

if (_.indexOf(directory.getTypes(), removedType) === -1) {

directory.$el.find("#filter select").children("[value='" + removedType + "']").remove();

}

},

//switch person edit mode

editPerson: function () {

this.$el.html(this.editTemplate(this.model.toJSON()));

//add select to set type

var newOpt = $("<option/>", {

html: "<em>Add new...</em>",

value: "addType"

});

this.select = directory.createSelect().addClass("type").val(this.$el.find("#type").val()).append(newOpt).insertAfter(this.$el.find(".name"));

this.$el.find("input[type='hidden']").remove();

},

addType: function () {

if (this.select.val() === "addType") {

this.select.remove();

$("<input />", {

"class": "type"

}).insertAfter(this.$el.find(".name")).focus();

}

},

saveEdits: function (e) {

e.preventDefault();

var formData = {},

prev = this.model.previousAttributes();

//get form data

$(e.target).closest("form").find(":input").not("button").each(function () {

var el = $(this);

formData[el.attr("class")] = el.val();

});

//use default photo if none supplied

if (formData.photo === "") {

delete formData.photo;

}

//update model

this.model.set(formData);

//render view

this.render();

//if model acquired default photo property, remove it

if (prev.photo === "/img/placeholder.png") {

delete prev.photo;

}

//update persons

_.each(persons, function (person) {

if (_.isEqual(person, prev)) {

persons.splice(_.indexOf(persons, person), 1, formData);

}

});

},

cancelEdit: function () {

this.render();

}

});

//define master view

var DirectoryView = Backbone.View.extend({

el: $("#persons"),

initialize: function () {

this.collection = new Directory(persons);

this.render();

this.$el.find("#filter").append(this.createSelect());

this.on("change:filterType", this.filterByType, this);

this.collection.on("reset", this.render, this);

this.collection.on("add", this.renderPerson, this);

this.collection.on("remove", this.removePerson, this);

},

render: function () {

this.$el.find("article").remove();

_.each(this.collection.models, function (item) {

this.renderPerson(item);

}, this);

},

renderPerson: function (item) {

var personView = new PersonView({

model: item

});

this.$el.append(personView.render().el);

},

getTypes: function () {

return _.uniq(this.collection.pluck("type"), false, function (type) {

return type.toLowerCase();

});

},

createSelect: function () {

var filter = this.$el.find("#filter"),

select = $("<select/>", {

html: "<option value='all'>All</option>"

});

_.each(this.getTypes(), function (item) {

var option = $("<option/>", {

value: item.toLowerCase(),

text: item.toLowerCase()

}).appendTo(select);

});

return select;

},

//add ui events

events: {

"change #filter select": "setFilter",

"click #add": "addPerson",

"click #showForm": "showForm"

},

//Set filter property and fire change event

setFilter: function (e) {

this.filterType = e.currentTarget.value;

this.trigger("change:filterType");

},

//filter the view

filterByType: function () {

if (this.filterType === "all") {

this.collection.reset(persons);

personsRouter.navigate("filter/all");

} else {

this.collection.reset(persons, { silent: true });

var filterType = this.filterType,

filtered = _.filter(this.collection.models, function (item) {

return item.get("type").toLowerCase() === filterType;

});

this.collection.reset(filtered);

personsRouter.navigate("filter/" + filterType);

}

},

//add a new person

addPerson: function (e) {

e.preventDefault();

var formData = {};

$("#addPerson").children("input").each(function (i, el) {

if ($(el).val() !== "") {

formData[el.id] = $(el).val();

}

});

//update data store

persons.push(formData);

//re-render select if new type is unknown

if (_.indexOf(this.getTypes(), formData.type) === -1) {

this.collection.add(new Person(formData));

this.$el.find("#filter").find("select").remove().end().append(this.createSelect());

} else {

this.collection.add(new Person(formData));

}

},

removePerson: function (removedModel) {

var removed = removedModel.attributes;

//if model acquired default photo property, remove it

if (removed.photo === "/img/placeholder.png") {

delete removed.photo;

}

//remove from contacts array

_.each(persons, function (person) {

if (_.isEqual(person, removed)) {

persons.splice(_.indexOf(persons, person), 1);

}

});

},

showForm: function () {

this.$el.find("#addPerson").slideToggle();

}

});

//add routing

var PersonsRouter = Backbone.Router.extend({

routes: {

"filter/:type": "urlFilter"

},

urlFilter: function (type) {

directory.filterType = type;

directory.trigger("change:filterType");

}

});

//create instance of master view

var directory = new DirectoryView();

//create router instance

var personsRouter = new PersonsRouter();

//start history service

Backbone.history.start();

}(jQuery));

Step 4

Now we add a Style Sheet:

Add StyleSheet

Add the following code:

#persons {width:1300px; margin:auto; }

.person-container { width:400px; padding:10px; border:1px solid #aaa; margin:0 10px 10px 0; position:relative; float:left; font-family:sans-serif; color:#333; background-color:#eee; }

.person-container h1 { margin:0; font-weight:normal; }

.person-container h1 span { float:right; font-size:14px; line-height:24px; font-weight:normal; }

.person-container img { border-width:1px; border-style:solid; border-color:#fff; border-right-color:#aaa; border-bottom-color:#aaa; margin-right:10px; float:left; height:50px ;width:50px;}

.person-container div { margin-bottom:24px; font-size:14px; }

.person-container a { color:#333;}

.person-container dl { margin:0; float:left; font-size:14px; }

.person-container dt, .person-container dd { margin:0; float:left; }

.person-container dt { width:50px; clear:left; }

.person-container button { margin-top:10px; float:right; }

header { margin-bottom:10px; }

header:after { content:""; display:block; height:0; visibility:hidden; clear:both; font-size:0; line-height:0; }

#filter { float:left; }

#showForm { float:right; }

#addPerson { display:none; width:466px; float:right; clear:both; font-family:sans-serif; font-size:14px; }

#addPerson label { width:60px; margin-right:10px; text-align:right; line-height:25px; }

#addPerson label, #addPerson input { display:block; margin-bottom:10px; float:left; }

#address { width:380px; margin-left:2px; }

#addPerson label[for="name"], #addPerson label[for="address"], #addPerson label[for="contact"] { clear:both; }

#addPerson button { display:block; margin:10px 10px 0 0; float:right; clear:both; }

.person-container input, .person-container select { display:block; margin:0; float:left; }

.person-container .name, .person-container .address { clear:left; }

.person-container input { margin:0 10px 3px 0; }

.person-container .address { width:395px; margin-right:0; }

.person-container .contact { width:90px; }

.person-container form button { margin:5px 0 0; }

Step 5

Now execute the application.

We can see the output like this. There is a display of the record for the person with edit and save buttons and an Add new Data link is displayed.

Display record

Add new Person

Edit Record