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')
});
});
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);
}
});
I am creating a model on click and was wondering where is the best place to listen for validation events on the model? I create the model, set the input value on the attribute then create a listener, it doesn't feel right having this listener in the click handler but maybe I'm wrong?
JS
buttonClicked: function(event) {
var input = this.$('.js-input'),
itemValue = input.val(),
model = new Item({
item: itemValue
}, {
validate: true
});
this.listenTo(model, 'invalid', this.onFormError, this);
}
Link to codepen http://codepen.io/styler/pen/rearYp
It depends what you do with the model. If you want to display it in a new view, the validation should be inside the initialize of the new view.
var view= Backbone.View.extend({
model:Item,
initialize: function (model, options) {
this.listenTo(this.model, 'invalid', this.onFormError, this);
},
......
}
If you use it locally you can let it on the click handler.
Try this ...............
var view= Backbone.View.extend({
model:Item,
initialize: function (model, options) {
this.model= new Item();
this.listenTo(this.model, 'invalid', this.onFormError, this);
this.render();
},
buttonClicked: function(event) {
var input = this.$('.js-input'),
itemValue = input.val(),
this.model.set('item', itemValue);
if(!this.model.isValid(true)){
return;
}
}
......
}
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 have a problem with backbone. I have two models: A and B
I need to listen to events in one model (for instance A), and then after the event has happened make changes in the view of model B and its view.
Does anyone have a fairly simple example how such functionality can be implemented in backbone?
var Model_A_View= Backbone.View.extend({
events: {
// some events;
"click" : "ok",
},
initialize: function () {
this.Model_A = new Model_A({ // });
}
var Model_B_View= Backbone.View.extend({
events: {
// some events;
},
initialize: function () {
this.Model_B = new Model_B({ // });
this.listenTo( model_A , "change: ok", this.dosomethingFunction());
}
dosomethingFunction: function () {
//dosomething
}
Your code hard to read, no code style, and some synax error.
However, you'd better create model outside of you view's initialize function, and pass the model as a para when new a view:
var modelA = new Model_A();
var viewA = new Model_A_View({
model: modelA
});
var modelB = new Model_B();
var viewB = new Model_B_View({
model: modelB
});
viewB.listenTo(modelA, "change:ok", viewB.dosomethingFunction);
As variant you can use The Backbone Events. Few simple steps, how you can do this.
1.Specify events global object:
window._vent = _.extend({}, Backbone.Events);
2.Specify event triggers in your view/model/collection. For example for your view:
var Model_A_View = Backbone.View.extend({
events: {
// some events;
"click" : "ok",
},
initialize: function() {
this.Model_A = new Model_A({});
},
ok: function() {
vent.trigger('model_A:update', this.model); //you can also send your model
}
});
3.Specify event listening in your Model_B:
var Model_B_View = Backbone.View.extend({
initialize: function() {
this.Model_B = new Model_B({});
vent.on('model_A:update', this.updateModel, this);
},
updateModel: function() {
//this function will be call after `model_A:update` event triggered
//do something with Model_B
}
});
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);
}
});