Passing instance classes in WinJS - javascript

I'm trying to pass a class instance about so that I can persist its member variables. I have the following code
var mainNamespace = WinJS.Namespace.define("MainNamespace", {
setupClass: WinJS.Class.define(
function () { },
{
createSetup: function CreateSetup() {
var interactionInst = new mainNamespace.interaction();
drawScreen.DrawScreen(interactionInst);
var backgroundProc = new
mainNamespace.createProc(interactionInst);
}
),
interaction: WinJS.Class.define(
function() {},
{
clickedPos: 0,
handleTouch: function handleTouch(event) {
this.clickedPos = event.x;
console.log("New pos: " + this.clickedPos);
}
),
createProc: WinJS.Class.define(
function (interaction) {
setInterval(this.runProc, 1000, interaction);
},
{
runProc: function runNewProc(interaction) {
console.log(interaction.clickedPos);
}
...
The drawScreen namepsace is as follows:
WinJS.Namespace.define("drawScreen", {
DrawScreen: WinJS.Class.define(
function DrawScreen(interaction) {
/// Do some screen set-up here
canvas.addEventListener("MSPointerUp", interaction.handleTouch, false);
}
)
});
The problem I have is that the interaction.clickedPos never changes. Why does it not change, and am I going about this the right way for a javascript app?
EDIT:
I've now worked out WHY this is happening, but not how to fix it. When interaction.handleTouch fires, this refers to the canvas object, and NOT the interaction object - so I have no access to it's members.

The problem is on the following line:
canvas.addEventListener("MSPointerUp", interaction.handleTouch, false);
here you are passing a reference to the function handleTouch() without any connection to the current instance (interaction). You can change the context of the event handler by using the .bind() method:
canvas.addEventListener("MSPointerUp", interaction.handleTouch.bind(interaction), false);

Related

Can't remove event listener for windows object

I am having a lot of trouble trying to remove an event listener.
I have created a website that relies on JavaScript quite heavily. When you navigate on the website it is basically loading in elements dynamically without a page refresh with template literals.
I have to sometimes load in content and add infinite scroll but also be able to remove that event again.
This is the code I use to handle scroll events:
var start = 30;
var active = true;
function yHandler(elem)
{
var oHeight = selectElems('content_main', 'i').offsetHeight;
var yOffset = window.pageYOffset;
var hLimit = yOffset + window.innerHeight;
if (hLimit >= oHeight - 500 && active === true)
{
active = false;
new requestContent({
page: GET.page,
type: returnContentType(GET.page),
scroll: true,
start: start
}, (results) => {
if(results){
setTimeout(()=>{
active = true;
start = start + 30;;
}, 400);
new ContentActive();
}
});
}
}
var scrollRoute =
{
contentScroll: () =>{
yHandler();
}
};
var scrollHandler = function(options)
{
var func = options.func.name;
var funcOptions = options.func.options;
var elem = options.elem;
var flag = options.flag;
this.events = () => {
addEvent(elem, 'scroll', ()=>{
scrollRoute[func](elem, funcOptions);
}, flag);
}
this.clear = () => {
elem.removeEventListener('scroll', scrollRoute[func](), flag);
}
}
I am using this function to set events
function addEvent(obj, type, fn, flag = false) {
if (obj.addEventListener) {
obj.addEventListener(type, fn, flag);
} else if (obj.attachEvent) {
obj["e" + type + fn] = fn;
obj[type + fn] = function () {
obj["e" + type + fn](window.event);
};
obj.attachEvent("on" + type, obj[type + fn]);
} else {
obj["on" + type] = obj["e" + type + fn];
}
}
I am calling this code from whatever code when I need to set the infinite scroll event
new scrollHandler({
func: {
'name':'contentScroll',
},
elem: window,
flag: true,
}).events();
I am calling this code from whatever code when I need to remove the infinite scroll event but without any luck
new scrollHandler({
func: {
'name':'contentScroll',
},
elem: window,
flag: true,
}).clear();
How do I successfully remove the event listener? I can't just name the instances, that will be so messy in the long run when setting and removing the scroll events from various different places.
Two problems:
You have to pass the same function to removeEventListener as you passed to addEventListener. (Similarly, you have to pass the same function to detachEvent as you passed to attachEvent using Microsoft's proprietary stuff — but unless you really have to support IE8 and earlier, you can ditch all that.) Your code isn't doing that.
When trying to remove the handler, you're calling scrollRoute[func]() and passing its return value into removeEventListener. As far as I can tell, that's passing undefined into removeEventListener, which won't do anything useful.
Here's the code I'm referring to above:
this.events = () => {
addEvent(elem, 'scroll', ()=>{ // *** Arrow function you don't
scrollRoute[func](elem, funcOptions); // *** save anywhere
}, flag); // ***
}
this.clear = () => {
elem.removeEventListener('scroll', scrollRoute[func](), flag);
// Calling rather than passing func −−−^^^^^^^^^^^^^^^^^^^
}
Notice that the function you're passing addEvent (which will pass it to addEventListener) is an anonymous arrow function you don't save anywhere, but the function you're passing removeEventListener is the result of calling scrollRoute[func]().
You'll need to keep a reference to the function you pass addEvent and then pass that same function to a function that will undo what addEvent did (removeEvent, perhaps?). Or, again, ditch all that, don't support IE8, and use addEventListener directly.
So for instance:
var scrollHandler = function(options) {
var func = options.func.name;
var funcOptions = options.func.options;
var elem = options.elem;
var flag = options.flag;
var handler = () => {
scrollRoute[func](elem, funcOptions);
};
this.events = () => {
elem.addEventListener('scroll', handler, flag);
};
this.clear = () => {
elem.removeEventListener('scroll', handler, flag);
};
};
(Notice I added a couple of missing semicolons, since you seem to be using them elsewhere, and consistent curly brace positioning.)
Or using more features of ES2015 (since you're using arrow functions already):
var scrollHandler = function(options) {
const {elem, flag, func: {name, options}} = options;
const handler = () => {
scrollRoute[name](elem, options);
};
this.events = () => {
elem.addEventListener('scroll', handler, flag);
};
this.clear = () => {
elem.removeEventListener('scroll', handler, flag);
};
};

Phaser.Signal: listener is a required param of add() and should be a Function

I am currently using Phaser 2.x.The problem is in create method. I am passing the function as an argument to the this.claimButton.events.onInputDown.add() method. But it is not able to reference the function what am I currently referring to.What can I do for this.
I have tried with this.game.claimTicket() and this.game.claimTicket and also tried this.claimTicket into the this.claimButton.events.onInputDown.add() as a parameter. But I am still getting the error saying Uncaught Error: Phaser.Signal: listener is a required param of add() and should be a Function.
But when I tried this.claimButton.events.onInputDown.add(function () { console.log('hello') })
It worked just fine.
var GameState = {
//initiate game settings
init: () => {
//adapt to screen size, fit all the game
this.game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
this.game.scale.pageAlignHorizontally = true;
this.game.scale.pageAlignVertically = true;
},
preload: () => {
this.game.load.image('background', 'assets/images/background.jpg')
this.game.load.image('ticket', 'assets/images/ticket2.jpg')
this.game.load.image('claimButton', 'assets/images/claimButton.png')
},
create: () => {
this.background = this.game.add.sprite(0, 0, 'background')
this.ticket = this.game.add.sprite(this.game.world.centerX, 130, 'ticket')
this.ticket.anchor.setTo(0.5)
this.claimButton = this.game.add.sprite(this.game.world.centerX + 180, 125, 'claimButton')
this.claimButton.anchor.setTo(0.5);
this.claimButton.inputEnabled = true;
this.claimButton.input.pixelPerfectClick = true;
//this.claimButton.events.onInputDown.add(function () { console.log('It worked as I expected') })
this.claimButton.events.onInputDown.add(this.game.claimTicket,this)
},
claimTicket: (sprite) => {
console.log('claimed')
}
};
//initiate the Phaser framework
var game = new Phaser.Game(640, 360, Phaser.AUTO);
game.state.add('GameState', GameState);
game.state.start('GameState');
I expected the function claimTicket to be called from inside the this.claimButton.events.onInputDown.add() but it showing the error
it's because this binding of arrow function.
the this inside GameState.create is the outer scope (probably the global)
Just don't use arrow function here.
let GameState = {
...
create(){
...
},
...
}
or
let GameState = {
...
create:function(){
...
},
...
}

How to get parent class from inner var in Javascript?

I have class like this
class Game {
constructor(player1, player2, gameId) {
this.Clockk = {
totalSeconds: timeForAnswer,
nowSeconds: timeForAnswer,
start: function () {
var self = this;
this.interval = setInterval(function () {
self.nowSeconds -= 1;
if (self.nowSeconds == 0) {
here I want to call "answered" function
}
},
reset: function(){
this.nowSeconds = this.totalSeconds;
},
};
answered(player) {
console.log(player);
}
};
I want to call function of Game class from this.Clockk variable.
this keyword inside a variable is this.Clockkitself, how can I get parrent class itself?
If you are in a method of the Clockk object, then this will refer to the Clockk object and, in Javascript, there is no natural way to refer to a "parent" or "containing" object. So, you have to store a reference to the parent somewhere. I can think of two straightforward ways to store it that come to mind. One is to store it in the parent scope, the other is to store it in the instance data of the Clockk object itself.
FYI, I am also assuming that the answered() method is supposed to be a method on Game object (your code in your question doesn't quite show that - but that appears to be the intent).
Here are two ways to do be able to call the parent's answered() method from with the this.Clockk.start() method:
Save parent reference in parent scope
class Game {
constructor(player1, player2, gameId) {
// save parent reference here so child scopes can refer to it
let parentObj = this;
this.Clockk = {
totalSeconds: timeForAnswer,
nowSeconds: timeForAnswer,
start: function () {
var self = this;
this.interval = setInterval(function () {
self.nowSeconds -= 1;
if (self.nowSeconds == 0) {
// here I want to call "answered" function
parentObj.answered();
}
},
reset: function(){
this.nowSeconds = this.totalSeconds;
},
};
}
answered(player) {
console.log(player);
}
}
Save parent reference in Clockk instance data
class Game {
constructor(player1, player2, gameId) {
this.Clockk = {
// save parent reference in our own instance data
parent: this,
totalSeconds: timeForAnswer,
nowSeconds: timeForAnswer,
start: function () {
var self = this;
this.interval = setInterval(function () {
self.nowSeconds -= 1;
if (self.nowSeconds == 0) {
// here I want to call "answered" function
self.parent.answered();
}
},
reset: function(){
this.nowSeconds = this.totalSeconds;
},
};
}
answered(player) {
console.log(player);
}
}
Or, you could break the Clockk implementation out into it's own class definition and then you'd pass the parent to it in its constructor and then its constructor would store the parent reference in its own instance data.

Binding events to functions within objects

I'm running into a peculiar issue with jQuery/JS in general.
I'm working on a JS file that let me create portlets for a client site. I'm using SignalR to communicate changes to the users.
The following piece of code gives me a bit of a headache as to why it won't work.
Eenheden: function (poPortlet) {
this.moPortlet = poPortlet;
this.Init = function () {
$(this.moPortlet).find('.portlet-title').text($(this.moPortlet).attr('data-cpv-type') + ' Eenheden')
};
this.BindHub = function () {
CPV.moHub.on('onEenheidUpdate', this.Events.Update);
};
this.Events = {
Update: function (pnId, psStatus) {
CPV.Log('Error', 'Update: ' + pnId + ' ' + psStatus);
}
};
}
I am trying to bind the function this.Events.Update on the SignalR event onEenheidUpdate. Instances of these Eenheiden objects are not unique on the pages. The idea is that they contain the same data, but can get filtered, creating a different portlet depending on some configs.
My problem is that the onEenheidUpdate function doesn't trigger the proper event. I want to do it like this so I can use references I set for the unique object, such as the jQuery object I set on initialization.
Problem
Your problem is that when jQuery or javascript in general triggers an event callback, the "context" (value of this) is changed. For example
var MyModule = {
init: function () {
$('.amazing-element').on('click', function () {
// This breaks because `
// `this` is not MyModule
// When the callback is triggered
// `this` is `<p class="amazing-element"></p>`
this.onClick()
})
},
onClick: function () {
console.log('wee, I was clicked')
}
}
Function.prototype.bind to the rescue!
var MyModule = {
init: function () {
$('.amazing-element').on('click', function () {
this.onClick()
}.bind(this))
// ^ here we bind the context of the callback to the
// correct version of `this`.
},
onClick: function () {
console.log('wee, I was clicked')
}
}
So your exact example would look like:
Eenheden: function (poPortlet) {
this.moPortlet = poPortlet;
this.Init = function () {
$(this.moPortlet).find('.portlet-title').text($(this.moPortlet).attr('data-cpv-type') + ' Eenheden')
};
this.BindHub = function () {
CPV.moHub.on('onEenheidUpdate', this.Events.Update.bind(this));
// ^ change here
};
this.Events = {
Update: function (pnId, psStatus) {
CPV.Log('Error', 'Update: ' + pnId + ' ' + psStatus);
}
};
}

Using 'this' inside a callback inside an object literal [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Javascript prototype ‘this’ issue
I have an event listener which of course calls a method on an event. This method tries unsuccessfully to hold a reference to the holding objects this so that it can access other properties of the object.
There is a single comment denoting where the behavior is not understood. this_hold.Name is not accessible there as I thought it would be.
/*MUserExist
**
**
**
*/
$A.module({
Name: 'MUserExist',
S: {
ClientStorage: SClientStorage,
ComMessage: SComMessage,
ComText: SComText,
DynSma: SDynSma,
DynTwe: SDynTwe,
DynArc: SDynArc,
AniMorphLabel: SAniMorphLabel,
AniFlipPage: SAniFlipPage
},
E: {
but: $A('#ue_but')[0],
text: $A('#ue_go')[0],
form: $A('#ue_fo')[0],
check: $A('#ue_check')[0]
},
J: {
box: $('#ue_box')
},
init: function () {
var pipe = {},
this_hold = this;
this.J.box.draggable();
this.E.but.addEventListener("click", function () {
pipe = $A.definePipe(this_hold.Name);
$A.machine(pipe);
}, false);
this.E.text.addEventListener("keypress", this.enter, false);
this.S.AniMorphLabel.run(["ue_email",
"ue_email_lab",
"ue_go",
"ue_pass_lab"
]);
},
enter: function (event) {
var pipe = {},
this_hold = this;
if (event.keyCode === 13) {
pipe = $A.definePipe(this_hold.Name); // fails here what does 'this' point to?
$A.machine(pipe);
event.preventDefault();
}
},
pre: function (pipe) {
var form_elements = this.E.form.elements,
text_object = new this.S.ComText(form_elements);
pipe.enter = this.enter;
if ($A.Un.get('load') === '1') {
if (!text_object.checkFull()) {
pipe.type = 'empty';
return this.S.ComMessage.message(pipe);
}
if (!text_object.checkPattern('email')) {
pipe.type = 'email';
return this.S.ComMessage.message(pipe);
}
if (!text_object.checkPattern('pass')) {
pipe.type = 'pass';
return this.S.ComMessage.message(pipe);
}
}
pipe.page = text_object.getArray();
pipe.proceed = true;
pipe.page.remember = this.E.check.checked;
return pipe;
},
post : function (pipe) {
if (pipe.proceed === true) {
this.S.ComMessage.resetView('ue_email');
this.S.ComMessage.resetView('ue_go');
this.S.ClientStorage.setAll(pipe.server.smalls);
this.S.DynSma.run(pipe.server.smalls);
this.S.DynArc.run(pipe.server.arcmarks);
this.S.DynTwe.run(pipe.server.tweets);
this.S.AniFlipPage.run('ma');
} else {
return this.S.ComMessage.message(pipe);
}
}
});
this likely points to the DOM node from which the event was triggered. Have you tried writing this to the console to inspect it?
console.log(this);
this is the DOM object that generated the event. It is NOT your javascript object.
When you pass this.enter as the method for the event handler, the method enter does not stay bound to your object. If you want that to happen, you have to change your code to cause that to happen by doing something like this:
// save local copy of my object so I can refer to it in
// the anonymous function
var obj = this;
this.E.text.addEventListener("keypress", function(event) {obj.enter(event)}, false);
It is important to remember that this is set by the caller of a method/function. In this case the caller of the event handler is the event sub-system in the browser. It does not know what your object is and it's designed behavior is to set this to the DOM object that caused the event. So, if you want to call your obj.enter method, you can't just pass enter as the event handler. Instead, you make a separate function that gets called as the event handler and you then call obj.enter() from that using your object as the base so that this gets set correctly.
Another solution would be to use .bind() which also creates a stub function that binds the right this to a function call, but I don't use .bind() myself because it doesn't work in all older browsers.
Try to change how the event is being bound
this.E.text.addEventListener("keypress", this.enter, false);
to
var that = this;
this.E.text.addEventListener("keypress", function(event) {
that.enter(event);
}, false);

Categories