onclick event updating the model attribute but the view not rendering - backbone.js

In my backbone, i made a onclick event to each model, to update the name.. that works fine. also i am getting the trigger in my view to update the view of the model..
But the view not updating the model.. and the text not at all changing in the html..
my model view:
var studentView = Backbone.View.extend({
tagName:'li',
events:{
'click':"called"
},
template:_.template($("#studentTemplate").html()),
render:function(){
this.$el.append(this.template(this.model.toJSON()));
this.model.get('scored') > 60 ? this.$el.addClass("active") : null;
return this;
},
called:function(){
this.model.set('name','text'); // i am setting a name as 'text' for example
}
});
my render View:
var studentsView = Backbone.View.extend({
el:$(".page"),
events:{
"click #highScoreBtn":"showHighScore"
},
initialize:function(){
this.collection = new collection(student);
this.render();
this.collection.on('change', this.renderOne,this);
},
render:function(){
var that = this;
_.each(this.collection.models, function(item){
that.$el.find('ul').append(new studentView({model:item}).render().el);
})
},
renderOne:function(data){
this.$el.find('ul').append(new studentView({model:data}).render().el); // appending as new element instead updating the existing one..
}
})
so, what is wrong with my code.. or any one correct me this in my jsfiddle..
here is my jsfiddle link
Thanks in advance..

this is works me:
var studentView = Backbone.View.extend({
tagName:'li',
events:{
'click':"called"
},
template:_.template($("#studentTemplate").html()),
initialize:function(){
this.listenTo(this.model, 'change', this.render);
},
render:function(){
this.$el.html(this.template(this.model.toJSON()));
this.model.get('scored') > 60 ? this.$el.addClass("active") : null;
return this;
},
called:function(){
this.model.set('name','text');
}
});

Related

Backbone.js collection view render multiple times for single trigger

I have backbone.js collection and collectionview. collection view listening to its collection add event. But when I add new models to it's collection it renders mutiple times for each model.
Please Check the JSFiddle
var ImageCollectioView = Backbone.View.extend({
initialize: function() {
this.collection.bind('add', this.render, this);
},
collection: imgColection,
el: '#cont',
render: function() {
var els = [], self = this;
this.collection.each(function(image){
var imageView = new ImageView({model: image});
self.$el.append(imageView.render().el);
});
return this;
}
});
Your render method renders the entire collection. So after adding a model you should clear the existing item views:
render: function() {
var els = [], self = this;
this.$el.empty();
//------^---- clear existing
this.collection.each(function(image){
var imageView = new ImageView({model: image});
self.$el.append(imageView.render().el);
});
return this;
}
That being said, it's better to add a separate method that just appends single item view rather than rendering the entire collection:
var ImageCollectioView = Backbone.View.extend({
initialize: function() {
this.render();
this.listenTo(this.collection, 'add', this.renderItem);
},
el: '#cont',
render: function() {
this.collection.each(this.renderItem, this);
return this;
},
renderItem: function(image) {
var imageView = new ImageView({
model: image
});
this.$el.append(imageView.el);
}
});
Updated Fiddle

Backbone Client Side Collection Filtering

Consider a backbone app that has a select list of categories, and a table of files underneath it. All files, regardless of the category they belong to are in the App.collection.files collection. When a user selects a new category, I'd like to filter the table to only show those matching files.
I'd like to do this completely on the client side. I do NOT want to fetch a new list of files from the server each time a new category is picked.
I plan to to a App.collection.files.where({category_id: xx}) within App.View.Files.render() to accomplish the filtering itself.
What is the best Backbone-y way to trigger/respond to an event to get this working?
One idea I am playing with is another collection, that contains the filtered files, but not sure if this is the best approach.
App.View.FilesPage = Backbone.View.extend({
template: App.utils.template('files-page'),
className: 'container-fluid',
initialize: function(){
this.render();
},
render: function(){
this.$el.html(this.template());
this.$el.find('.categories').append(
new App.View.Categories({collection:App.collection.categories}).el
);
this.$el.find('.files table').append(
new App.View.Files({collection:App.collection.files}).el
);
return this;
}
});
App.View.Categories = Backbone.View.extend({
tagName: 'select',
events: {
'change': 'onChange'
},
initialize: function(){
this.$el.attr('size', this.collection.length + 1);
this.render();
},
render: function(){
var option = function(value, text, isSelected){
return $('<option></option>', {'value':value, 'text':text});
};
this.$el.html(option(0, 'All Documents'));
this.$el.append(
this.collection.invoke(function(){
return option(this.get('category_id'), this.get('title'));
})
).val(0);
return this;
},
onChange: function(event){
}
});
App.View.Files = Backbone.View.extend({
tagName: 'tbody',
initialize: function(){
this.render();
},
render: function(){
this.$el.html(
this.collection.invoke(function(){
return new App.View.File({model:this}).el;
})
);
return this;
}
});
Here is one solution I came up with, that seems fine and dandy on the surface.
Code changes are called out within comments.
Thoughts on this solution, and completely different solutions are welcome.
App.View.FilesPage = Backbone.View.extend({
template: App.utils.template('files-page'),
className: 'container-fluid',
initialize: function(){
// CHANGE: Store application wide reference to 'Categories' model
App.model.categories = new App.Model.Categories();
this.render();
},
render: function(){
this.$el.html(this.template());
this.$el.find('.categories').append(
new App.View.Categories({
// CHANGE: Pass model into 'Categories' view
model: App.model.categories,
collection: App.collection.categories
}).el
);
this.$el.find('.files table').append(
new App.View.Files({collection:App.collection.files}).el
);
return this;
}
});
App.View.Categories = Backbone.View.extend({
tagName: 'select',
events: {
'change': 'onChange'
},
initialize: function(){
this.$el.attr('size', this.collection.length + 1);
this.render();
},
render: function(){
var option = function(value, text, isSelected){
return $('<option></option>', {'value':value, 'text':text});
};
this.$el.html(option(0, 'All Documents'));
this.$el.append(
this.collection.invoke(function(){
return option(this.get('category_id'), this.get('title'));
})
).val(0);
return this;
},
onChange: function(){
// CHANGE: Update attribute within 'Categories' model
this.model.set('category_id', this.$el.val());
}
});
App.View.Files = Backbone.View.extend({
tagName: 'tbody',
initialize: function(){
this.collection.on('reset', this.render, this);
// CHANGE: Listen for change on 'Categories' model, and re-render this view
App.model.categories.on('change:category_id', this.render, this);
this.render();
},
render: function(){
this.$el.html(
_.invoke(this.getFilteredFiles(), function(){
return new App.View.File({model:this}).el;
})
);
return this;
},
// CHANGE: Return a filtered set of models from collection, based on 'Categories' model attributes
getFilteredFiles: function(){
var categoryId = App.model.categories.get('category_id');
if (categoryId!=0){
return this.collection.where({'category_id': categoryId});
}
else{
return this.collection.models;
}
}
});

detect select status of options in a multiselect with backbone.picky

I have a multiselect dropdown that I'm rendering with Backbone. As user selects or deselects options, I'd like those (de)selections to be saved asynchronously via Backbone.
I found Backbone.Picky, and thought it might be helpful in my endeavor, but I can't seem to get it to detect selects.
In my FieldView's clicked function below, console.log(this.model.selected); always writes undefined to the log. Why?
var Field = Backbone.Model.extend({
initialize: function(){
var selectable = new Backbone.Picky.Selectable(this);
_.extend(this, selectable);
}
});
var FieldView = Backbone.View.extend({
tagName: "option",
initialize: function(){
_.bindAll(this, 'render');
},
events: {
"click":"clicked"
},
clicked: function(e) {
var data_type = this.model.get("DATA_TYPE");
console.log(this.model.selected); // why is this undefined?
console.log("it's a " + data_type);
},
render: function(){
this.$el.attr('value', this.model.get('COLUMN_NAME')).html(this.model.get('display_name'));
return this;
}
});
Here's a jsfiddle http://jsfiddle.net/EAZCt/2/ for more context.
Using Backbone, how can I asynchronously save the select-status of options in a multiselect list?
Your model object doesn't ever have "selected" property because you never select the model. I have never used Backbone.Picky but it seems that you could try:
var FieldView = Backbone.View.extend({
tagName: "option",
initialize: function(){
this.model.on('selected', this.selected, this);
},
events: {
"click":"clicked"
},
clicked: function() {
this.model.select();
},
selected: function() {
var data_type = this.model.get("DATA_TYPE");
console.log(this.model.selected);
console.log("it's a " + data_type);
},
render: function(){
this.$el.attr('value', this.model.get('COLUMN_NAME')).html(this.model.get('display_name'));
return this;
}
});
http://jsfiddle.net/hGEYL/

Backbone.js not rendering

My collection is not rendering for some reason. Cannot find out why.
TreeItem = Backbone.Model.extend({
});
TreeList = Backbone.Collection.extend({
model: TreeItem,
url: "/get_tree_list"
});
window.tree_list = new TreeList();
// VIEW
window.TreeItemView = Backbone.View.extend({
tagName: 'li',
initialize: function(){
_.bindAll(this, 'render');
},
render: function(){
$(this.el).html('<span>'+this.model.get('title')+'</span>');
return this;
}
});
window.TreeListView = Backbone.View.extend({
el: "#tree-structure",
events: {
},
initialize: function() {
_.bindAll(this, 'appendItem', 'render');
tree_list.bind('add', this.appendItem);
tree_list.fetch();
this.render();
},
render: function() {
tree_list.each(this.appendItem);
return this;
},
appendItem: function(item){
var tree_item_view = new TreeItemView({
model: item
});
$(this.el).append(tree_item_view.render().el);
}
});
var tree_list_view = new TreeListView;
Backbone.js provides a lot to be interpreted that's where people new go wrong. Your mistake is fundamental in nature. You tie the View directly to the model
see initialize function where a instance of collection is rendered!!
Always and anywhere you create model, collection pass then as parameters to the constructor of views. Check my fiddle
Never call render inside model, view or collection. They must be inside application file
JsFiddle
http://jsfiddle.net/35QGM/

How to bind elements in backbone?

I have a small backbone class:
view = Backbone.View.extend({
events: {
"click textarea" : "doSomething"
},
doSomething : function() {
var textarea = $(this.el).find('textarea')
// I would like to just call, this.textarea, or this.elements.textarea
}
});
Ideally I would like to be able to access my textarea through a variable instead of having to search the element every time. Anyone have any idea on how to accomplish this?
maybe i am under thinking it but how bout giving the textarea a class or id and target specifically when needed,
or create a sub view that generates the textarea
var View = Backbone.View.extend({
el: '#main',
initialize: function(){
this.render();
},
render: function() {
var subview = new SubView();
this.$('form').append(subview.el);
this.$('form').show();
},
});
var SubView = Backbone.View.extend({
tagName: 'textarea',
id: 'whateverId',
events: {
"click" : "doSomething"
},
initialize: function(){
this.render();
},
doSomething : function(event) {
var textarea = $(event.target);
// or var textarea = $(this.el);
},
render: function(){
return $(this.el);
}
});
The other answers get you the reference that you need, but if you really need a handle to the textarea, then you can do something like this:
view = Backbone.View.extend({
initialize: function() {
this.myTextareaElement = $(this.el).find('textarea')
}
events: {
"click textarea" : "doSomething"
},
doSomething : function() {
// use this.myTextareaElement ...
}
});
pass the event as the argument and then use the event target
view = Backbone.View.extend({
events: {
"click textarea" : "doSomething"
},
doSomething : function(event) {
var textarea = $(event.target);
}
});

Resources