I have a controller:
var layout = new LayoutView();
App.holder1.show(layout);
var my_view = new myView({id: options})
layout.holder1.show();
console.log(my_view.model.get('name')) <---- I want this
I want to get my_view.model.get('name') however, the issue is I get undefined. I have console.log the model and it is populated ok, however I think it's because it's not fully loaded yet when I try the get.
This is my current thisView:
var thisView = Backbone.Marionette.ItemView.extend({
initialize: function (options) {
this.model.fetch();
},
model: new myModel(),
template: testExampleTemplate,
});
return thisView;
You'll have the object populated only after the success callback function:
initialize: function (options) {
this.model.fetch({
success: function(model){
console.log(model.get('name'));
};
});
}
Listen for an event. "change" or "reset" will work.
viewInstance.model.on("change", function(){
viewInstance.model.get("nameOfAttribute");
// do something
});
http://backbonejs.org/#Events-catalog
There's a few ways to approach this. First, you could listen for a change event from the model in the view, and do whatever it is you need when the change event fires. If you need to do something no matter what, you have a couple of options: you could write an implementation for you model's parse method that fires an event your view listens for and does something in response, or you can do something in the success callback for the fetch method itself (passed as an option to fetch). I can provide an example if I understand better which approach makes sense for your situation.
Related
is it possible to pass the questions variable into the view render?
Ive attempted calling this.render inside the success on the fetch however I got an error, presumably it's because this. is not at the correct scope.
app.AppView = Backbone.View.extend({
initialize: function(){
var inputs = new app.Form();
inputs.fetch({
success: function() {
var questions = inputs.get(0).toJSON().Questions;
app.validate = new Validate(questions);
app.validate.questions();
}, // End Success()
error: function(err){
console.log("Couldn't GET the service " + err);
}
}); // End Input.fetch()
this.render();
}, // End Initialize
render: function(){
el: $('#finder')
var template = _.template( $("#form_template").html(), {} );
this.$el.html(template);
}
The success callback is called with a different this object than your View instance.
The easiest way to fix it is to add something like this before you call inputs.fetch:
var self = this;
And then inside the success callback:
self.render();
I'm not quite sure what you're trying to achieve, but if your problem is calling render from the success callback, you have two options, Function#bind or assigning a self variable.
For more information about "self" variable, see var self = this? . An example:
var self = this;
inputs.fetch({
success: function () {
self.render();
},
...
});
You should probably do some reading on JavaScript scopes, for example "Effective Javascript" or search the topic ( for example this MDN article ) online to get a better idea what happens there.
For Function#bind(), see the MDN article about it. With Backbone I suggest you use Underscore/LoDash's _.bind instead though, to make sure it works even where Function#bind() is not supported.
As for more high-level concepts, the fetching operation looks like it belongs to the model or router level and instead you should assign the questions variable as the model of your view. Ideally views don't do data processing / fetching, they're just given a model that has the methods necessary to perform any data transformations you might need.
The views shouldn't even need to worry about where the model comes from, this is normally handled by a router in case of single page applications or some initialization code on the page.
The following code works but I would like to improve readability and accessibility avoiding to write callbacks.
I need to render my view when fetch is performed on my collection.
Here the working code:
var MyView = Backbone.View.extends({
initialize: function()
{
var that = this;
MyCollection.fetch({
success: function () {
that.render();
}
});
},
....
});
Here my attempt which does not work:
var MyView = Backbone.View.extends({
initialize: function()
{
MyCollection.fetch();
MyCollection.bind('change', this.render);
},
....
});
Looks like you need to set the context for the call to bind. Like this:
MyCollection.bind('change', this.render, this);
One excellent thing about Coffeescript is that it takes care of these things much more cleanly.
ETA: the change event isn’t triggered on fetch, it’s only triggered when one of the models in the collection changes. reset is, though. Also, you’re binding to the event after triggering the fetch, not sure if that’s what you intend.
Aside: seems confusing to me that you’re capitalising the MyCollection member, makes it easily mixed up with a class.
I'm not sure if I'm doing this right, first time playing with Backbone.js.
I have two views with two models and I want to use the event aggregator method to fire events between the two.
The aggregator declaration:
Backbone.View.prototype.eventAggregator = _.extend({}, Backbone.Events);
So in one view I have a line like this that will fire the removeRow method.
this.eventAggregator.trigger("removeRow", this.row);
In another view
MyView = Backbone.View.extend({
initialize: function() {
this.eventAggregator.bind("removeRow", this.removeRow);
this.model.get("rows").each(function(row) {
// Do stuff
});
},
removeRow: function(row) {
// row is passed in fine
// this.model is undefined
this.model.get("rows").remove(row);
}
});
I think I understand why this.model is undefined, but what can I do to maintain a reference so that I can use this.model in the callback? I thought about passing the model to the first view and then passing it back in the trigger call, but that seems to make the entire point of an event aggregator pointless. If I have the model I can just call the .remove method directly and have lost the benefit of my first view being unaware of the model. Any suggestions?
I think you have binding problem.
You have two ways to assure that this will be the View instance:
1. Using bindAll
In your View.initialize() you can add this line:
_.bindAll( this, "removeRow" )
Interesting post of #DerickBailey about this matter
2. Using the optional third argument in your bind declaration
Like this:
this.eventAggregator.bind("removeRow", this.removeRow, this);
Backbone documentation about this matter
Supply your View object as third parameter of the bind method:
this.eventAggregator.bind("removeRow", this.removeRow, this);
The third parameter is the context of calling your callback. See the docs.
Also, you can use .on() instead of .bind() which is shorter...
You need to bind this so scope isn't lost. The blog link on the other answer uses underscore's bindAll
initialize: function() {
_.bindAll(this, 'removeRow');
this.eventAggregator.bind("removeRow", this.removeRow);
this.model.get("rows").each(function(row) {
// Do stuff
});
},
I have been using Backbone on a new project and so far have loved it, but I have come to a blocker that I can't seem to get around.
Without trying to explain my whole domain model, I have found that when you save a model, the response comes back from the server and gets parsed again, creating new sub objects and therefore breaking and event bindings I had previously put on the object.
For instance, if I save ContentCollection (its a Backbone.Model not a collection) when it comes back from the server, the response gets parsed and creates a new Collection in this.contentItems, which breaks all the binding I had on this.contentItems. Is there any way to get around this? Tell backbone not to parse the response somehow? Grab the bindings off the original list, and then re-attach them to the new list?
App.ContentCollection = Backbone.Model.extend({
urlRoot: '/collection',
initialize: function() {
},
parse: function(resp, xhr) {
this.contentItems = new App.ContentList(resp.items)
this.subscriptions = new App.SubscriptionList(resp.subscriptions)
return resp
},
remove: function(model){
this.contentItems.remove(model)
this.save({'removeContentId':model.attributes.id})
},
setPrimaryContent: function(model){
this.save({'setPrimaryContent':model.attributes.id})
}
})
Has anyone run into this before?
I think the issue here is the way you're using the parse() method. Backbone just expects this method to take a server response and return a hash of attributes - not to change the object in any way. So Backbone calls this.parse() within save(), not expecting there to be any side-effects - but the way you've overridden .parse(), you're changing the model when the function is called.
The way I've dealt with this use case in the past is to initialize the collections when you first call fetch(), something like:
App.ContentCollection = Backbone.Model.extend({
initialize: function() {
this.bind('change', initCollections, this);
},
initCollections: function() {
this.contentItems = new App.ContentList(resp.items);
this.subscriptions = new App.SubscriptionList(resp.subscriptions);
// now you probably want to unbind it,
// so it only gets called once
this.unbind('change', initCollections, this)
},
// etc
});
I'd like to find a way to raise a backbone.js "event" without something having changed in the model or in the dom.
For instance, I'm loading the Facebook SDK asynchronously. I'm subscribed to the auth.login event, and would like to send a message to my view that the user has logged in so it can re-render itself appropriately.
My view looks similar to this:
window.CreateItemView = Backbone.View.extend({
el: $('#content'),
initialize: function() {
this.render();
},
render: function() {
// do something
return this;
},
setSignedRequest: function(signedRequest) {
//do something with signedRequest
}
});
In my facebook code, I do this:
FB.Event.subscribe('auth.login', function(response){
if (response.status === 'connected') {
var uid = response.authResponse.userID;
var accessToken = response.authResponse.accessToken;
window.signedRequest = response.authResponse.signedRequest;
if (window.view && window.view.setSignedRequest) {
window.view.setSignedRequest(window.signedRequest);
}
}
});
However, while window.view exists, it cannot see the setSignedRequest method. I've ensured that my scripts are loading in the correct order. Strangely I have this same code on a different page albeit a different View object and it works fine. I haven't seen any difference that would account for this.
A better solution would be to raise some sort of event and have the view listen for it. However, I don't want to utilize the change event on the model as the signedRequest shouldn't be a property of the model. Is there a better way of accomplishing this?
Backbone.View is extended with Backbone.Events which means you can easily trigger custom events and pass whatever data you want
var View = Backbone.View.extend({
initialize: function() {
this.on('customEvent', this.doSomething, this);
}
doSomething: function(someData) {
// this!
}
});
var view = new View();
view.trigger('customEvent', "someDataHere");
though I don't know why you can't see the method on the view - it should work - Are you 100% sure you are instantiating the view correctly? and that window.view is instance of CreateItemView ?
If you want to do that outside your model and subscribe to the event on your view you could do the following:
var SomeObject = {...};
_.extend(SomeObject, Backbone.Events);
Then, you can subscribe to events on SomeObject
SomeObject.on('myevent', someFunc, this);
or trigger the event
SomeObject.trigger('myevent', data);