Re-render view without models changed backbone - javascript

i'm rendering a view from a collection of user.When a specific attribute(Status=online,offline) in a user change the view correctly show on the dom the value of attribute changed. But if i want render the view without the model in which the attribute is changed or viceversa add to view a model in wich attribute is changed?
This is a code that send to view a collection with users status online:
var user_on=Models.utenti.filter(function(model){
return model.get('status') === "on";
});
var users_online = new Usercollection(user_on);
var page=new Homelistuser({model:users_online});
this.changePage(page);
And this is a view:
var Homelistuser = Backbone.View.extend({
tagName: "ul",
id: "list",
template: Handlebars.compile(template),
initialize: function () {
Models=this.model;
this.model.bind("reset", this.render, this);
$(window).on('orientationchange', this.onOrientationChange);
},
render: function (eventName) {
$(this.el).empty();
_.each(this.model.models, function (ad) {
$(this.el).append(new SingleUserView({
model: ad
}).render().el);
}, this);
return this;
},

You could filter online users in the render function of your view, I believe you called the users_online collection as model in it, so:
model.reset(model.filter(function(model){
return model.get('status') === 'on';
}));
or maybe just filter elements out as you append SingleUserViews
_.each(this.model.models, function (ad) {
if (ad.get('status') !== 'on') return;
$(this.el).append(new SingleUserView({
model: ad
}).render().el);
}, this);

Related

Collection not firing listen events

In my backbone collection I am working with a collection view at the moment, the view is generated "on the fly".
I am struggling to get the view to update when something is added to the collection, on debugging I have noticed that the collection does not have a _listenid which in my limited knowledge I assume means it cannot listen for the events bound to it?
What would this be happening?
Here is my view,
Pops.Views.ProjectManagers = Backbone.View.extend({
className: 'ui-user-list ui-user-list--single',
template: _.template($("#tpl-project-managers").html()),
events: {
"click .js-add-user": "addUser"
},
initialize: function () {
// this.collection.on('all', function() {
// console.log(arguments);
// });
// this.collection.on('reset', this.render, this);
console.log(this.collection);
this.collection.on('add', this.addOneProjectManager, this);
},
render: function () {
//alert("!!!!!");
this.$el.html(this.template());
this.addAllProjectManagers();
return this;
},
addAllProjectManagers: function () {
this.collection.each(this.addOneProjectManager, this);
},
addOneProjectManager: function (model) {
console.log(model);
if(this.model.get('is_admin') == true) {
model.set('admin', true);
}
var teamMember = new App.Views.SingleTeamMember({
model: model,
project: this.model
});
this.$('.ui-member-list').prepend(teamMember.render().el);
},
});
If I physically refresh the page, the collection then has a _listenid
I initialise this view like this,
var projectManagerList = new Pops.Views.ProjectManagers({
model : this.model,
collection : this.model.get('project_manager')
});
and this is the model I pass through,

Backbone - aggregator event firing for all models in collection

I'm trying to use the event aggregator to fire a method off of a model's view. The problem is, when I fire the update or save method for the ItemView, it iterates through all models in the collection. How do I get it to not only fire properly for the model which the view represents (or a new modal in the save method's case), but also prevent it from firing for every model in the collection?
This application consists of a collection of Items, each Item has a model which is rendered into an ItemView and listed on the page. If a user clicks the edit item icon, then a ModalView is instantiated and the current Item model data is injected into the ModalView.
The ModalView which gets loaded with a template for the respective task. For this instance, i'm loading a template to edit the Item. Here's a summary of the relevant code:
var ModalView = Backbone.View.extend({
tagName: "section",
className: "modal",
events: {
'click .close': 'close',
'click .minimize': 'minimize',
'click .maximize': 'maximize',
'click .save-item': 'saveItem',
},
html: null,
initialize: function(options) {
this.template = _.template(ModalTemplate);
this.vent = options.vent;
},
saveItem: function() {
this.vent.trigger('item.save');
},
});
The item collection's view is here:
var ItemsView = Backbone.View.extend({
tagName: 'ul',
className: 'item-items',
render: function(){
var self = this;
// Make it flex GRRRR
this.$el.addClass('flex-item');
this.collection.each(function(item) {
var date = item.get('created_at');
var itemView = new ItemView({ model: item, vent: App.vent });
this.$el.append(itemView.render().el);
}, this);
return this;
}
});
Finally, the item model's view which contains the edit method that fires the ModalView
var ItemView = Backbone.View.extend({
tagName: 'li',
className: 'item',
events: {
'click .edit-item': 'edit'
},
initialize: function(options) {
this.template = _.template(ItemTemplate);
options.vent.bind("item.save", this.save);
options.vent.bind("item.update", this.update);
},
save: function() {
var attributes, item;
item = new App.api.item.model();
attributes = getMeta(true);
item.save(attributes)
.done(function(res) {
Ui.modal.destroy();
// Re-render items
App.routers.main.render.User.sidebar();
App.routers.main.render.Item.items(function() {
Ui.resizeContent();
});
})
.fail(function(res) {
console.log(res);
});
},
update: function() {
console.log('update') // fires App.api.item.collection.length times
var attributes, item;
item = App.api.item.collection.get(App.rendered.modal.$el.data('id'));
attributes = getMeta();
item.save(attributes)
.done(function(res) {
Ui.modal.destroy();
// Re-render items
App.routers.main.render.Item.items(function() {
Ui.resizeContent();
});
})
.fail(function(res) {
console.log(res);
});
},
edit: function() {
Ui.modal.new(ItemModalTemplate, this.model.attributes);
App.rendered.modal.$el.attr('data-id', this.model.get('_id'));
// New Editor
var editor = document.querySelector('#item-editor');
window.editor = new MediumEditor(editor, editorOptions);
}
});
Obviously I'm missing something fundamental here because console.log('update') in the save method of the ItemView fires for every item in the collection. What I was trying to do was keep the logic for save, and update in the view for the Item for organizational purposes.
Many thanks.
Instead of options hold the model itself in the ItemModelView so you can call save directly without need for events.
Replace this Ui.modal.new(ItemModalTemplate, this.model.attributes); with UI.modal.new(ItemModalTemplate, this.model), and this this.vent.trigger('item.save'); with this.model.save()

Collection add event listener in Backbone

I am trying to update my view whenever I add a new model to my collection. My first question is do I automatically add a model to my collection when I save that model, like:
PostsApp.Views.Form = Backbone.View.extend({
template: _.template($('#form-template').html()),
render: function(){
this.$el.html(this.template(this.model.toJSON()));
},
events:{
'click button' : 'save'
},
save: function(e){
console.log("is this working");
e.preventDefault();
var newname = this.$('input[name=name-input]').val();
var newadress = this.$('input[name=adress-input]').val();
this.model.save({name: newname, adress : newadress});
}
});
or do I still have to do collection.add()
Other than that to see the new model in my view I am trying to add an 'add' event listener like this:
PostsApp.Views.Posts = Backbone.View.extend({
initialize: function(){
this.collection.on('add', this.addOne, this);
},
render: function(){
this.collection.forEach(this.addOne, this);
},
addOne: function(post){
var postView = new PostsApp.Views.Post({model:post});
postView.render();
this.$el.append(postView.el);
}
});
This not only doesnt work, but when I add the initialize method, it just duplicates everything in my model when the page is first loaded.
Nope.. When you do a model.save , it will just create a zombie model ( If it not already a part of the collection .i.e If a New model is saved) which is not a part of any collection.
So your add event will not be triggered for the collection.
If you want the add event to be triggered , Use the create method of collection , which then will know on which collection the new model has to be added..
collection.create({model});
Then it would internally add the model to the collection and fire the add event
Also it is a better idea to use listenTo instead of attaching events using on
this.listenTo(this.collection, 'add', this.addOne);
Code
PostsApp.Views.Form = Backbone.View.extend({
template: _.template($('#form-template').html()),
render: function () {
this.$el.html(this.template(this.model.toJSON()));
},
events: {
'click button': 'save'
},
save: function (e) {
console.log("is this working");
e.preventDefault();
var newname = this.$('input[name=name-input]').val();
var newadress = this.$('input[name=adress-input]').val();
this.collection.create({
name: newname,
adress: newadress
});
}
});
PostsApp.Views.Posts = Backbone.View.extend({
initialize: function () {
this.listenTo(this.collection, 'add', this.addOne);
},
render: function () {
this.collection.forEach(this.addOne, this);
},
addOne: function (post) {
var postView = new PostsApp.Views.Post({
model: post,
collection : this.collection
});
postView.render();
this.$el.append(postView.el);
}
});

Backbone FETCH from JSON, edit model data then SAVE back to JSON

I've been working through Code School's Anatomy of Backbone.js course, but am confused when trying to save model changes back to the server. Perhaps you can help.
This is what I understand needs to happen:
Populate collection from a JSON data source using fetch();
Append the collection to the DOM
Edit a model (uncheck checkbox, which sets 'favourite' to false)
Save the model.
My assumption is that if I were to unselect a record as a 'favourite' then hit refresh, the change would be persistant and also evident in the JSON file. However, this isn't the case and the original collection is loaded and JSON is unchanged.
I think my confusion is in using the fetch method and declaring the URL within the model and collection.
How can I get this model change to be persistant?
Model:
var Contact = Backbone.Model.extend({
url: '/contacts',
defaults:{
favourite: false
},
toggleFavourite: function(){
if(this.get('favourite') === false)
{
this.set({ 'favourite': true });
} else {
this.set({ 'favourite': false })
}
this.save();
}
});
Collection
var Contacts = Backbone.Collection.extend({
model: Contact,
url: '/contacts'
});
Views
var ContactView = Backbone.View.extend({
className: 'record',
template: _.template('<span><%= name %></span>' +
'<span class="phone-number"><%= phone %></span>' +
'<input type="checkbox" <% if(favourite === true) print("checked") %>/>'),
events: {
'change input': 'toggleFavourite',
'click .phone-number': 'dial'
},
initialize: function(){
this.model.on('change', this.render, this);
},
toggleFavourite: function(e){
this.model.toggleFavourite();
},
dial: function(e){
alert('Dialing now...');
},
render: function(){
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var ContactsView = Backbone.View.extend({
initialize: function(){
this.collection.on('add', this.addOne, this);
this.collection.on('reset', this.addAll, this);
},
addOne: function(contact){
var contactView = new ContactView({ model: contact });
this.$el.append(contactView.render().el);
},
addAll: function(){
this.collection.forEach(this.addOne, this);
},
render: function(){
this.addAll();
}
});
App.js
var contacts = new Contacts(); //creates list
contactsView = new ContactsView({ collection: contacts}); //creates list view
contacts.fetch({url: 'contacts/data.json'}); //populates list
$('#mainPanel').append(contactsView.el); //appends list to DOM
Backbone works on client, and can't change file on server itself.
You need to store dynamic data somewhere on server (maybe mongodb if you use json it will be easier).
contacts/data.json named static file. because it is not changing while you did't owerwrite it on the server.

Getting the attribute from a View's Model when the view is clicked (backbone.js)

When a user clicks on a div with class .photo_container which is part of the view PhotoListView, there is a function sendSelectedPhotoId that will be triggered. This function has to get the attribute photo_id from the Photo model that belongs to this view whose div .photo_container element has been clicked, and send it to the serverside via fetch().
Problem: So far I managed to get the function sendSelectedPhotoId to be triggered when the div is clicked, but I cant figure out how to get the photo_id attribute of the view's Photo model. How should I achieve this?
On a side note, I'm not sure whether the correct photo_id will be send.
Code
$('#button').click( function() {
// Retrieve photos
this.photoList = new PhotoCollection();
var self = this;
this.photoList.fetch({
success: function() {
self.photoListView = new PhotoListView({ model: self.photoList });
$('#photo_list').html(self.photoListView.render().el);
}
});
});
Model & Collection
// Models
Photo = Backbone.Model.extend({
defaults: {
photo_id: ''
}
});
// Collections
PhotoCollection = Backbone.Collection.extend({
model: Photo,
url: 'splash/process_profiling_img'
});
Views
// Views
PhotoListView = Backbone.View.extend({
tagName: 'div',
events: {
'click .photo_container': 'sendSelectedPhotoId'
},
initialize: function() {
this.model.bind('reset', this.render, this);
this.model.bind('add', function(photo) {
$(this.el).append(new PhotoListItemView({ model: photo }).render().el);
}, this);
},
render: function() {
_.each(this.model.models, function(photo) {
$(this.el).append(new PhotoListItemView({ model: photo }).render().el);
}, this);
return this;
},
sendSelectedPhotoId: function() {
var self = this;
console.log(self.model.get('photo_id'));
self.model.fetch({
data: { chosen_photo: self.model.get('photo_id')},
processData: true,
success: function() {
}});
}
});
PhotoListItemView = Backbone.View.extend({
tagName: 'div',
className: 'photo_box',
template: _.template($('#tpl-PhotoListItemView').html()),
initialize: function() {
this.model.bind('change', this.render, this);
this.model.bind('destroy', this.close, this);
},
render: function() {
$(this.el).html(this.template( this.model.toJSON() ));
return this;
},
close: function() {
$(this.el).unbind();
$(this.el).remove();
}
});
SECOND ATTEMPT
I also tried placing the event handler and sendSelectedPhotoId in the PhotoListItemView where I managed to get the Model's attribute properly, but I can't figure out how to trigger the reset event when the PhotoList collection did a fetch().
View
PhotoListItemView = Backbone.View.extend({
tagName: 'div',
className: 'photo_box',
events: {
'click .photo_container': 'sendSelectedPhotoId'
},
template: _.template($('#tpl-PhotoListItemView').html()),
initialize: function() {
this.model.bind('change', this.render, this);
this.model.bind('destroy', this.close, this);
},
render: function() {
$(this.el).html(this.template( this.model.toJSON() ));
return this;
},
close: function() {
$(this.el).unbind();
$(this.el).remove();
},
sendSelectedPhotoId: function() {
console.log('clicked!');
var self = this;
console.log(self.model.get('photo_id'));
self.model.fetch({
data: { chosen_photo: self.model.get('photo_id')},
processData: true,
success: function() {
$(this.el).html('');
}});
}
});
Problem: With this, I cant seem to fire the reset event of the model after doing the fetch() in function sendSelectedPhotoId, which means I cant get it to re-render using PhotoListView's render().
In the screenshot below from Chrome's javascript console, I printed out the collection after sendSelectedPhotoId did its fetch(), and it seems like the fetched added the new data to the existing model, instead of creating 2 new models and removing all existing model!
You already have child views for each model, so I would put the click event handler in the child view. In the handler in the child, trigger an event passing this.model, and listen for that event in your parent.
Update based on update:
Try changing
this.model.bind('reset', this.render, this); to
this.model.bind('remove', this.render, this); // model is a collection right?
and then remove the model from the collection after the view is clicked. Also, I don't think using Model.fetch is what you really want to do. Maybe a .save or a custom method on the model?
Update based on author's comment showing sample base from blog
I would not follow that blog's advice. If you are using backbone professionally I can't recommend the Thoughtbot ebook enough.
It's $50 for a work in progress, and it's worth every penny
It has a simple sample application that lays out how to organize a backbone app. This is why I bought the book.
It uses Rails in the examples for the backend, but I have used Rails, Node, and C# MVC and all work no problem.

Categories