Raising a Backbone.js View event - javascript

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);

Related

Is there a way to console.log all event occuring inside Backbone Marionette ItemView

I have a backbone marionette view in which i have defined many events.
I want to store a log for any event which is occuring inside my itemview. I dont want to write function calling for my audit data on every data, instead i had like to override backbone marionette events method so that it is applied on all my ItemViews.
I tried using:
var originalTrigger = Backbone.Events.trigger;
Backbone.Events.trigger = function(){
console.log("Event Triggered:");
console.log(arguments.join(", "));
originalTrigger.apply(this, arguments);
}
But it doesnt do anything for me. Please help
Thank you in advance
If you check the Backbone source you'll see things like this:
_.extend(Model.prototype, Events, {
where Model and Events are local aliases for Backbone.Model and Backbone.Events respectively. That means that the methods from Backbone.Events will have been copied to the model, collection, and view prototypes before you have a chance to wrap Backbone.Events.trigger with your auditing.
You'll want to wrap all four triggers after Backbone is loaded but before anything else (including Marionette) is loaded. Something like this:
(function() {
var trigger = Backbone.Events.trigger;
var wrapper = function() {
console.log("Event Triggered:");
console.log(arguments.join(", "));
trigger.apply(this, arguments);
};
Backbone.Model.prototype.trigger = wrapper;
Backbone.Collection.prototype.trigger = wrapper;
Backbone.View.prototype.trigger = wrapper;
})();
after <script src="backbone.js"> but before <script src="backbone.marionette.js"> should do the trick.

How to pass back model attribute from a view Backbone

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.

Backbone view get model variable after model has finished fetch

In Backbone.js, how can i retrieve a veriable that is returned by a model function after the fetch has completed? As I understand the model cannot communicate with the view, so how can the view listen in on the model specific function fetch?
Thanks!
My attempt to do this can be seen below (if you see code mistakes, don't mind them, I wrote this as an example of what I'm trying to do):
var ScheduleModel = Backbone.Model.extend({
urlRoot: '/api/schedule/1',
getSubjectData: function(){
this.fetch({
success: function(data, scheduleData){
return scheduleData;
}
});
}
});
var ScheduleView = Backbone.View.extend({
initialize: function(){
console.log(this.model.getSubjectData());
}
});
You can listen to several model events with listenTo:
http://backbonejs.org/#Events-listenTo
since model.fetch triggers the 'change' event (http://backbonejs.org/#Model-fetch) somewhat similar in your view code:
var ScheduleView = Backbone.View.extend({
initialize: function(){
console.log(this.model.getSubjectData());
this.listenTo(this.model, 'change', this.doSmthng);
},
doSmthng: function () {
// ....
}
});
should fire the doSmthng models' fetch is done.
You can do fetch inside view like this.
var ScheduleView = Backbone.View.extend({
initialize: function(){
this.model.fetch({success: function() {
//you can do your stuff here.
//Try to get model data using `model.get`.
}});
}
}
and,
As I understand the model cannot communicate with the view.
This is wrong. You can set like this inside your view.
this.model.view = this;
and you can access view in your model as like this.
this.view
But in my apps i am not doing this. Accessing view inside model will collapse the purpose of backbone.

Passing data into the render Backbone.js

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.

backbone.js: how to bind render

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.

Categories

Resources