JavaScript "this" references the wrong object within an event handler - javascript

I'm writing some MVC JavaScript code using Prototype. The problem is that when I reference this in the snippet below it does of course reference the Window object, whereas I need it to refer to my Controller instance so it can invoke my updateValue function, passing the HTML element ID.
I think I have to use bind or bindAsEventListener but I don't really understand how.
var Foo = {
Controller: Class.create({
observeEvents: function() {
$$("#myform input").each(function(input) {
input.observe("blur", this.updateValue(input.id); // <- wrong this!
});
},
updateValue: function(elementID) {
...
}
})
}
Any help would be much appreciated.

This should work.
observeEvents: function() {
var Controller = this;
$$("#myform input").each(function(input) {
input.observe("blur", Controller.updateValue.bind(Controller, input.id));
});
...
If you don't ever want to learn how to use bind (and frankly I don't blame you) then you can forget about it and just use another teensy closure.
observeEvents: function() {
var Controller = this;
$$("#myform input").each(function(input) {
input.observe("blur", function(event) {
Controller.updateValue(input.id);
});
});
...

Related

create jquery extension. problems with scope

I create a simple jQuery extension(it's my first).
(function($){
var MyClass = function(opt){
//..
};
//one of the methods of my extension
$.fn.myExtension = function(opt){
this._ext = new MyClass(opt);
return this;
};
$.fn.myExtensionOtherMethod = function(){
if(this._ext){
//do something ..
}
return this;
};
}(jQuery));
//using ..
$(document).ready(function(){
$('#selector').myExtension({
//options ..
});
$('#selector').myExtensionOtherMethod();
});
when i invoke method $('#selector').myExtensionOtherMethod();, this does not contains this._ext variable. I know that this is other scope, but i know that there is some way to access that variable in both methods.how can i do it?
This isn't really a scope issue. This is because the jQuery prototype $.fn gives you a jquery object as this. Even though you are selecting the same element each time its a new jQuery object set as the context so that property is gone. You can put that property on the DOM element and achieve the outcome you want.
(function($) {
var MyClass = function(opt) {};
//one of the methods of my extension
$.fn.myExtension = function(opt) {
this[0]._ext = new MyClass(opt);
return this;
};
$.fn.myExtensionOtherMethod = function() {
if (this[0]._ext) {
//do something ..
}
return this;
};
}(jQuery));
//using ..
$(document).ready(function() {
$('#selector').myExtension({
//options ..
});
$('#selector').myExtensionOtherMethod();
});
This is just a quick example above. If your selector finds more than one element you should loop though them. But I only grabbed the first index since you were selecting by ID.
Fiddle: https://jsfiddle.net/AtheistP3ace/gd1ehk0d/
As mentioned above by #charlietfl, I agree with that comment. Happy to explain why what you did didn't work but there may be better ways to achieve what you are looking for.

How to access an object/var from view1 but instantiated in view2 in Backbone js?

I am trying to access an object(this.historyComboBox) declared in a view(StatusView)'s render function and trying to access the same object from another view(HistoryView)'s extendedEvents function.
I have tried to use this.historyComboBox to access it but unable to hold any reference. Got really puzzled. If anyone has got some different idea I am ready to try it out!
Note: StatusView gets initialized prior to HistoryView.
Following is the code snippet.
StatusView = Backbone.View.extend({
init: function() {
//some code
},
render: function() {
this.historyComoBox = new sys.ComboBox();
}
}
HistoryView = Backbone.View.extend({
template: _.template(historyTemplate),
init: function() {
//some code
},
extendedEvents: {
'click #refreshButton': function() {
//want to access historyComoBox; not accessible with 'this.historyComoBox'
}
}
}
To get a property of a StatusView instance, you need a reference to that instance. So, if you have something like this:
var statusView = new StatusView();
Then from within the methods of HistoryView, you can do this:
statusView.historyComboBox;
However, while you can do it this way, I wouldn't access the StatusView instance directly like this. A better way would be to pass it to the HistoryView instance as a parameter, which you would receive in the initialize method. This keeps the views loosely coupled,
var HistoryView = Backbone.View.extend({
initialize: function (options) {
this.statusView = options.statusView;
},
events: {
'click #refreshButton': function () {
// use this.statusView;
}
}
});
(I notice you're using the names init and extendedEvents. You don't mention that you're using a third-party library with Backbone or something else that might change those, so I'll just mention that Backbone expects these to be initialize and events respectively.)

JS Controller call parent in callback?

I have a controller that is extended by a a childcontroller. This child needs needs to display an element first and after that raise the overriden parent function. Here is my abstracted code:
Ext.define('ParentController', {
extend: 'Ext.app.Controller',
//lot of code
myFunction(){
//....
this.getSomething();
}
}
and then there is a controller extending this existing one:
Ext.define('ChildController', {
extend: ParentController,
onlyChildFunction(dummy){
//create element
...
element.onClosed = function(){
dummy();
}
return element;
}
//override
myFunction(){
//this.callParent();
var element = onlyChildFunction( this.self.superclass.myFunction() );
element.show();
}
Passing the superclass function doesn't work because the ParentController is using a lot of references to a instance of ParentController (this).
I don't want to copy the code from ParentController directly, because it would mean to maintain the same function twice. One way could be to call myFunction() on element.onClosed again and make some kind of flag there only to do a callParent(). But this sounds ugly - any better suggestions?
Thanks in advance - help is appreciated!
If I understand correctly, onlyChildFunction must be passed a Function. (Here, you are passing the return value of calling the parent class's myFunction.)
Would something like this work ?
Ext.define("ChildController", {
...
myFunction : function () {
var that = this;
var element = onlyChildFunction( function () {
that.self.superclass.myFunction()
});
...
}
}
The idea being that you pass a function that calls the "parent" function in the right context.
In which case, you might be able to use the bind function to the same effect :
var element = onlyChildFunction(this.self.superclass.myFunction.bind(this));
But I'm not sure what the this / self / superclass values are in your case...

Calling a custom function in JQuery

I want to call a JavaScript function that I made after a JQuery event has been called. I defined a function called scrambleDot earlier like this var scrambleDot = new function()
{ //my code }. Here's the code that I tried to use:
$('#reveal').click(function() {
$('.cover').css({'visibility':'hidden'});
$('#under').css({'visibility':'visible'});
})
$('#conceal').click(function() {
$('scrambleDot');
})
})
You have to call it just like:
scrambleDot();
To define a function, you don't need the new operator, so you should have:
var scrambleDot = function() { //my code }
If it still throws an error, it means it was defined in other scope. To make it globally accesible, do this when defining it:
window.scrambleDot = function() { //my code }
Cheers
We have to use new keyword, only when the function is used as a constructor for new Objects. So, the definition should not use new.
var scrambleDot = function() { //my code }
If the function need not be created dynamically, I would recommend
function scrambleDot() {
...
}
To invoke the function, simply do
scrambleDot();
For that call the function instead of selecting an element as:
$('#reveal').click(function() {
$('.cover').css({'visibility':'hidden'});
$('#under').css({'visibility':'visible'});
})
$('#conceal').click(function() {
scrambleDot();
});
And also, you write functions as:
function scrambleDot () {
// your code
}
It is a better practice than the variable one.

Question regarding "this" inside anonymous function of prototype.js .each method

I've been searching for hours for a solution to this problem. I'm creating a table using prototype.js 1.6.0.1 and am having trouble with the this object in context with the .each function. here is a snippit.
var Table = Class.create({
initialize : function(id) {
this.elmnt = $(id);
this.rows = [];
},
initRows : function() {
$A(this._elmnt.tBodies).each(function(body) {
$A(body.rows).each(function(row) {
//right here is where i would like to call
// this.rows.push(row);
console.log(this); // prints DOMWindow
});
});
}
});
As you can see inside the second .each function this resolves to DOMWindow. I would like to be able to call this.rows.push(row) but I can't as "this" isn't resolving as expected.
Any help would be appreciated. I know i could do the standard (i=0; i < length; i++) loop but I was trying to make this a little cleaner. Thanks for any guidance you can offer.
The easiest way to work around this is to save this at the start of initRows and refer to in within the each functions
initRows : function() {
var self = this;
$A(this._elmnt.tBodies).each(function(body) {
$A(body.rows).each(function(row) {
//right here is where i would like to call
self.rows.push(row);
console.log(self); // prints DOMWindow
});
});
}
The problem you're running into is that this can be manipulated by the caller of the function. It's very common in callbacks to set this to an element which is relevant to the call back. In the case of each it's set to the element for the current iteration of the value.
The self trick works because it saves the this as it's bound in the function initRows and then uses that saved value in the iteration.
initRows : function() {
$A(this._elmnt.tBodies).each(function(body) {
$A(body.rows).each((function(e, row) {
e.rows.push(row);
console.log(e);
}).bindAsEventListener(this, row));
});
}

Categories

Resources