ES6, PreloadJS and scope - scope

In PreloadJS' callback functions I'm losing scope. Have tried different methods such as an arrow function (which aren't valid on the highest level etc... Any tips? (sorry for the malformed code block (class defenition etc), still getting used to this editor, trust me, all is working code)...
import Navigation from './Navigation'
import ajax from "./Ajax";
import Helper from "./Helper";
let preload = null;
export default class Page{
constructor(){
this.preload = new createjs.LoadQueue();
this.preload.addEventListener("fileprogress", this.handleFileProgress);
this.preload.addEventListener("fileload", this.handleFileComplete);
}
initPage(_arg = []){
this.buildPage(this.thePage);
}
buildPage(_content){
this.loadImage(item.featured_image_thumbnail_url);
}
handleFileComplete(event){
console.log(event);
console.log(this); // undefined
let el = document.getElementById('heroImage');
let loader = el.getElementsByClassName('loader')[0];
this.showPage(); // cannot read property showPage of undefined
}
// small arrow test, doesn't seem valid
//handleFileComplete = (event) =>{
// this.showPage();
//}
handleFileProgress(event){
let hero = document.getElementById('heroImage')
let loader = hero.getElementsByClassName('loader')[0];
}
loadImage(_img){
let el = document.getElementById('heroImage');
let loader = el.getElementsByClassName('loader')[0];
let loadManifest = [
{
id: 'pat',
src: _img,
scope: this
}];
this.preload.loadManifest(loadManifest, true);
}
showPage(){
//Helper.removeClass(document.getElementById('mytest','hidden'));
}
}

Ok, update after only 5 min....
Addded an arrow function as a variable and passed that on to the event listener. This is not my preferred way but it does work though...
constructor(){
let handleFileCompleted = (event) => {
this.showPage(event)
}
this.preload = new createjs.LoadQueue();
this.preload.addEventListener("fileprogress", this.handleFileProgress);
this.preload.addEventListener("fileload", handleFileCompleted);
}
showPage(event){
console.log('I'm now reachable!');
console.log(event); // fileload event
}
Perhaps this can help anybody out.
Still interested to see if there's a better way though...

Related

Unsure why a jest test is failing

I have the following test code. The test is failing, but I'm not entirely sure why (I have a suspicion, but I'm unsure of how to fix it). Here are the high level classes:
// file myFile.js
const EventEmitter = require('events').EventEmitter;
const em = new EventEmitter();
class Foo {
constructor () {
this.support = 0;
em.on('onNewValue', this._handleNewValue.bind(this));
}
_handleNewValue (newValue) {
this.support = newValue;
console.log(this.support); // Prints whatever newValue is
}
}
class Bar {
setValue (newValue) {
em.emit('onNewValue', newValue);
}
}
Here's the test
const foo = new Foo();
expect(foo.support).toEqual(0); // Passes - initial value of support is 0
const bar = new Bar();
bar.setValue(10); // Emits an event that causes foo to set it's internal support value to 10.
console.log('Checking support'); // This prints after the console.log in foo._handleNewValue.
expect(foo.support).toEqual(10); // This fails - still reports initial value of 0
The high-level idea is that bar.setValue() emits an event that is listened to by the Foo class. The event listener updates the value of it's internal member support to be what the event payload is.
The test fails and reports the initial value of support. My hunch is that there is some timing issue going on (the expect gets called first, and then the event listener inside the Foo class).
However, if I console.log, the log in the event listener prints first, and then the log for 'Checking support', which does not seem to go along that theory.
Could someone help me figure out why this test is failing, and how to fix it? I don't want to explicitly call foo._handleNewValue in the test.
I tried your code using jest and it worked perfectly. Here's what I've tried
// file myFile.js
const EventEmitter = require('events').EventEmitter;
const em = new EventEmitter();
class Foo {
constructor() {
this.support = 0;
em.on('onNewValue', this._handleNewValue.bind(this));
}
_handleNewValue(newValue) {
this.support = newValue;
console.log(this.support); // Prints whatever newValue is
}
}
class Bar {
setValue(newValue) {
em.emit('onNewValue', newValue);
}
}
test('should catch bar emitted event', () => {
const foo = new Foo();
expect(foo.support).toBe(0)
const bar = new Bar()
bar.setValue(10)
expect(foo.support).toBe(10)
})

How to export object from module.exports?

I need to require a file passing a parameter, for this I used the following syntax:
module.exports = function(bot) {
const menu = new TelegrafInlineMenu(bot);
return menu;
};
the problem's that the code above export the function, I need to return the menu object, is there a way to do this?
I require the script using:
const menu = require('menu')(bot);
problem's that menu is a function not an object
function TelegrafInlineMenu(bot) {
// constructor
if (!(this instanceof TelegrafInlineMenu)) {
return new TelegrafInlineMenu(bot);
}
}
TelegrafInlineMenu.prototype.someFunction = function () {
// etc.
};
module.exports = TelegrafInlineMenu;

passing function to a class in nodejs

I have a function that I need to pass to a class I have defined in nodeJs.
The use case scenario is I want to give the implementer of the class the control of what to do with the data received from createCall function. I don't mind if the method becomes a member function of the class. Any help would be appreciated.
//Function to pass. Defined by the person using the class in their project.
var someFunction = function(data){
console.log(data)
}
//And I have a class i.e. the library.
class A {
constructor(user, handler) {
this.user = user;
this.notificationHandler = handler;
}
createCall(){
var result = new Promise (function(resolve,reject) {
resolve(callApi());
});
//doesn't work. Keeps saying notificationHandler is not a function
result.then(function(resp) {
this.notificationHandler(resp);
}) ;
//I want to pass this resp back to the function I had passed in the
// constructor.
//How do I achieve this.
}
callApi(){ ...somecode... }
}
// The user creates an object of the class like this
var obj = new A("abc#gmail.com", someFunction);
obj.createCall(); // This call should execute the logic inside someFunction after the resp is received.
Arrow functions (if your Node version supports them) are convenient here:
class A {
constructor(user, handler) {
this.user = user;
this.notificationHandler = handler;
}
createCall() {
var result = new Promise(resolve => {
// we're fine here, `this` is the current A instance
resolve(this.callApi());
});
result.then(resp => {
this.notificationHandler(resp);
});
}
callApi() {
// Some code here...
}
}
Inside arrow functions, this refers to the context that defined such functions, in our case the current instance of A. The old school way (ECMA 5) would be:
createCall() {
// save current instance in a variable for further use
// inside callback functions
var self = this;
var result = new Promise(function(resolve) {
// here `this` is completely irrelevant;
// we need to use `self`
resolve(self.callApi());
});
result.then(function(resp) {
self.notificationHandler(resp);
});
}
Check here for details: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_separate_this

I just started learning node.js and i implemented a demo code exhibiting event listeners. I got an error

I am getting this error in my code
TypeError: account.on() is not a function
Where did i go wrong?
Code
var events = require('events');
function Account() {
this.balance = 0;
events.EventEmitter.call(this);
this.deposit = function(amount) {
this.balance += amount;
this.emit('balanceChanged');
};
this.withdraw = function(amount) {
this.balance -= amount;
this.emit('balanceChanged');
};
}
Account.prototype._proto_ = events.EventEmitter.prototype;
function displayBalance() {
console.log('Account balance : $%d', this.balance);
}
function checkOverdraw() {
if (this.balance < 0) {
console.log('Account overdrawn!!!');
}
}
function checkgoal(acc, goal) {
if (acc.balance > goal) {
console.log('Goal Achieved!!!');
}
}
var account = new Account();
account.on('balanceChanged', displayBalance);
account.on('balanceChanged', checkOverdraw);
account.on('balanceChanged', function() {
checkgoal(this, 1000);
});
account.deposit(220);
account.deposit(320);
account.deposit(600);
account.withdraw(1200);
Your example code is not idiomatic Node JS.
I'd strongly recommend you follow the recommended best practices when creating new inheritable objects, as in:
var util=require('util');
var EventEmitter = require('events').EventEmitter;
var Account = function(){
EventEmitter.call(this); // should be first
this.balance=0; // instance var
};
util.inherits(Account,EventEmitter);
Account.prototype.deposit = function(amount){
this.balance += amount;
this.emit('balanceChanged');
};
Account.prototype.withdraw = function(amount){
this.balance -= amount;
this.emit('balanceChanged');
};
var account = new Account();
var displayBalance = function(){
console.log("Account balance : $%d", this.balance);
};
account.on('balanceChanged',displayBalance);
account.deposit(200);
account.withdraw(40);
// ... etc. ....
Which, when run displays:
Account balance : $200
Account balance : $160
Best practices are there so that
your code can be expressed in a way that is easy for others to understand
you don't run into unexpected problems when you try to replicate functionality that is already defined, possibly complex and difficult to understand.
The reason that util.inherits exists is so you don't have to worry about how the prototype chain is constructed. By constructing it yourself, you will often run into the problem you experienced.
Also, since the current Node runtime (>6.0) also includes most of the ES6 spec, you can also (and really should) write your code as:
const util = require('util');
const EventEmitter = require('events').EventEmitter;
const Account = () => {
EventEmitter.call(this);
this.balance = 0;
};
util.inherits(Account,EventEmitter);
Account.prototype.deposit = (val) => {
this.balance += val;
this.emit('balanceChanged');
};
Account.prototype.withdraw = (val) => {
this.balance -= val;
this.emit('balanceChanged');
};
The use of the const keyword assures the variables you create cannot be changed inadvertently or unexpectedly.
And the use of the "fat arrow" function definition idiom (() => {}) is more succinct and thus quicker to type, but also carries the added benefit that it preserves the value of this from the surrounding context so you never have to write something like:
Account.prototype.doSomething = function() {
var self = this;
doSomething(val, function(err,res){
if(err) {
throw err;
}
self.result=res;
});
};
which, using the 'fat arrow' construct becomes:
Account.prototype.doSomething = () => {
doSomething(val, (err,res) => {
if(err) {
throw err;
}
this.result=res; // where 'this' is the instance of Account
});
};
The "fat arrow" idiom also allows you to do some things more succinctly like:
// return the result of a single operation
const add = (a,b) => a + b;
// return a single result object
const getSum = (a,b) => {{a:a,b:b,sum:a+b}};
Another way to create inheritable "classes" in ES6 is to use its class construction notation:
const EventEmitter = require('events');
class Account extends EventEmitter {
constructor() {
super();
this._balance = 0; // start instance vars with an underscore
}
get balance() { // and add a getter
return this._balance;
}
deposit(amount) {
this._balance += amount;
this.emit('balanceChanged');
}
withdraw(amount) {
this._balance -= amount;
this.emit('balanceChanged');
}
}
It should be noted that both ways of constructing inheritable prototypal objects is really the same, except that the new class construction idiom adds syntactic "sugar" to bring the declaration more in-line with other languages that support more classical object orientation.
The ES6 extensions to node offer many other benefits worthy of study.

Javascript accessing object and array defined in modular function

This is a bit foreign to me and I'm probably not understanding it correctly. This is what I have:
var imgModule = (function() {
var imgLocations = {};
var images = [];
imgLocations.setImage = function(img, location) {
imgLocations[img] = location;
}
imgLocations.getImg = function(img) {
return imgLocations[img];
}
imgLocations.setImageArray = function(img) {
images.push(img);
}
imgLocations.getImageArray = function() {
return images;
}
return imgLocations;
}());
I want to be able to access the imgLocations Object and images array from outside this function. The setting functions work, but
document.getElementById("but").onclick = function() {
console.log(imgModule.imgLocations.getImageArray());
console.log(imgModule.imgLocations.getImg(imgName));
}
Both return "undefined". How do I access these variables? And how can I improve this function? Please be patient with me and explain what I'm doing wrong :) I'm trying to learn it the right way instead of defining a global variable outside all functions.
The reason why this isn't working, is because your imgModule is returning the imgLocations object. That being the case, imgModule will actually be the imgLocations object. So you would access your methods like so:
imgModule.setImage()
imgModule.getImg()
imgModule.getImageArray()
imgModule.setImageArray()
And as #gillesc stated. If you are wanting to keep the current syntax of imgModule.imgLocations.getImg() then you could return the imgLocations like so
return {
imgLocations: imgLocations
}
doing so would allow you to add more functionality to your module
return {
imgLocations: imgLocations,
otherObject: otherObject
}
...
imgModule.otherObject.someFunctionCall();
The problem is you are returning the object created and are not setting it as a property of an object.
So in your case this is how it would work.
document.getElementById("but").onclick = function() {
console.log(imgModule.getImageArray());
console.log(imgModule.getImg(imgName));
}
What you need to do is return it like this
return {
imgLocations: imgLocations
}
If you want the API you are attending to create and still have access to the array which you can not do currently.
You don't access imgModule.imgLocations, since what you return is imgLocations, you should access them as:
document.getElementById("but").onclick = function() {
console.log(imgModule.getImageArray());
console.log(imgModule.getImg(imgName));
}
It seems you try to write module pattern.
For deep understanding, I recommend you following article:
The Module Pattern, by Addy Osmani
and pay attention to example with counter:
var testModule = (function () {
var counter = 0;
return {
incrementCounter: function () {
return counter++;
},
resetCounter: function () {
console.log( "counter value prior to reset: " + counter );
counter = 0;
}
};
})();
// Usage:
// Increment our counter
testModule.incrementCounter();
// Check the counter value and reset
// Outputs: counter value prior to reset: 1
testModule.resetCounter();

Resources