Backbone.js calling function render from event handler - javascript

I got my view like this :
render:function(){
this.template = _.template(tpl.get('tplUsersManagement'));
this.$el.html(this.template({models : this.model.models}));
this.$el.i18n();
$('#formAddUser')
.on('invalid', function () {
var invalid_fields = $(this).find('[data-invalid]');
console.log(invalid_fields);
})
.on('valid', this.addUser);
return this;
},
addUser: function(event){
event.preventDefault();
var newUser = new UserModel({
. . .
});
var that=this;
newUser.save({},{
headers:{"X-Token":"theToken"},
statusCode:{
202: function(){
that.render();//here I want to call render function
}
}});
}
}
So I want to call my render function from my addUser function. I try to do it with this=that and then that.render but I got an error and it says that :
Uncaught TypeError: Object # has no method 'render'
I think its because in the event handler this become my form.

You are already aware of the context constraints and that's why you are using the var that = this trick, but there is another place you need to implement it:
var self = this; // I prefer "self" rather than "that"
$('#formAddUser').on('invalid', function () {
// handle invalid data
}).on('valid', function( ev ){
ev.preventDefault();
self.addUser();
return this;
});
The addUser function is part of your view module, but within the valid/invalid event handlers, the context (the this variable) is changed. You'll need to keep a reference of the correct context (var self = this;) before entering the scope of the event handler so that it can be used to call your addUser() function.

There will be 2 options.
1.Use event delegation of backbone view. It will be let you use your view object in event handler.
2.Bind your addUser function to this.
addUser: function() {
...
//do something
...
}.bind(this)

Related

Redefining the context of an event handler

I'm creating a listener for the event show_keyboard. In the handler, I need to unbind from this event. In this case, the event is thrown from a native android plugin.
appView.sendJavascript("cordova.fireWindowEvent('native.showkeyboard', { 'keyboardHeight':" + Integer.toString(keyboardHeight)+"});");
In addHeightHandler, I need this to be the parent to addHeightHandler in order to unbind from it. Therefore I'm passing self when calling addHeightHandler. However, when I do this, I cannot get access to e and the keyboardHeight attribute.
Note: my employer insists it must be done like this, without anonymous functions or global setting of the self variable
/*
* When keyboard is shown, add height of keyboard to body to make scrollable.
*/
this.addHeightHandler = function (e) {
keyboardHeight = e.keyboardHeight;
//e is undefined
//do some stuff to add keyboard height
window.removeEventListener('show_keyboard', this.addHeightHandler);
};
/*
* Listen for showkeyboard events thrown by native code on Android
*/
this.addKeyboardListeners = function () {
var self = this;
window.addEventListener('native.showkeyboard', function () {
self.addHeightHandler(self)
}, false);
};
I know there are other ways of doing this, but this is the way I've been directed to do it. I believe passing self to addHeightHandler means e will be overwritten, is this correct?
Yes, the following:
this.addKeyboardListeners = function () {
var self = this;
window.addEventListener(eventConstants.nativeShowKeyboard, function () {
self.addHeightHandler(self);
});
};
Is passing self as the value of e.
What you can do is bind the addHeightHandler function to the desired context. In addKeyboardListeners, you could:
this.addKeyboardListeners = function () {
var handler = this.addHeightHandler.bind(this);
addEventListener(eventConstants.nativeShowKeyboard, handler);
};
What the above does is "bind" the context of the addHeightHandler function to this, meaning that when it is called, the this keyword inside the function will be a reference to whatever this was when you bound it.
The function will still take e as an argument, so when the event occurs and the handler is run, e will still be the event.

Referencing another function in array of functions

I'm using require.js and have a library of functions I use in multiple places. I define the functions thusly:
define(function (require) {
"use strict";
var $ = require('jquery');
var UsefulFuncs = {};
UsefulFuncs.hideAlert = function() {
$('.alert').hide();
};
UsefulFuncs.loadURL = function (url){
navigator.app.loadUrl(url, { openExternal:true });
return false;
};
UsefulFuncs.linkClicked = function (e) {
e.preventDefault();
var url = $(e.currentTarget).attr("rel");
this.loadURL(url);
};
return UsefulFuncs;
});
Then, in my backbone view, I call a function from the library with:
UsefulFuncs = require('app/utils/useful_func'),
....
UsefulFuncs.linkClicked
This works fine for any standalone function in the library e.g. hideAlert(). However when one function in the library refers to another, such as linkClicked() calling loadURL(), I get an error:
Uncaught TypeError: Object [object Object] has no method 'loadURL'.
Any ideas how I can reference loadUrl()?
I would assume you set UsefulFuncs.linkClicked as a handler for an event.
If you were to use it like this:
UsefulFuncs.linkClicked()
, this inside the function would refer to UsefulFuncs, so this.loadURL() would be valid.
However, since you set it as a handler for an event, it can be called in other ways, and this is set to some other object (probably the element which triggered the event). To avoid the event handling mechanism changing your this, you can bind the function when you assign it:
element.onclick = UsefulFuncs.linkClicked.bind(UsefulFuncs);
Another way to go around it is to avoid using this in the handler, like so:
UsefulFuncs.linkClicked = function (e) {
e.preventDefault();
var url = $(e.currentTarget).attr("rel");
UsefulFuncs.loadURL(url);
};
This creates a closure over UsefulFuncs (the first method does as well, but it's more standardized).
Try something like this:
define(function (require) {
"use strict";
var $ = require('jquery');
var hideAlert = function() {
$('.alert').hide();
};
var loadURL = function (url){
navigator.app.loadUrl(url, { openExternal:true });
return false;
};
var linkClicked = function (e) {
e.preventDefault();
var url = $(e.currentTarget).attr("rel");
loadURL(url);
};
return {
hideAlert : hideAlert,
loadURL : loadURL,
linkClicked : linkClicked
};
});

JavaScript - Call internal method from event handler

I apologize if this is a duplicate. I've tried finding a solution for this exact problem but couldn't come up with anything.
Here I have an Object literal:
var Facets = {
searchresults: {},
init: function() {
$('.class').on("click", function(e){
e.preventDefault();
Facets.searchAll();
Facets.getFacets();
});
},
searchAll: function(){
// Some ajax code
this.searchresults = data;
},
getFacets: function(){
$('.somenode').each(function(){
var url = this.makeURL(somedata);
});
},
makeURL: function(){
// creates URL
}
};
Facets.init();
The problem is when the method getFacets calls this.makeURL, I get an error. I'm not entirely sure why because I thought the call to getFacets has the context of the Facets object.
The way I understand it is that when the event handler is triggered, it has the context of the window. Therefore, I call Facets.getFacets and Facets.searchAll so that those functions will know what to call. But I know I'm not understanding something correctly...
I've also tried Facets.getFacets().call(this) to get the correct context, but that didn't work.
You can capture the scope like this:
var Facets = {
searchresults: {},
init: function() {
$('.class').on("click", function(e){
e.preventDefault();
Facets.searchAll();
Facets.getFacets();
});
},
searchAll: function(){
// Some ajax code
this.searchresults = data;
},
getFacets: function(){
var self = this;
$('.somenode').each(function(){
var url = self.makeURL(somedata);
});
},
makeURL: function(){
// creates URL
}
};
Facets.init();

Variable scope in Backbone.js

I have web application in Backbone.js that is using Stripe payment system.
This is part of the model code:
App.Models.Payment = Backbone.Model.extend({
defaults: {
id:null
},
url: function() {
// code
},
initialize:function(){
this.id='';
this.saveClicked= false;
this.saveDetailsChecked= true;
}
this model is used in this view:
App.Views.BillingView = Backbone.View.extend({
el: '#billing-fields',
events: {
'click #billing-footer-buttons .navigation .confirm-btn': 'saveCardClicked'
},
initialize:function(models, options) {
Stripe.setPublishableKey('**********************');
var self = this;
},
saveCardClicked:function(e) {
e.preventDefault();
if (this.model.saveClicked) return false;
this.model.saveClicked = true;
var $form = $('#payment-form');
Stripe.createToken($form, this.stripeResponseHandler);
},
cancelClicked:function() {
vent.trigger('showScreen', 'subscribe-container');
},
stripeResponseHandler:function(status, response) {
var $form = $('#payment-form');
self.saveDetailsChecked = document.getElementById("billing-checkbox").checked;
var atr1 = document.getElementById("billing-card-number").value;
var atr2 = self.model.savedCard;
if(document.getElementById("billing-card-number").value == self.model.savedCard){
self.model.set('use_saved_card','1');
vent.trigger('doPayment');
}else{
if (response.error) {
// code
} else {
// code
}
}
self.saveClicked = false;
}
});
In the saveCardClicked function I am able to access the variables from the model like the saveClicked variable.
But in the stripeResponseHandler I am not able to access the 'saveClicked' variable from the model, in this function this refers to window, and self variable that is defined in the initialize function cannot be accessed also.
stripeResponseHandler is called from the Stripe API.
Is there any way that I can access the savedCard variable in the stripeResponseHandler function or I should use global variable?
Try to use this:-
Stripe.createToken($form, this.stripeResponseHandler.bind(this));
Sure, you can use bind.
Bind works by binding parameters to a certain function call, and will return a function that, when invoked, will be passed the parameter that you declared when calling bind (plus, actually, any other expected parameter that you didn't declared). There is a special parameter to pass to bind. The first one, and it is the this object that will be used when the function is called.
So, what you would do is to call
Stripe.createToken($form, this.stripeResponseHandler.bind(this));
here you are declaring a callback (stripeResponseHandler) that when invoked, will have this as this object (I know the phrase is convoluted, but I think you got it).
Also, keep in mind that your line
var self = this;
is not working as well as it is scoped to the initialize function, thus not visible outside of it.

How do I access class variables from an event in Javascript?

I'm using the prototype javascript libraries. My code is below. I've created a class and in that class I have created an event listener for one of the class methods. In that method I need to access both the attributes of the form element that triggers the event and the class properties. Currently, the code understands $(this).getValue() in the event because this is being assigned to the form element that triggered the event. How can I access the this.makingRequest and this.user properties from the event function? Can I pass them in as parameters somehow? If so, how? Nothing I've tried to this point works. Thanks for any help.
function AddressBookEntryObj(user, selectField)
{
this.selectField = selectField;
this.user = user;
this.makingRequest = false;
this.debugMode = false;
Event.observe(this.selectField, 'change', handleEvent);
}
//Prototype statements
AddressBookEntryObj.prototype.handleEvent = handleEvent;
//End prototype statements
function handleEvent()
{
try
{
if (!this.makingRequest)
{
this.makingRequest = true;
var ajax =
new Ajax.Request(
'/servlet/MyReallyCoolServlet',
{
method: 'get',
parameters: {
action: 'doGet',
whichFunc: 'MyReallyCoolFunction',
fieldValue: $(this).getValue(),
user: this.user
},
onCreate: handleAjaxCreate,
onComplete: handleChangeComplete
}
);
return true;
}
else
{
return false;
}
}
catch (ex)
{
alert("handleEvent Error: \r\n" + ex.toString());
}
}
Thanks!
Andrew
jsfiddle example
Take a look at this jsfiddle I made with your code. You needed to add "bind(this)" to your callback handler. Doing that will make "this" be your object and use "e.target" to access the html element.
_Michael
Firstly use:
AddressBookEntryObj.prototype.handleEvent = function()
{
}
Then this inside handleEvent should point to AddressBookEntryObj. To be sure it will work everywhere inside that function use something like:
var _this = this;
And then use _this inside any event handlers.

Categories

Resources