backbone view render not creating - javascript

Just beginning with backbone and after few hours can't seem to get even a view render working correctly. I've included all appropriate JavaScript files in HTML. Here is my script:
(function($) {
// MODELS
var Paper = Backbone.Model.extend ({
defaults : {
title : null,
author: null,
}
});
// COLLECTIONS
var PaperCollection = Backbone.Collection.extend({
model : Paper,
initialize : function() {
console.log("We've created our collection");
}
});
// VIEWS
var PaperView = Backbone.View.extend({
tagName:'li',
className: 'resultTable',
events: {
'click .ptitle':'handleClick'
},
initialize: function() {
_.bindAll(this, 'render', 'handleClick');
},
render: function() {
$(this.el).html('<td>'+this.model.get('title')+'</td>');
return this; // for chainable calls
},
handleClick: function() {
alert('Been clicked');
}
});
var ListView = Backbone.View.extend({
events: {
//"keypress #new-todo": "createOnEnter",
},
initialize : function() {
console.log('Created my app view');
_.bindAll(this, 'render', 'addOne', 'appendOne');
this.collection = new PaperCollection();
this.collection.bind('add', this.appendOne); // collection event binder
this.counter = 0;
this.render();
},
render : function() {
console.log('Render app view');
$(this.el).append("<button id='add'>Add list item</button>");
$(this.el).append("<p>More text</p>");
// $(this.el).append("<ul></ul>");
/*
_(this.collection.models).each(function(item){ // in case collection is not empty
appendOne(item);
}, this); */
},
addOne: function() {
this.counter++;
var p = new Paper();
p.set({
title: "My title: " + this.counter // modify item defaults
});
this.collection.add(p);
},
appendOne: function(p) {
var paperView = new PaperView({
model: p
});
$('ul', this.el).append(paperView.render().el);
}
});
var App = new ListView({el: $('paper_list') });
// App.addOne();
})(jQuery);
Note not getting any errors in console on FF - but still not displaying any of the render outputs in AppView). Appreciate any help. Simple HTML:
<body>
<div class="container_16">
<div class="grid_16">
<div id="paper_list">
Text...
<ul class="thelist"></ul>
</div>
</div>
<div class="clear"></div>
</div>
</body>

This will at least get you rendering the ListView...
// MODELS
var Paper = Backbone.Model.extend ({
defaults : {
title : null,
author: null,
}
});
// COLLECTIONS
var PaperCollection = Backbone.Collection.extend({
model : Paper,
initialize : function() {
console.log("We've created our collection");
}
});
// VIEWS
var PaperView = Backbone.View.extend({
tagName:'li',
className: 'resultTable',
events: {
'click .ptitle':'handleClick'
},
initialize: function() {
_.bindAll(this, 'render', 'handleClick');
},
render: function() {
$(this.el).html('<td>'+this.model.get('title')+'</td>');
return this; // for chainable calls
},
handleClick: function() {
alert('Been clicked');
}
});
var ListView = Backbone.View.extend({
el: '#paper_list',
events: {
"click #add": "createOnEnter",
},
initialize : function() {
console.log('Created my app view');
_.bindAll(this, 'render', 'addOne', 'appendOne');
this.collection = new PaperCollection();
this.collection.bind('add', this.appendOne); // collection event binder
this.counter = 0;
this.render();
},
render : function() {
console.log(this);
$(this.el).append("<button id='add'>Add list item</button>");
$(this.el).append("<p>More text</p>");
// $(this.el).append("<ul></ul>");
/*
_(this.collection.models).each(function(item){ // in case collection is not empty
appendOne(item);
}, this); */
},
addOne: function() {
this.counter++;
var p = new Paper();
p.set({
title: "My title: " + this.counter // modify item defaults
});
this.collection.add(p);
},
appendOne: function(p) {
var paperView = new PaperView({
model: p
});
$('ul', this.el).append(paperView.render().el);
}
});
$(function(){
var App = new ListView();
});
A couple of things...First, I initialized your ListView inside of a document.ready to make sure that the DOM was ready to go, second, I made the el in the listview simply #paper_list then you can do $(this.el) later.
I at least got the button and "more text" to show up...Let me know if that helps!

Related

Backbone create a new view instance every request page

In my App i have created a View. this View is composed of a Template like a little Form. The Form has an button and in my View i create an click event to handle this button to create a new instance of another View passing the Form data to this View and put the data on html element. The problem is: if i enter in home route or in product 3 times and send a Form data, will appears 3 same Form datas.
Form view
window.userFormView = Backbone.View.extend({
el:$("#principal"),
events : {
'click .userButton' : 'newUser'
},
initialize:function(){
this.template = _.template($("#userFormView").html());
},
newUser : function(ev) {
ev.preventDefault();
//criamos uma nova instancia do model
window.user_view = new userViewes({model: users});
var u = { nome : $("#iName").val() ,sobrenome : $("#iLName").val() };
var user = new userModel(u);
users.add(user);
console.log(users);
return false;
},
render: function() {
this.$el.html("");
this.$el.html(this.template);
}
});
Form Template View
<script type="text/template" id="userFormView">
<form action="" id="form-new-user" class="formulario">
<span class="label">Name?</span><input type="text" id="iName" class="input">
<span class="label">Last Name?</span><input type="text" id="iLName" class="input">
<button class="userButton">Send</button>
<hr>
</form>
</script>
and my route are like this:
window.AppRouter = Backbone.Router.extend({
//
// Definindo rotas
//
routes: {
'home': 'index',
'product': 'productsList',
'foo1': 'doNothing1',
'foo2': 'doNothing2'
},
index: function () {
window.users = new userCollections();
window.userForm = new userFormView();
},
productsList : function() {
window.pCollection = new productCollections();
window.produtoForm = new produtoFormView();
},
doNothing1: function () {
console.log('doNothing1()');
},
doNothing2: function () {
console.log('doNothing2()');
}
});
window.router = new AppRouter();
Backbone.history.start();
userViewes view
window.userViewes = Backbone.View.extend({
// model: users,
el: $("#userContainer"),
initialize: function(){
this.model.on("add", this.render, this);
this.model.on("remove", this.render, this);
},
render: function() {
var self = this;
self.$el.html("");
this.model.each(function(user, indice) {
self.$el.append((new userView({model: user })).render().$el);
});
return this;
}
});
and finally userView:
window.userView = Backbone.View.extend({
//model: new userModel(),
tagName : 'div',
class : "userName",
events :{
'click .editar' : 'editar',
'click .remover' : 'remover',
'blur .sobrenome': 'fechar',
'keypress .sobrenome' : 'onEnterUpdate',
},
editar : function(ev) {
ev.preventDefault();
this.$('.sobrenome').attr('contenteditable', true).focus();
},
fechar : function(ev) {
var sobrenome = $(".sobrenome").text();
this.model.set("sobrenome", sobrenome);
$(".sobrenome").val();
this.$(".sobrenome").removeAttr("contenteditable");
},
onEnterUpdate : function(ev) {
var self = this;
if(ev.keyCode == 13) {
self.fechar();
_.delay(function(){
self.$(".sobrenome").blur();
}, 100);
}
},
remover : function(ev) {
ev.preventDefault();
window.users.remove(this.model);
},
initialize: function(){
this.template = _.template($("#userTemplate").html());
},
render : function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
When your view is using el option, make sure you clean up the existing view before you make a new one.
As it is, every time you switch between routes (without a full page refresh) a new instance pointing to same element is created which causes more and more event handlers to be bound to the el element which is in DOM, and the views stay in memory because of the binding. Try something like:
index: function () {
window.users = window.users || new userCollections();
if(window.userForm){
// clean up is important
window.userForm.remove();
}
window.userForm = new userFormView();
},
And of course, instead of repeating similar code in all routes, have a variable like this.currentView that points to the active view, and a common function that does necessary clean up
P.S: Adding properties to window object is a bad practice. Create your own name space or use the Router instance instead of window
I have found the answer. i implemented singleton pattern to get only one instance of the object. follow the code:
var single = (function(){
function createInstance() {
window.userForm = new userFormView();
window.users = new userCollections();
}
function users() {
return window.users;
}
function userForm() {
return window.userForm;
}
return {
init : function() {
if(!window.users && !window.userForm) {
createInstance();
}else{
this.render();
}
},
render: function() {
window.userForm.render();
}
}
}());
single.init();

Toggle class on mouse click event

I've got a Backbone.View that renders a collection and filters it on mouse click. I need to add class active to the button that I click, but the problem is that buttons are the part of this view and whenever I try to addClass or toggleClass it just renders again with default class. Here's my view:
var ResumeList = Backbone.View.extend({
events: {
'click #active': 'showActive',
'click #passed': 'showPassed'
},
initialize: function () {
this.collection = new ResumeCollection();
},
render: function (filtered) {
var self = this;
var data;
if (!filtered) {
data = this.collection.toArray();
} else {
data = filtered.toArray();
}
this.$el.html(this.template({ collection: this.collection.toJSON() });
_.each(data, function (cv) {
self.$el.append((new ResumeView({model: cv})).render().$el);
});
return this;
},
showActive: function () {
this.$('#active').toggleClass('active');
// a function that returns a new filtered collection
var filtered = this.collection.filterActive();
this.render(filtered);
}
});
But as I've already told, the class I need is toggled or added just for a moment, then the view is rendered again and it is set to default class. Is there any way to handle this?
I simplified the rendering and added some optimizations.
Since we don't have your template, I changed it to enable optimization:
<button id="active" type="button">Active</button>
<button id="passed" type="button">Passed</button>
<div class="list"></div>
Then your list view could be like this:
var ResumeList = Backbone.View.extend({
events: {
'click #active': 'showActive',
'click #passed': 'showPassed'
},
initialize: function() {
this.childViews = [];
this.collection = new ResumeCollection();
},
render: function() {
this.$el.html(this.template());
// cache the jQuery element once
this.elem = {
$list: this.$('.list'),
$active: this.$('#active'),
$passed: this.$('#passed')
};
this.renderList(); // default list rendering
return this;
},
renderList: function(collection) {
this.elem.$list.empty();
this.removeChildren();
collection = collection || this.collection.models;
// Underscore's 'each' has a argument for the context.
_.each(collection, this.renderItem, this);
},
renderItem: function(model) {
var view = new ResumeView({ model: model });
this.childViews.push(view);
this.elem.$list.append(view.render().el);
},
showActive: function() {
this.elem.$active.toggleClass('active');
var filtered = this.collection.filterActive();
this.renderList(filtered);
},
/**
* Gracefully call remove for each child view.
* This is to avoid memory leaks with listeners.
*/
removeChildren: function() {
var view;
while ((view = this.childViews.pop())) {
view.remove();
}
},
});
Additional information:
Managing Views and Memory Leaks
Underscore's each (notice the third argument)
Try to avoid callback hell, make the callbacks reusable (like renderItem)
I have edited the snippet can you try this.
var ResumeList = Backbone.View.extend({
events: {
'click #active': 'filterActive',
'click #passed': 'showPassed'
},
toggleElement: undefined,
initialize: function () {
this.collection = new ResumeCollection();
},
render: function (filtered) {
var self = this;
var data;
if (!filtered) {
data = this.collection.toArray();
} else {
data = filtered.toArray();
}
this.$el.html(this.template({ collection: this.collection.toJSON() });
_.each(data, function (cv) {
self.$el.append((new ResumeView({model: cv})).render().$el);
});
return this;
},
filterActive: function (evt) {
this.toggleElement = this.$el.find(evt.currentTarget);
// a function that returns a new filtered collection
var filtered = this.collection.filterActive();
this.render(filtered);
this.toggleActive();
},
toggleActive: function() {
if(this.toggleElement.is(':checked')) {
this.$el.find('#active').addClass('active');
} else {
this.$el.find('#active').removeClass('active');
}
}
});
Please note: I have taken checkbox element instead of button.

Update a collections view dynamically after removing the models from it

I have the following code:
// Create the Backbone view to display a list of vehicles
var Vehicle = Backbone.Model.extend({
idAttribute: "registrationNumber",
urlRoot: "/api/vehicles",
validate: function(attrs) {
if (!attrs.registrationNumber)
return "Registration number is required!";
}
});
var Vehicles = Backbone.Collection.extend({
model: Vehicle
});
// View for the model
var VehicleView = Backbone.View.extend({
tagname: "li",
attributes: {
"data-color" : "Rainbow"
},
events: {
"click .deleteButton" : "deleteVehicle"
},
render: function() {
var template = _.template($("#vehicleTemplate").html());
var html = template(this.model.toJSON());
this.$el.html(html);
return this;
},
deleteVehicle: function() {
console.log("Model destroyed");
this.model.destroy();
}
});
var VehiclesView = Backbone.View.extend({
tagName : "ul",
initialize: function () {
this.model.on("change", this.render, this);
this.model.on("add", this.render, this);
this.model.on("remove", this.render, this);
},
render: function() {
var self = this;
this.model.each(function(vehicle){
var vehicleView = new VehicleView({model: vehicle});
self.$el.append(vehicleView.render().$el);
});
}
});
// Add cars to the collection
var car1 = new Vehicle({registrationNumber: "XLI887", color: "blue"});
var car2 = new Vehicle({registrationNumber: "ZNP123", color: "blue"});
var car3 = new Vehicle({registrationNumber: "XUV456", color: "grey"});
var vehicles = new Vehicles([car1, car2, car3]);
var vehiclesView = new VehiclesView({el: "#container", model: vehicles});
vehiclesView.render();
The rendering looks good, but when I click delete, the new collection (with one less element) is appended to the existing view of the older collection. How do I replace the old collection view with the new one?
I modified the "remove" handler as below and it dynamically removed the "li" element from the list and showed the new collection.
initialize: function () {
this.model.on("change", this.render, this);
this.model.on("add", this.render, this);
this.model.on("remove", this.onDelete, this);
},
onDelete : function(vehicle) {
console.log("Vehicle deleted");
this.$el.find("li#" + vehicle.id).remove();
}

Backbone.js: View does not reflect Model change in Todo App

I've been following Addy Osmani's book Developing Backbone.js Applications, the version available online for free. As such, the first exercise is the much touted sample "Todo app", which can also be lifted from Backbone's original documentation samples.
While the app updates the view when a new entry is added to the list of things to do, I've hit an odd snafu: When I edit a list entry and press the enter button, nothing happens. The input doesn't go out of focus and the label in the entry doesn't update automatically. However, the Model does save the edited entry, because when I refresh the page, the edited entry features the change I made it to it.
I have no idea what is going on, as the view does listen to model changes.
Here's the code:
app.TodoView = Backbone.View.extend({
tagName: "li",
template: _.template($("#item-template").html()),
events:{
"dblclick label":"edit",
"keypress .edit": "updateOnEnter",
"blur .edit": "close"
},
initalize: function(){
this.listenTo(this.model, "change", this.render);
},
render: function(){
this.$el.html(this.template(this.model.toJSON()));
this.myInput = this.$(".edit");
return this;
},
edit: function(){
this.$el.addClass("editing");
this.myInput.focus();
},
close: function(){
var value = this.myInput.val().trim();
if(value) this.model.save({ title: value});
this.$el.removeClass("editing");
},
updateOnEnter: function(x){
if(x.keyCode === 13)
this.close();
}});
app.AppView = Backbone.View.extend({
el: $("#app"),
statsTemplate: _.template($("#stat-template").html()),
events:{
"keypress #newentry": "createOnEnter",
"click #clear-completed": "clearCompleted",
"click #toggle-all": "toggleAllComplete"
},
initialize: function(){
this.allCheck = this.$("#toggle-all")[0];
this.newJob = this.$("#newentry");
this.main = this.$("#main");
this.foot = this.$("#foot");
this.listenTo(app.TodoCol, "add", this.addOne);
this.listenTo(app.TodoCol, "reset", this.addAll);
this.listenTo(app.TodoCol, "change:completed", this.filterOne);
this.listenTo(app.TodoCol, "filter", this.filterAll);
this.listenTo(app.TodoCol, "all", this.render);
app.TodoCol.fetch();
console.log(app.TodoCol.toJSON());
},
addOne: function(todo){
var view = new app.TodoView({model: todo});
$("#items").append(view.render().el);
},
addAll: function(todo){
this.$("items").html("");
app.TodoCol.each(this.addOne, this);
},
filterOne: function(todo){
todo.trigger("visible");
},
filterAll: function(){
app.TodoCol.each(this.filterOne, this);
},
render: function(){
var comp = app.TodoCol.getCompleted().length;
var open = app.TodoCol.getOpen().length;
if(app.TodoCol.length){
this.main.show();
this.foot.show();
this.foot.html(this.statsTemplate({
completed: comp,
remain: open
}));
this.$("#filters li").removeClass("active")
.filter('[href="#/' + ( app.TodoFilter || '' ) + '"]')
.addClass('selected');
}else{
this.main.hide();
this.foot.hide();
}
this.allCheck.checked = !open;
},
newEntry: function() {
return {
title: this.newJob.val().trim(),
order: app.TodoCol.nextOrder(),
completed: false
};
},
createOnEnter: function(event){
if(event.which !== 13 || !this.newJob.val().trim())
return;
app.TodoCol.create(this.newEntry());
this.newJob.val("");
},
clearCompleted: function(){
_.invoke(app.TodoCol.getCompleted(), "destroy");
return false;
},
toggleAllComplete: function(){
var comp = this.allCheck.checked;
app.TodoCol.each(function(t){
t.save({ completed: comp});
});
}});
Here are the models for good measure:
app.Todo = Backbone.Model.extend({
defaults:{
title: "",
completed: false
},
toggle: function(){
this.save({completed: !this.get("completed")});
}});
app.List = Backbone.Collection.extend({
model: app.Todo,
localStorage: new Backbone.LocalStorage("todo-backbone"),
getCompleted: function(){
return this.where({completed: true});
},
getOpen: function(){
return this.where({completed: false});
},
nextOrder: function(){
if(!this.length) return 1;
return this.last().get("order") + 1;
},
comparator: function(todo){
return todo.get("order");
}});
Please help me!

Backbone Sorting and Updating a listview after an action

i am trying to make my first backbone app, and have run into a problem that i just cant solve..
I have a list of links, each link has a counter next to it,
when i click on a link i want the counter to increment by 1. (i have made this, and it is working)
Next i want the link i clicked to move up in the list IF the counter value is higher than the link above.
like this.
first link (4)
second link (3)
third link (3) <-- if i click on this link i want it to move up above second link.
I have tried using comparator and sortBy, but each time i try something i just cant seem to re-render the view and also have the link move up one spot.
I did manage to sort the list initially, when the main view is initialized.
But updating the view and list placement after i click one of the links i cant figure out how to accomplish.
my code:
(function() {
window.App = {
Models: {},
Collections: {},
Views: {}
};
window.template = function(id) {
return _.template( $('#' + id).html() );
};
//Modellen
App.Models.Task = Backbone.Model.extend({
defaults: {
name: 'Foo Bar Baz',
uri: 'http://www.google.com',
counter: 0
},
validate: function(attr) {
if ( ! $.trim(attr.name) ) {
return 'En opgave kræver en title.';
};
}
});
//Collection
App.Collections.Tasks = Backbone.Collection.extend({
model: App.Models.Task,
comparator: function(task) {
return task.get('counter');
},
});
//Singel view
App.Views.TaskView = Backbone.View.extend({
tagName: 'li',
template: template('Tasks'),
initialize: function() {
this.model.on('change', this.render, this);
this.model.on('destroy', this.remove, this);
},
events: {
'click .edit' : 'retTask',
'click .delete' : 'destroy',
'click .uriLink' : 'addCounter'
},
retTask: function() {
var newTaskNavn = prompt('Hvad skal det nye navn være', this.model.get('name'));
if ( !newTaskNavn ) return;
this.model.set('name', newTaskNavn);
},
destroy: function() {
this.model.destroy();
},
addCounter: function(e) {
e.preventDefault();
var newCounter = this.model.get('counter');
this.model.set('counter', newCounter + 1);
},
remove: function() {
this.$el.remove();
},
render: function() {
this.$el.html(this.template(this.model.toJSON()) );
return this;
}
});
//Collection View
App.Views.TasksView = Backbone.View.extend({
tagName: 'ul',
initialize: function() {
this.collection.on('add', this.addOne, this);
this.render();
},
render: function() {
this.collection.each(this.addOne, this);
return this;
},
addOne: function(task) {
var taskView = new App.Views.TaskView({ model: task });
this.$el.append(taskView.render().el);
}
});
App.Views.AddTask = Backbone.View.extend({
el: '#addTask',
initialize: function() {
},
events: {
'submit' : 'submit'
},
submit: function(e) {
e.preventDefault();
var taskNavn = $(e.currentTarget).find('.navnClass').val(),
uriNum = $(e.currentTarget).find('.uriClass').val();
if ( ! $.trim(taskNavn)) {
var test = prompt('opgaven skal have et navn', '');
if ( ! $.trim(test)) return false;
taskNavn = test;
}
if( uriNum.indexOf( "http://" ) == -1 ) {
addedValue = 'http://',
uriNum = addedValue + uriNum;
}
$(e.currentTarget).find('input[type=text]').val('').focus();
//var task = new App.Models.Task({ name: taskNavn, uri: uriNum });
this.collection.add({ name: taskNavn, uri: uriNum });
}
});
// new tasks collection
var tasks = new App.Collections.Tasks([
{
name: 'Foo',
uri: 'www.google.com',
counter: 3
},
{
name: 'Bar',
uri: 'http://google.com',
counter: 2
},
{
name: 'Baz',
uri: 'http://www.google.com',
counter: 1
}
]);
// tasks.comparator = function(task) {
// return task.get("counter");
// };
tasks.sort();
// new collection view (add)
var addTaskView = new App.Views.AddTask({ collection: tasks});
// new collection view
var tasksView = new App.Views.TasksView({ collection: tasks });
$('.tasks').html(tasksView.el);
})();
My HTML: (if someone wanna try to replicate the scenario :)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>LinkList</title>
</head>
<body>
<h1>Mine opgaver</h1>
<form action="" id="addTask">
<input class="navnClass" type="text" placeholder="Link name"><input clas s="uriClass" type="text" placeholder="www.url-here.com">
<button class="nyOpgave">Ny opgave</button><br />
</form>
<div class="tasks">
<script type="text/template" id="Tasks">
<span class="linkNavn"><%= name %></span> - <%= uri %> : [<span class="counterClass"><%= counter %></span>] <button class="edit">Edit</button> <button class="delete">Delete</button>
</script>
</div>
<script src="js/underscore.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>
<script src="js/jquery.js"></script>
<script src="js/backbone.js"></script>
<script src="main.js"></script>
</body>
</html>
can anyone please help me figure this one out ?
/Cheers
Marcel
Okay , i have created the application for you , as you have intended it to run.I'm going to try and explain you the entire code , what i have written and why i have written.
First , take a look at the JSfiddle : here
Next , let me explain :
1.This is my model that stores the name of the link , href , the id(not used in my example but its just good practise to assign a unique id to each model) and finally the number of clicks to a link(model).
var myModel = Backbone.Model.extend({
defaults:{
'id' : 0,
'name' : null,
'link' : '#',
'clicks' : 0
}
});
2.This the collection , that stores all my models , i have added a comparator function so that when ever you add a model to a collection , it will sort the collection.
Note : i have added a - sign to sort the collection in descending order of clicks (link with maximum click to appear first)
var myCollection = Backbone.Collection.extend({
model: myModel,
comparator: function(item) {
return -item.get('clicks');
}
});
3.Now this is my main view , what do i mean main view ? This view does the main rendering of the list , that you want to show.Pretty self explanatory code here.One thing , the this.coll.bind('add change',this.render,this) , i have added a 'change' because whenever any of the models in this collection change , we want to re-render the entire list , this happens when i change the count of any link , on clicking it , i want to re-render the entire list.
var myView = Backbone.View.extend({
el: $("#someElement"),
tagName : 'ul',
initialize: function() {
this.coll = new myCollection();
this.coll.bind('add change',this.render,this);
},
events: {
"click #add": "add"
},
add: function(e){
e.preventDefault();
var mod = new myModel();
var name = $('#name').val();
var link = $('#link').val();
mod.set({'id':mod.cid, 'name':name,'link':link});
this.coll.add(mod);
},
render : function(){
$('#list').empty();
this.coll.sort();
this.coll.forEach(function(model){
var listItem = new printView({ model: model});
$('#list').append(listItem.render().el);
});
}
});
4.This is my sub-view , why do i ever make a second view , why isnt 1 view sufficient ?
Well this consider a scenario, with every link you have a delete button (for instance) when i click the delete button (and i have just 1 view) how do i identify which model to destroy(remove from collection ? ) , 1 possible way would be to associate a cid with each model and then on click i can do a this.coll.getByCid() , but this isnt such a good way to do it , IMHO , so i created a separate view for each model.This View renders each model and returns nothing more.
var printView = Backbone.View.extend({
tagName: 'li',
initialize : function(options) {
_.bindAll(this, "render");
},
events:{
"click a": "count"
},
render:function(){
var linkName = this.model.get("name");
var link= this.model.get("link");
var clicks = this.model.get("clicks");
this.$el.append("<a class='link' href='"+link+"'>"+linkName+"</a> ("+clicks+")");
return this;
},
count:function(e){
e.preventDefault();
e.stopPropagation();
var clicks = this.model.get("clicks");
clicks++;
this.model.set({'clicks':clicks});
}
});
5.Initializing my (main) myView
new myView();
Note: I do believe that this application/code can be written in much better way , with several improvements but with my calibre and with the fact that it works ( :p ) i think it can help you.
The collection comparator is only executed when new models are added to the collection: it doesn't update the collection order when properties change. In order to achieve this, you need to call collection.sort():
App.Collections.Tasks = Backbone.Collection.extend({
model: App.Models.Task,
initialize: function() {
this.on('change:counter', this.sort);
},
comparator: function(task) {
return task.get('counter');
}
});
In the list view you can listen to the collection's sort event, and re-render your view:
App.Views.TasksView = Backbone.View.extend({
tagName: 'ul',
initialize: function() {
this.collection.on('add', this.addOne, this);
this.collection.on('sort', this.render, this);
this.render();
},
render: function() {
//if there are existing child views, remove them
if(this.taskViews) {
_.each(this.taskViews, function(view) {
view.remove();
});
}
this.taskViews = [];
this.collection.each(this.addOne, this);
return this;
},
addOne: function(task) {
var taskView = new App.Views.TaskView({ model: task });
this.$el.append(taskView.render().el);
//keep track of child views
this.taskViews.push(taskView);
}
});

Categories

Resources