'This' scope changes when calling function from array - javascript

For code shortness I'm trying to store some similar functions inside an array, and call the correct function depending on a numeric key.
Each function returns a Promise, that actually wraps an Observable, I'm fecthing some data and pushing it to the corresponding data array.
Calling each individual functions works fine, but when I'm trying to call them from the functions array (event based call), the "this' keyword refers to the function array instead of the class.
Here is the code:
public currentKey = 0;
...
constructor(private api: ApiService) {
}
ngOnInit() {
this.loadTrending(0); //THIS WORKS!
this.loadNews(0);
....
this.loadingMethods = [this.loadTrending, this.loadNews, this.loadCrowdfunding, this.loadUpcoming]
}
loadData(event) {
this.loadingMethods[this.currentKey](this.offsets[this.currentKey]).then(bool => {
event.target.complete(); // THIS DOESN'T WORK
});
}
public loadTrending(offset: number): Promise<boolean> {
return new Promise((resolve, reject) => {
this.api.trending(offset).subscribe(trending => { //'THIS' REFERS HERE TO THE ARRAY
this.gameLists[this.TRENDING_KEY].push(...trending.list);
resolve(true);
});
});
}
Is there a way to achieve this function call and have this refering to the class as usual ?
EDIT : I'm already using ECMA6 arrow functions that works througout all my project, so I don't think How to access the correct `this` inside a callback? is the answer here. Even though it is well explained there.
Error is:
core.js:15724 ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'trending' of undefined
TypeError: Cannot read property 'trending' of undefined
at news.page.ts:68
at new ZoneAwarePromise (zone.js:910)
Thanks.

Answer given by #ConnorsFan & #Pointy.
Need to to use .bind() in my array since calling a method from an array will normally change the context of 'this' even though I'm using arrow functions.
this.loadingMethods = [this.loadTrending.bind(this), ...]
Thanks.

Related

Meteor.publish with call to an async function returning error

I am trying to create a wrapper method for Meteor.publish that does an access check before actually calling it.
The access check is represented by an async-await method, which leads to the following error in my wrapper function when running it, if I convert my wrapper method to an async one.
Exception from sub alerts.notAcknowledged id 2 Error: Publish function
can only return a Cursor or an array of Cursors at
Subscription._publishHandlerResult
(packages/ddp-server/livedata_server.js:1132:18)
If I remove the async keyword from the wrapper method and comment out the call to the async-await method, the wrapper method is working as expected.
I did not find any answer to solve my issue, but after checking several solutions and trying out, I got to the following solution:
export const wrapperPublish = (metadata: any, callback: Function) => {
Meteor.publish(metadata.name, (params: LooseObject) => {
const allowAccessSync = Meteor.wrapAsync(allowAccess);
const { error, hasAccess } = MeteorPromise.await(allowAccessSync({
token: params.token,
methodName: params.methodName,
}, () => {}));
check(params, metadata.checks);
if (!hasAccess) {
// do something
}
return callback(params);
});
};
In the code snippet above, the allowAccess represents the function returning a Promise and the solution was the Meteor.wrapAsync that transforms an async function into a convenient synchronous-looking function.
https://docs.meteor.com/api/core.html#Meteor-wrapAsync

Sinon Stub JavaScript Method Chain

I'm looking to use sinon stub to test the method chain below:
driver.manage().window().setSize()
I found a related question that explains how to access one method down the chain, however this does not seem to give me access to any additional methods.
t.context.webdriver = sinon.stub(new WebDriver)
sinon.stub(t.context.webdriver, "manage", () => {
return {
window: sinon.stub().returns();
};
})
which returns the error
Error: this._driver.manage(...).window(...).setSize is not a function
How to I stub multi-level method chains?
I'm not sure what your trying to to test, but the error is coming from the fact that the object your stub is returning doesn't have a window() function or a setSize(). Chains work because each part of the chain returns something with a method that matches the next call. So if you stuff something early in the chain, you need to makes sure what you return has those methods. Maybe that involves passing back the original return, or maybe you fake the whole chain.
Here's an example that at least won't throw:
const sinon = require('sinon')
// some fake object that maybe looks like what you have
let driver = {
manage(){ return this},
window() { return this},
setSize() {console.log("size set")}
}
// stubb manage and now you're resposible for the whole chain
sinon.stub(driver, "manage").callsFake(() => {
console.log("called")
return {
window(){
return { setSize: sinon.stub().returns() }
}
};
})
Of course, there are a lot of variations possible depending on what you're trying to test.

How to get prototype properties

i write this code but i don`t now why my console.log give me undefined and how can i get value of prototype properties or functions
(function() {
function Users() {}
Users.prototype.getUser = function() {
$.getJSON('/usersList/users.json', function(request) {
Users.prototype.allUsers = request.users;
});
}
var users = new Users();
users.getUsers();
console.log(users.allUsers);
}
())
What i wont to achieve is to have this user list as my object property like User.allUsers in some array.
Thanks
This is because $.getJSON is asynchronous.
When you create an object from User prototype, $.getJSON has to call its callback yet.
In the other hand, you're trying to simulate global variables adding data properties to a given prototype. This is a bad practice and, at the end of the day, you've already realized that you got stuck on this approach.
What you need an initialization code executed whenever the site or app is started so those allUsers are called once and injected everywhere:
const initApp = () => Promise.all([
$.getJSON("/someData/users.json")
// other comma-separated async functions which return promises
])
initApp().then(([allUsers, otherData, yetAnotherData]) => {
callSomeFunctionWhichRequiresAllUsers(allUsers)
})

Why can´t I work with this Object outside the ngOnInit() Method?

I am getting some data from my server and want to do some stuff with it inside the .ts file. It's an elementary thing that I don't understand about Typescript/angular so far...Hope someone can help me here
user: any;
public doStuff(){
alert(this.user.username);
}
a user is an object having different properties like 'username' that is initialized on the ngOnInit() block.
I´m setting it in the ngOnInit method. The service is injected correctly and following code works properly
ngOnInit() {
this.authService.getProfile().subscribe(profile =>{
this.user= profile.user;
this.initStuff();
},
err => {
console.log(err);
return false;
});
}
it's alerting the username as intended...but as soon as I move the method call of the doStuff() Method outside that Codeblock, it´s not working anymore, in the browser-console it says "cannot read property 'value' of undefined" - why is it undefined? If I use {{user.username}} in the component.html, it also shows me the correct username
ngOnInit() {
this.authService.getProfile().subscribe(profile =>{
this.user= profile.user;
},
err => {
console.log(err);
return false;
});
this.initStuff(); // why cant i call it here? Its where I also call all of my other doStuff() methods
}
It is not working because getProfile() is async operation. If you want to do stuff with server data it has to be done in subscribe next() method. Next method is called when aysnc operation has new value.
If you call initStuff() outside of next() method, it will be executed immediately that's why your getting "cannot read peoperty 'value' of undefined".
Hope this can help you.
If I have understood all correctly you are trying to call not yet received data and it is reason of you console error. In described function you use Observable (RxJs) and is function is async. If you want to do something with variable 'profile' you should to place operation in Observable callback, is meant in body subscribe's call method.

Angular 2. Losing scope of this in Promise

I feel like I am missing something here. I have a service that grabs some data. I convert it to a promise and then try and work on the data in a seperate method.
When once it hits the method I loose the ability to access my objects that i would normally access from this.whatever. If I leave all the code from the addJobsToTree in the then block, it works fine. I can also access this from every where else in the component. I'm sure i'm doing something dumb but can't figure it out.
ngOnInit(){
this._scheduleDataService.getSavedScheduleData(this.buildDateStringFromCalendar(),1004)
.toPromise()
.then(this.addToJobsTree);
}
private addToJobsTree(res){
for(let xx of res){
this._sharedService.jobs.push(xx); //Comes back as cannot read _sharedService of null
console.log(this._sharedService.jobs);
}
}
It's because you reference a function and you lose the context of the function. To fix that you need to explicitly link the function to an object.
You can use either the bind method:
ngOnInit(){
this._scheduleDataService.getSavedScheduleData(this.buildDateStringFromCalendar(),1004)
.toPromise()
.then(this.addToJobsTree.bind(this); // <-----
}
(note: here is the drawback to using the bind method with TypeScript: https://basarat.gitbooks.io/typescript/content/docs/tips/bind.html)
or an arrow function to fix that:
ngOnInit(){
this._scheduleDataService.getSavedScheduleData(this.buildDateStringFromCalendar(),1004)
.toPromise()
.then((data) => { // <-----
this.addToJobsTree(data);
});
}

Categories