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);
}
});
Related
need help, can't understand how to attach each View of the model to each already existing DIV in DOM ( have and div.container with div.widget array ).
// Model
V.Models.Shortcode = Backbone.Model.extend({});
// Shortcodes Collection Init
V.Collections.Shortcodes = Backbone.Collection.extend({
model: V.Models.Shortcode,
});
When Iframe load, push storage from server to collection:
$('#preview').on('load', function() {
var ShortcodesCollection = new V.Collections.Shortcodes( Vision.ShortcodeStorage );
var Preview = new V.Views.Preview({
collection: ShortcodesCollection,
el: $('.container')
});
Preview.render();
});
Render Preview with collection:
// Collection View in iframe
V.Views.Preview = Backbone.View.extend({
initialize: function() {
this.collection.on('add', this.addOne, this);
},
render: function() {
this.collection.each(this.addOne, this);
return this;
},
addOne: function(ad) {
var shortcodeView = new V.Views.Shortcode({ model: ad });
shortcodeView.render();
}
});
View for each Model:
// Shortcode View
V.Views.Shortcode = Backbone.View.extend({
events: {
'click .widget' : 'SomeActionOnView'
},
render: function(){
//console.log(this.el);
//console.log(this.model.toJSON());
},
SomeActionOnView: function(){
console.log(this);
}
});
Question is, how to attach V.Views.Shortcode to each div with "widget" class to bind events. Thanks!
Can you please try this?
V.Views.Shortcode = Backbone.View.extend({
events: {
'click .widget' : 'SomeActionOnView'
},
render: function(){
//code here to write stuff of this.$el
$("div.widget").append(this.$el);
},
SomeActionOnView: function(){
console.log(this);
}
});
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,
On the Model initialization it sets some params. On the View initialization is the listener which must catch that event. But it doesn't. However it can do that on following events (like a button's click). So questions:
Is it normal behavior?
If yes then what is purpose of that
If not, what is my error?
The place to play is here: http://plnkr.co/edit/ddIZrkoI1n1CE6YrvMrC?p=preview
$(function(){
var Mod = Backbone.Model.extend({
defaults:{
test:null
},
initialize: function(){
this.set('test', 'hello');
console.log(this.attributes);
}
});
var Vw = Backbone.View.extend({
events : {
'click #btn-change' : function(){
var mod_value = $('[name="data-input"]').val();
console.log('mod_value: '+mod_value);
this.model.set('test', mod_value);
}
},
initialize: function(){
console.dir({
model: this.model,
view: this
});
this.listenTo(this.model, 'change', function(){
console.log('%cModel changed', 'font-size:18px; color: navy');
console.log({
model_changed: this.model.changed,
keys: _.keys(this.model.changed),
attributes: this.model.attributes
});
$('#new-data').html('<h4>Model is changed</h4>'+this.model.get('test'));
});
}
});
var md = new Mod(), nv = new Vw({
model: md,
el:$('body')
});
});
If you need that set event in model's initialization, as model.changed won't change until next set, and your handler doesn't depend on the params given by the event, you can either manually call the change event handler in your view's init, as Altered Plunker, or use model.trigger('change', ...) to trigger the change handler.
$(function(){
var Vw = Backbone.View.extend({
events : {
'click #btn-change' : function(){
var mod_value = $('[name="data-input"]').val();
console.log('mod_value: '+mod_value);
this.model.set('test', mod_value);
}
},
initialize: function(){
console.dir({
model: this.model,
view: this
});
// Pull the handler out as an attr of view's
this.listenTo(this.model, 'change', this.onModelChanged);
// Manually call the handler to get the changes made by mode's
// initialization.
this.onModelChanged();
},
onModelChanged: function(){
console.log('%cModel changed', 'font-size:18px; color: navy');
console.log({
model_changed: this.model.changed,
keys: _.keys(this.model.changed),
attributes: this.model.attributes
});
$('#new-data').html('<h4>Model is changed</h4>'+this.model.get('test'));
}
});
var Mod = Backbone.Model.extend({
defaults:{
test:null
},
initialize: function(){
this.set('test', 'hello');
console.log(this.attributes);
}
});
var md = new Mod(), nv = new Vw({
model: md,
el:$('body')
});
});
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()
I make a simple todo app:
var Todo = Backbone.Model.extend({
});
var Todos = Backbone.Collection.extend({
model: Todo
});
var todos = new Todos();
var ItemView = Backbone.View.extend({
tagName: "li",
template: _.template($("#item-template").html()),
render: function () {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
initialize: function () {
this.listenTo(todos, 'remove', this.remove);
},
events: {
"click .delete": "clear"
},
clear: function () {
todos.remove(this.model);
}
});
var AppView = Backbone.View.extend({
el: $("body"),
initialize: function () {
this.listenTo(todos, 'add', this.addOne);
},
addOne: function(todo) {
var view = new ItemView({
model: todo
});
this.$("#list").append(view.render().el);
},
events: {
"click #create": "create"
},
create: function () {
var model = new Todo({
title: this.$("#input").val()
});
todos.add(model);
}
})
var app = new AppView();
and DEMO online is here: http://jsfiddle.net/JPL94/1/
I can add item correctly, but when I want delete some item, all of them been removed;
I found it related to the bind event in ItemView, when I click one delete button, all of them are triggered.
But how can I solve this problem?
You are listening to remove events from the collection, and if my memory serves me right a collection will dispatch a remove event whenever a model is removed, so when you remove a model from the collection, all the views will see the event.
I changed your initialize in the view to
initialize: function () {
this.listenTo(this.model, 'remove', this.remove);
},
And it seems to work.
http://jsfiddle.net/JPL94/5/