I have my code, its a basic TODO list which just has the below lines of code.
The issue I am facing is that almost everything is crowded in the view itself. There is hardly any lines of code in the model or the collection, and also, there no controller.
This is negating the MVC in backbone, which i am trying to implement. Is there any way in which I can remove the code from the view and place it into any of the other modules?
(function($){
Backbone.sync = function(method, model, success, error){
success();
}
var Item = Backbone.Model.extend({});
var List = Backbone.Collection.extend({
model: Item
});
var ItemView = Backbone.View.extend({
tagName: 'div', // name of tag to be created
events: {
'click span.delete': 'remove'
},
initialize: function(){
_.bindAll(this, 'render', 'unrender', 'remove'); // every function that uses 'this' as the current object should be in here
this.model.bind('change', this.render);
this.model.bind('remove', this.unrender);
},
render: function(){
$(this.el).html("<span style='margin-left: 20px;'></span>"+this.model.get('part')+'<span class="delete" style="cursor:pointer; color:red; font-family:sans-serif;">[delete]</span>');
return this; // for chainable calls, like .render().el
},
unrender: function(){
this.model.destroy();
}
});
var ListView = Backbone.View.extend({
el: $('body'), // attaches `this.el` to an existing element.
events: {
'click button#add': 'addItem'
},
initialize: function(){
_.bindAll(this, 'render', 'addItem'); // fixes loss of context for 'this' within methods
this.collection = new List();
this.collection.bind('add', this.appendItem); // collection event binder
this.render(); // not all views are self-rendering. This one is.
},
render: function(){
var self = this;
$(this.el).append("<body style='margin: 0; padding: 0;' id='body'><div style='margin: 20px;'><h1>TODO list in plain JS and JQuery</h1><input type='text' id='taskBox' autofocus/>");
$(this.el).append("<span id='appendToSpan'></span>");
$(this.el).append("<button style='margin-left: 20px;' id='add'>Add list item</button>");
_(this.collection.models).each(function(item){ // in case collection is not empty
self.appendItem(item);
}, this);
},
addItem: function(){
var item = new Item();
var val = $(this.el).find('input#taskBox').val();
$(this.el).find('input#taskBox').val('');
//this.model.set({'part': val});
item.set({'part': val});
this.collection.add(item);
},
appendItem: function(item){
var itemView = new ItemView({
model: item
});
$('span#appendToSpan', this.el).append(itemView.render().el);
}
});
var listView = new ListView();
})(jQuery);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>TODO App</title>
</head>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>
<script src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
<script src="http://documentcloud.github.io/backbone/backbone-min.js"></script>
<script src="views/demo1View.js" type="text/javascript"></script>
</body>
</html>
Backbonejs is not an MVC Framework. It's rather more MVP ( P - presenter) framework. In other words you could reside your logic into the View that plays role of Controller in true MVC frameworks.
Just try to separate responsibility of components and keep it as simple as possible.
I would recommend the following refactoring of your code:
(function($){
Backbone.sync = function(method, model, success, error){
success();
}
var Item = Backbone.Model.extend({});
var List = Backbone.Collection.extend({
model: Item
});
var ItemView = Backbone.View.extend({
events: {
'click .js-delete': 'remove'
},
template: _.template($('#itemViewTemplate').html()),
initialize: function(){
this.listenTo(this.model, 'change', this.render);
this.listenTo(this.model, 'remove', this.unrender);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
unrender: function(){
this.$el.remove();
}
});
var ListView = Backbone.View.extend({
events: {
'click .js-addItem': 'addItem'
},
initialize: function(){
this.listenTo(this.collection, 'add', this.appendItem);
},
render: function(){
this.collection.each(this.appendItem, this);
return this;
},
addItem: function(){
var $input = this.$('input#taskBox')
var val = $input.val();
this.collection.add({part: val});
// clear input
$input.val('');
},
appendItem: function(item){
var itemView = new ItemView({
model: item,
template: this.template
});
this.$('#appendToSpan').append(itemView.render().el);
}
});
// Initialize widget and empty collection
var listView = new ListView({
el: 'body',
collection: new List()
});
// Render collection
listView.render();
})(jQuery);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>TODO App</title>
<style>
body {
margin: 0;
padding: 0;
}
.container {
margin: 20px;
}
.addButton {
margin-left: 20px;
margin-top: 10px;
}
.item-name {
margin-left: 20px;
}
.item-remove {
cursor:pointer;
color:red;
font-family:sans-serif;
}
</style>
</head>
<body>
<div class="container">
<h1>TODO list in plain JS and JQuery</h1>
<input type='text' id='taskBox' autofocus/>
<div id='appendToSpan'></div>
<button class="addButton js-addItem">Add list item</button>
<!-- Item template -->
<script type="text/template" id="itemViewTemplate">
<span class="item-name"><%= part %></span>
<span class="js-delete item-remove">[delete]</span>
</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>
<script src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
<script src="http://documentcloud.github.io/backbone/backbone-min.js"></script>
</body>
</html>