Here is what i have as the backbone code:
myEventModel = Backbone.Model.extend({});
var view_one = Backbone.View.extend({
initialize : function(){
myEventModel.on('change:someChange',function(){alert('Change observed for view one')});
}
render: function(){ $('body').append('view one')}
});
var view_two = Backbone.View.extend({
initialize : function(){
myEventModel.on('change:someChange',function(){alert('Change observed for view two')})
}
render: function(){ $('body').append('view two')}
});
So I have two views listening to a global model events. So when in console, I do
myEventModel.set({'someChange':new Date().getTime()});
i should see two alerts. But I don't.
jsfiddle
i've made some changes to your fiddle: http://jsfiddle.net/ya4t1c24/1/
you should create your model and views first, then events will fire.
myEventModel = Backbone.Model.extend({});
var view_two = Backbone.View.extend({
initialize : function(){
model.on('change:someChange',function(){alert('Change observed for view one')})
}
});
var view_one = Backbone.View.extend({
initialize : function(){
model.on('change:someChange',function(){alert('Change observed for view two')})
}
});
var model = new myEventModel;
var var_view_one = new view_one({model: model})
var var_view_two = new view_two({model: model})
model.set({'someChange':new Date().getTime()});
You are mixing the definition of the views/models with creating objects of those types.
After you have defined your model, you should create an object of that type:
var myEventModel = Backbone.Model.extend({});
var myEventModelInstance = new myEventModel();
Then in your views you want to listen to events fired from the object not from the model definition, so you should change your views as in:
var view_one = Backbone.View.extend({
initialize : function(){
myEventModelInstance.on('change:someChange',function(){alert('Change observed for view one');})
}
});
var view_two = Backbone.View.extend({
initialize : function(){
myEventModelInstance.on('change:someChange',function(){alert('Change observed for view two');})
}
});
Finally you need to create your view objects before changing the model object, as the view objects are the ones will react to the event, not the view definitions:
var viewOneInstance = new view_one();
var anotherViewOneInstance = new view_one();
var viewTwoInstance = new view_two();
myEventModelInstance.set({'someChange':new Date().getTime()});
With the code above you will see 2 alerts with the message 'Change observed for view one' because 2 view_one objects were created, and a single alert with the message 'Change observed for view two' as a single view_two object was created.
See updated fiddle
Related
I'm struggling with a design decision and looking for some feedback. I don't think this question is necessarily specific to Backbone, but that's the framework I'm currently using.
I'm wondering if it's considered bad practice to store the classname of a view as part of a model. For example, let's say you have a parent view with multiple subviews of different types. Each subview contains an edit link, and when that edit link is clicked, the parent view should update it's contents to contain the edit view for that model. I'm using an "event bus" to orchestrate events.
For example:
var E = _.extend({}, Backbone.Events);
var ParentView = Backbone.View.extend({
initialize: function(options) {
this.apples = options.apples; // Backbone Collection of Apple models
this.oranges = options.oranges; // Backbone Collection of Orange models
this.$appleList = this.$('#apples');
this.$orangeList = this.$('#oranges');
this.$editScreen = this.$('#edit-screen');
// listen to edit events for models and render the
// edit screen for using the appropriate view
this.listenTo(E, 'edit', this.showEditScreen);
},
template: 'templates/parent',
render: function() {
this.$el.html(this.model.toJSON());
this.renderAppleViews();
this.renderOrangeViews();
},
renderAppleViews: function() {
var view = new AppleListView({collection: this.apples});
this.$appleList.html(view.render().el);
},
renderOrangeViews: function() {
var view = new OrangeListView({collection: this.oranges});
this.$orangeList.html(view.render().el);
},
// Show the edit screen for a particular model
showEditScreen: function(model) {
var view = new window[model.editScreenViewClass]({model: model}):
this.$editScreen.html(view.render().el);
}
});
AppleListView and OrangeListView simply loop through their respective collections and append a view to the list.
AppleListView adds AppleItemViews, and OrangeListView adds OrangeItemViews. I'm showing the relevant parts of those views below:
var AppleItemView = Backbone.View.extend({
events: {
'click .edit': 'onEditClick'
},
onEditClick: function(e) {
e.preventDefault();
E.trigger('edit', this.model);
}
});
var OrangeItemView = Backbone.View.extend({
events: {
'click .edit': 'onEditClick'
},
onEditClick: function(e) {
e.preventDefault();
E.trigger('edit', this.model);
}
});
Here is what the models would look like for this to work:
var Apple = Backbone.Model.extend({
editScreenViewClass: 'AppleEditView'
});
var Orange = Backbone.Model.extend({
editScreenViewClass: 'OrangeEditView'
});
I'm asking if it seems "ok" to store this editScreenViewClass on the model. That way I can retrieve it directly from the model passed into the event.
I'm trying to learn Backbone.js and for this I now want to load a collection of models into a view. By opening a tab in the window, I first add the following template:
<script type="text/template" id="tab-content-template">
<div class="conversation-window" id="conversation<%= ticketId %>"></div>
</script>
In this tempalte I now want to load a Collection of messages belonging to the ticketId. So I made a collection like this:
var MessageCollection = Backbone.Collection.extend({
url: 'ticket/:id/messages'
});
and a view:
var MessageView = Backbone.View.extend({
initialize: function(models, options) {
this.ticketId = options.ticketId;
},
el: function() {
return '#conversation' + this.ticketId;
},
className: 'user-message'
});
So I want the list of messages to be inserted within the #conversation1 (for ticketId 1). I then tried running this:
var messageView = new MessageView(messageCollection, {ticketId: 1});
messageView.render();
console.log(messageView);
Unfortunately nothing happens, and when I look into the console I see that ticketId: 1 but that el: undefined. I'm kinda lost in what I'm doing wrong (kinda lost in Backbone in general).
Does anybody know what I'm doing wrong here and how I can solve it? All tips are welcome!
I think this is what you want:
<div id = "conversation1">stuff insert here</div>
<script>
var MessageCollection = Backbone.Collection.extend({
// url: 'ticket/:id/messages' //<-- put this in router
});
// you need to create an instance of you collection somewhere and pass in
// models as parameter. note that your ticketId is included in the models:
var messageCollection = new MessageCollection([{ticketId:1,attr:'stuff1'}, {ticketId:2,attr:'stuff1'}])
var MessageView = Backbone.View.extend({
initialize: function(models, options) {
this.ticketId_1 = this.collection.models[0].get('ticketId');
this.ticketId_2 = this.collection.models[1].get('ticketId');
},
// can not reference el and define a <div id="user-message"> simultaneously
/*
el: function() {
return '#conversation' + this.ticketId;
},
*/
className: 'user-message',
render: function() {
$('#conversation'+ this.ticketId_1).html(this.$el.html('stuff from model goes in here'));
}
});
// var messageView = new MessageView( messageCollection, {ticketId: 1});
// the above wouldn't work. your ticketId should be passed into your view via model
// then you associate you view to your collection like so:
var messageView = new MessageView({ collection: messageCollection });
messageView.render();
console.log(messageView.$el.html());
</script>
I have a series of comments on a page, which can be edited. My idea was to render the comments by Rails and preload a json with all those comments in a Backbone Collection.
Then I would poll every x seconds, to see if there are changes. Normally I render the collection by looping over all the models, and create a view for each item. When a model gets updated, so will the view (comment im this case).
But my question is this, how do you bind a view to the model, when the view is already in the DOM. Especially since the view had a dynamic id. There is no point in rendering the view, since it's already there. When you render a view, backbone binds it through somekind of cid.
The only solution I can think of, is by setting an id in the dom object on pageload. iow
<div id="comment-<%= content.id %>"></div>
. And then in the initialize of the view, reset the id
class Comment extends Backbone.View
initialize: ->
#id = "comment-" + #model.get('id')
But I'm not sure if thats the way to go. Would events still be binded?
Special for you :)
var CommentsView = Backbone.View.extend({
tagName : 'ul',
comments : {},
initialize : function () {
this.listenTo(this.collection, 'add', this.addComment);
this.listenTo(this.collection, 'remove', this.removeComment);
this.listenTo(this.collection, 'change', this.updateComment);
},
addComment : function (model) {
this.comments[model.id] = new CommentView({model:model});
this.$el.append(this.comments[model.id].render().$el);
},
removeComment : function (model) {
this.comments[model.id].remove();
this.comments[model.id] = null;
},
updateComment : function (model) {
this.comments[model.id] = new CommentView({model:model});
this.$('[data-id="' + model.id + '"]').before(this.comments[model.id].render().$el).remove();
}
});
var CommentView = Backbone.View.extend({
tagName : 'li',
template : _.template('<div data-id="<%= id %>"><%= name %>: <%- message %></div>'),
render : function () {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
// comments
var initialComments = [{id:1, name:'user1', message:'great!'}, {id:2, name:'user2', message:':)'}];
var actualComments = [{id:1, name:'user1', message:'great! [edited]'}];
var comments = new Backbone.Collection();
var commentsView = new CommentsView({collection:comments});
// show comments
commentsView.render().$el.appendTo('body');
// simulate fetch
comments.add(initialComments);
// simulate update
_.delay(function() {
comments.update(actualComments);
},
2000);
jsfiddle
I am using backbone.js with ASP.NET MVC 4.
I want to call methods of different view from one of the view. To make this simpler to understand I have created a small example below.
Here in the MyView2 in side the OperationCompleted method I want to call the following...
call myMethodB of MyView 2
call myMethodA of MyView 1
call myMethodC of AppView
How do I do this ? I have temporarily used something like creating objects of view and calling them.
Something like this var view1 = new MyView1(); and then view1.myMethodA();, there has to be a better way, Please help me find it. Thanks
var MyModel = Backbone.Model.extends({
});
// View for a Main Grid
var MyView1 = Backbone.View.extend({
...
myMethodA: function(){
// do something with View 1
}
...
});
// View for subgrid in Main Grid
var MyView2 = Backbone.View.extend({
...
myMethodB: function(){
// do something with View 2
},
OperationCompleted: function(){
// call myMethodB of MyView 2
// call myMethodA of MyView 1
// call myMethodC of AppView
}
...
});
var AppView = Backbone.View.extend({
...
myMethodC: function(){
// do something with App View
}
...
});
Got this working ! had to use the Aggregator pattern, have pasted below a sample example of how I used it...
Backbone.View.prototype.eventAggregator = _.extend({}, Backbone.Events);
var model = Backbone.Model.extend({
});
var view1 = Backbone.View.extend({
initialize: function () {
this.eventAggregator.bind("doSomething_event", this.doSomething);
},
doSomething: function(name){
alert("Hey " + name + " !");
}
});
var view2 = Backbone.View.extend({
callToDoSomething: function(){
self.eventAggregator.trigger("doSomething_event", "Yasser");
}
});
References
https://stackoverflow.com/a/11926812/1182982
Another pattern here would be to call a view's function by triggering an event on the DOM element the view is attached to.
For example:
var AppView = Backbone.View.extend({
el: 'body',
events: {
'alertMe': 'alertMe'
},
alertMe: function(ev, arg){
console.log(args)
alert("You've been alerted!")
}
});
Then at some point later in your code (even in another view):
// Shows an alert and prints object.
$('body').trigger('alertMe', { foo: 'bar' });
Here is my content.js in which i am using backbone.js for rendering contents.
// Our basic **Content** model has `content`, `order`, and `done` attributes.
var Content = Backbone.Model.extend({
// If you don't provide a Content, one will be provided for you.
EMPTY: "empty Content...",
// Ensure that each Content created has `content`.
initialize: function() {
}
});
var ContentCollection = Backbone.Collection.extend({
model : Content
});
// Create our global collection of **Todos**.
window.Contents = new ContentCollection;
// The DOM element for a Content item...
var ContentView = Backbone.View.extend({
//... is a list tag.
tagName: "li",
events: {
"click .content": "open"
},
// a one-to-one correspondence between a **Content** and a **ContentView** in this
// app, we set a direct reference on the model for convenience.
initialize: function() {
_.bindAll(this, 'render', 'close');
this.model.bind('change', this.render);
this.model.view = this;
},
// Re-render the contents of the Content item.
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
Here is how i am making the list of content and rendering them.
for(var i=0; i<data.length; i++) {
var content = new Content(data[i]);
var templ=_.template($('#tmpl_content').html());
var view = new ContentView({model: content});
view.template=templ;
$("#content").append(view.render().el);
}
my question is how can i get the contetnt model listing .
as i have created the collection
var ContentCollection = Backbone.Collection.extend({
model : Content
});
// Create our global collection of **Todos**.
window.Contents = new ContentCollection;
So when i do watch Contents it shows length 0 and models [] .
how contetnt will get added in the collection . or how to see list of model in backbone.js
You need to Collection.add(models) before it will contain anything.
You also could specify a URL (which should return a JSON array of models) on your collection and then do window.Contents.fetch(). Backbone will auto-populate the model (Content) specified in your collection and automatically add them to your collection.