I have been trying to learn backbone.js and I thought the best way to learn it is by writing a dynamic table (i.e. add, edit and delete rows of data). I am very familiar with Node.js (back-end). Any comments or suggestions to improve this backbone.js code would be appreciated. This code works fine but I did not use any existing code. I wrote everything by myself so I am not sure whether I am on the right track or not.
$(function() {
var UserModel = Backbone.Model.extend({
"urlRoot": "/users",
"id": "id",
"defaults": function() {
// generate a random number and convert it to base 36
return {
"id": md5(Math.random().toString(36).substr(2)),
"name": "Name",
"age": 18,
"email": "[email protected]"
};
}
});
var UserView = Backbone.View.extend({
"templateView": _.template("\
<tr>\
<td>\
<%=user.name%>\
</td>\
<td>\
<%=user.age%>\
</td>\
<td>\
<%=user.email%>\
</td>\
<td>\
<botton class='btn btn-danger' data-action='delete' data-id=<%=user.id%>>delete</botton>\
<botton class='btn btn-primary' data-action='edit' data-id=<%=user.id%>>edit</botton>\
</td>\
<tr>\
"),
"templateEdit": _.template("\
<div class='modal fade' id='edit-modal' role='dialog'>\
<div class='modal-dialog'>\
<div class='modal-content'>\
<div class='modal-header'>\
<button type='button' class='close' data-dismiss='modal'>×</button>\
<h4 class='modal-title'><%=title%></h4>\
</div>\
<div class='modal-body'>\
<form role='form'>\
<div class='form-group'>\
<label>Name:</label>\
<input type='text' class='form-control' value=<%=user.name%> name='name'>\
</div>\
<div class='form-group'>\
<label>Age:</label>\
<input type='number' class='form-control'value=<%=user.age%> name='age'>\
</div>\
<div class='form-group'>\
<label>Email:</label>\
<input type='email' class='form-control' value=<%=user.email%> name='email'>\
</div>\
<button type='submit' class='btn btn-success'>Submit</button>\
</form>\
</div>\
<div class='modal-footer'>\
<button type='button' class='btn btn-default' data-dismiss='modal'>Close</button>\
</div>\
</div>\
</div>\
"),
"initialize": function(user) {
_.bindAll(this, "render");
this.render();
},
"renderView": function() {
this.setElement(this.templateView({
"user": this.model.toJSON()
}));
return this;
},
"renderEdit": function() {
this.setElement(this.templateEdit({
"user": this.model.toJSON(),
"title": "Edit user"
}));
return this;
},
"renderAdd": function() {
this.setElement(this.templateEdit({
"user": this.model.toJSON(),
"title": "Add user"
}));
return this;
}
})
var UsersCollection = Backbone.Collection.extend({
"url": "/users",
"model": UserModel
});
var UsersView = Backbone.View.extend({
"el": $("#users-table-body"),
"events": {
"click [data-action='delete']": "deleteUser",
"click [data-action='edit']": "editUser"
},
"initialize": function() {
_.bindAll(this, "render", "deleteUser", "editUser", "addUser");
var self = this;
$("#addUser").on("click", function() {
self.addUser();
});
this.collection = new UsersCollection();
this.collection.fetch({
"success": function(usersModels) {
self.render();
}
});
},
"render": function() {
this.$el.empty();
this.collection.each(function(user) {
var userView = new UserView({
"model": user
});
this.$el.append(userView.renderView().el);
}, this);
return this;
},
"deleteUser": function(event) {
var self = this;
var user = this.collection.get($(event.target).data("id").toString());
user.destroy({
"dataType": "text",
"success": function(model, response) {
self.collection.remove(user);
self.render();
}
});
},
"editUser": function(event) {
var self = this;
var user = this.collection.get($(event.target).data("id").toString());
var userView = new UserView({
"model": user
});
var modal = userView.renderEdit();
$("#user-edit").append(modal.el);
modal.$el.modal("show");
$("#user-edit").find("form").on("submit", function(event) {
event.preventDefault();
_.each($(this).find("input"), function(input) {
user.set($(input).attr("name"), $(input).val());
});
user.save(null, {
"dataType": "text",
"type": "PUT",
"success": function(data) {
// remove modal on success
modal.$el.modal("hide");
$("body").removeClass("modal-open");
$(".modal-backdrop").remove();
modal.$el.remove();
self.render();
}
});
});
},
"addUser": function() {
var self = this;
var user = new UserModel();
var userView = new UserView({
"model": user
});
var modal = userView.renderAdd();
$("#user-edit").append(modal.el);
modal.$el.modal("show");
$("#user-edit").find("form").on("submit", function(event) {
event.preventDefault();
_.each($(this).find("input"), function(input) {
user.set($(input).attr("name"), $(input).val());
});
user.save(null, {
"dataType": "text",
"type": "POST",
"success": function(data) {
// remove modal on success
modal.$el.modal("hide");
$("body").removeClass("modal-open");
$(".modal-backdrop").remove();
modal.$el.remove();
self.collection.add(user);
self.render();
}
});
});
}
});
Backbone.history.start({
"pushState": true
});
var usersView = new UsersView({
"collection": UsersCollection
});
});
1 Answer 1
As this question was asked nearly four years ago it is likely the case that you have learned more about JavaScript since then and/or changed this code. Nonetheless perhaps the info below will help you and/or somebody else.
It is interesting that The BackBoneJS documentation uses some valid JSON for specifying the keys of objects but not all. The keys in the original code don't all need to be double quoted - e.g.
var UserModel = Backbone.Model.extend({
"urlRoot": "/users",
"id": "id",
could simply be
var UserModel = Backbone.Model.extend({
urlRoot: "/users",
id: "id",
There is a lot of duplicate code in the addUser
and editUser
methods of UsersView
. Follow the Don't Repeat Yourself principle by abstracting the common code - perhaps into a method that handles the common functionality and can be called in both places. That way if you need to update something it can be done in one place instead of multiple.
In the UsersView
method initialize
I see the following:
var self = this;
That is an anti-pattern in Javascript. Instead of using that, use Function.bind()
to bind the context, or else use _.bind()
or _.bindAll()
as is used in other parts of the code.
The block after that line, i.e.
$("#addUser").on("click", function() {
self.addUser();
});
Can be simplified to
$("#addUser").on("click", this.addUser.bind(this));
This removes the excess function wrapper.
It can also be simplified using the jQuery shorthand method .click()
:
$("#addUser").click(this.addUser.bind(this));