Angular2 subscribe understand arrow function - javascript

I try to understand arrow functions of typescript by the example of Angular 2 Observable subscribe method. Could somebody explain me:
I have this code which works:
this.readdataservice.getPost().subscribe(
posts => { this.posts = posts; }
);
but should it be the same if I use this? But this doesn't work.
this.readdataservice.getPost().subscribe(
function (posts) {
this.posts = posts;
}
);

Arrow function is anonymous and doesn't bind its own this. Hence, this is this of current context.
Normal function binds this to the caller if we don't bind it explicitly
Then
this.readdataservice.getPost().subscribe(
posts => { this.posts = posts; }
);
Can be
var self = this;
this.readdataservice.getPost().subscribe(
function(posts) { self.posts = posts; }
);
Or
this.readdataservice.getPost().subscribe(
function(posts) { this.posts = posts; }.bind(this)
);

JS by default executes functions in the scope of the caller. If you pass a function around to be called somewhere else, this points to the caller.
In your code you pass the function to the observable via the subscribe(...) method and then the function is called by the observable when an event is to be emitted.
If you use arrow function, then this keeps pointing to the class where it is defined.
An alternative is using .bind(this) to tell JS this should keep pointing at the current class instance.
this.readdataservice.getPost().subscribe(
function (posts) {
this.posts = posts;
}.bind(this)
);
See also https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Related

Angular 2+ wait for subscribe to finish to update/access variable

I am having an issue with my variables being undefined. I am certain this is because the observable hasn't finished. Here is the part of my code in my .ts file that is causing the issue. (I'm placing the minimum code required to understand the issue. Also myFunction gets called from a click event in the HTML).
export class myClass {
myVariable: any;
myFunction() {
this.myService.getApi().subscribe(data => {
this.myVariable = data;
});
console.log(myVariable) --> undefined
}
}
So this piece of code calls a function in my service that returns some data from an API. The issue is that when I try to access the variable myVariable right outside of the subscribe function it returns undefined. I'm sure this is because the subscribe hasn't finished before I try to access myVariable
Is there a way to wait for the subscribe to finish before I try to access myVariable?
why not create a separate function and call it inside the subscription.
export class myClass {
myVariable: any;
myFunction() {
this.myService.getApi().subscribe(data => {
this.myVariable = data;
this.update()
});
this.update()
}
update(){
console.log(this.myVariable);
}
}
As you know subscriptions are executed when server return data but the out side of subscription code executed synchronously. That is why console.log outside of it executed. The above answer can do your job but you can also use .map and return observable as shown below.
let say you are calling it from s service
export class myClass {
myVariable: any;
// calling and subscribing the method.
callingFunction() {
// the console log will be executed when there are data back from server
this.myClass.MyFunction().subscribe(data => {
console.log(data);
});
}
}
export class myClass {
myVariable: any;
// this will return an observable as we did not subscribe rather used .map method
myFunction() {
// use .pipe in case of rxjs 6
return this.myService.getApi().map(data => {
this.myVariable = data;
this.update()
});
}
}

redux action, dispatch object with arrow function

I came across below codes
const ERROR_MSG = 'ERROR_MSG'
function errorMsg(msg){
return { msg, type:ERROR_MSG }
}
export function register({user,pwd,type}){
return dispatch=>{
axios.post('/user/register', {user,pwd,type})
.then(res=>{
if(res.status!==200){
dispatch(errorMsg(res.data.msg))
}
})
}
}
Its dispatch recieve a function as param, and the param return something. I'm not so comfortable with it, why need to create extra function? It make sense if errorMsg is global or else it will have redundant functions.
is it possible if I use arrow function then dispatch the action object straight away in the callback?
dispatch(()=>{res.data.msg, type:ERROR_MSG})
You can but note that your arrow function is not returning anything:
dispatch(()=>{res.data.msg, type:ERROR_MSG})
Either use an explicit return:
dispatch(()=>{return {res.data.msg, type:ERROR_MSG}})
Or wrap it with an expression:
dispatch(()=> ({res.data.msg, type:ERROR_MSG}))
Or just pass the object directly to dispatch:
dispatch({msg: res.data.msg, type:ERROR_MSG})
Yes is possible to dispatch straight away with arrow function however its not actually required, Instead of calling an extra function in dispatch, you could just dispatch the action with an object
return dispatch=>{
axios.post('/user/register', {user,pwd,type})
.then(res=>{
if(res.status!==200){
dispatch({ msg: res.data.msg, type:ERROR_MSG })
}
})
}

Dispatch undefined after binding to class in redux

Issue with a pattern i'm trying to use with redux.
I have a a mapDispatchToProps as below,
const mapDispatchToProps = (dispatch) => {
return {
presenter: new Presenter(dispatch),
};
};
and my presenter constructor looks as below:
constructor(dispatch) {
this.dispatcher = dispatch;
}
If I check the value of it in the constructor and after it's set, all is well. However later when a method tries to use it, the value of dispatch is undefined.
If i save it to a var outside the class, i.e.
let dispatch;
class Presenter {
constructor(dispatcher) {
dispatch = dispatcher.bind(this)
}
}
I've tried using .bind() within the first constructor also but it keeps becoming undefined!
Class methods were of the form:
someMethod() {
//do stuff
}
which means they have their own this scope bound... I'd have to bind the individual methods in the constructor, such as:
constructor(dispatch) {
this.dispatch = dispatch;
this.someMethod = this.someMethod.bind(this);
}
Or turn them into => functions so they take their context from the surrounding class, i.e.
someMethod = () => dispatch(/* an action */);

Javascript (react native): how to avoid that = this?

I'd like to avoid let that = this; because it seems to be a dirty solution. Is it e.g. possible to use .bind(this) anyhow?
My current code:
// ...
componentDidMount() {
let that = this; // <- how to avoid this line?
this.props.myService.listensTo('action', (data) => {
that.handleData(data);
});
}
handleData(data) {
// handle data
}
// ...
Thanks in advance!
Basically arrow functions will help with this and since React-Native doesnt have to deal with browser compatibility you can define you're functions like this:
handleData = (data) => {
this.setState({ data });
}
You won't ever have to .bind or that=this if you use this.
this is already bound because of the arrow function you've used.
// ...
componentDidMount() {
this.props.myService.listensTo(
'action',
(data) => this.handleData(data)
);
}
handleData(data) {
// handle data
}
// ...

Angular2 component's "this" is undefined when executing callback function

I have a component which calls a service to fetch data from a RESTful endpoint. This service needs to be given a callback function to execute after fetching said data.
The issue is when I try use the callback function to append the data to the existing data in a component's variable, I get a EXCEPTION: TypeError: Cannot read property 'messages' of undefined. Why is this undefined?
TypeScript version: Version 1.8.10
Controller code:
import {Component} from '#angular/core'
import {ApiService} from '...'
#Component({
...
})
export class MainComponent {
private messages: Array<any>;
constructor(private apiService: ApiService){}
getMessages(){
this.apiService.getMessages(gotMessages);
}
gotMessages(messagesFromApi){
messagesFromApi.forEach((m) => {
this.messages.push(m) // EXCEPTION: TypeError: Cannot read property 'messages' of undefined
})
}
}
Use the Function.prototype.bind function:
getMessages() {
this.apiService.getMessages(this.gotMessages.bind(this));
}
What happens here is that you pass the gotMessages as a callback, when that is being executed the scope is different and so the this is not what you expected.
The bind function returns a new function that is bound to the this you defined.
You can, of course, use an arrow function there as well:
getMessages() {
this.apiService.getMessages(messages => this.gotMessages(messages));
}
I prefer the bind syntax, but it's up to you.
A third option so to bind the method to begin with:
export class MainComponent {
getMessages = () => {
...
}
}
Or
export class MainComponent {
...
constructor(private apiService: ApiService) {
this.getMessages = this.getMessages.bind(this);
}
getMessages(){
this.apiService.getMessages(gotMessages);
}
}
Or you can do it like this
gotMessages(messagesFromApi){
let that = this // somebody uses self
messagesFromApi.forEach((m) => {
that.messages.push(m) // or self.messages.push(m) - if you used self
})
}
Because you're just passing the function reference in getMessages you don't have the right this context.
You can easily fix that by using a lambda which automatically binds the right this context for the use inside that anonymous function:
getMessages(){
this.apiService.getMessages((data) => this.gotMessages(data));
}
I have same issue, resolved by using () => { } instead function()
Please define function
gotMessages = (messagesFromApi) => {
messagesFromApi.forEach((m) => {
this.messages.push(m)
})
}

Categories

Resources