I tried to setup a timeout function with rxjs observable inside a angular2 component
this._subscription = Observable.timer(1000).subscribe(() => {
console.log('inside timeout')
})
and unsubscribe it in other method. However the observable never executed
If i change to
let _subscription = Observable.timer(1000).subscribe(() => {
console.log('inside timeout')
})
It works fine. I also tried with
this._subscription=setTimeout(()=>{},1000)
same thing happened. I suspect it's the ngZone bug so I wrap the function inside
this._ngZone.runOutsideAngular(() => {})
but result is the same. Anyone encounter the same issue before? i am using angular 2.2.4
Subscribing to an observable should be done (in most cases) within ngOnInit method.
So now, if you want to unsubscribe from it, you might do it like that:
private onDestroy$ = new Subject<void>();
private stopObs$ = new Subject<void>();
ngOnInit() {
someObs$
// stop the observable if the component is being destroyed
.takeUntil(this.onDestroy$)
// stop the component from another method if you want
.takeUntil(this.stopObs$)
.do(x => console.log(`A value has been received: ${x}`))
.subscribe();
}
callThisMethodToStopListeningToObs() {
this.stopObs$.next();
this.stopObs$.complete();
}
ngOnDestroy() {
this.onDestroy$.next();
this.onDestroy$.complete();
}
Here someObs$ might be an interval, a timer or whatever observable :).
Turns out i have a clear subscription in onNgInit to clear subscription upon Component initialisation.
NgInit is run after component method in which the observable sits. So it cancels the subscription immediately after it's created. Now i removed the subscription reset, it works fine. What i learnt is your component method can be called by other component before NgInit hook happen
Related
Is there any advantage or benefit to using a async binding vs just mapping to a concrete object when my service call returns with data for my HTML page?
Here is an example of the two options.
Map to a component property
// component
event: any;
// ngOnInit()
this.eventService.getEvent(this.id).pipe(take(1)).subscribe(response => {
this.event = response;
}, error => {
console.log(error);
});
// service
getEvent(id: number): Observable<any> {
return this.http.get<any>(this.baseUrl + 'events/' + id);
}
<div>{{event.title}}</div>
<div>{{event.date}}</div>
map to a async binding
// component
event$: Observable<any> = of (undefined);
// ngOnInit
this.event$ = this.eventService.getEvent(this.id).pipe(take(1),
catchError(error => {
console.log(error);
return throwError(error);
}));
// service
getEvent(id: number): Observable<any> {
return this.http.get<any>(this.baseUrl + 'events/' + id);
}
<div>{{(event$ | async).title}}</div>
<div>{{(event$ | async).date}}</div>
Async Pipe Method
The async pipe subscribes to an Observable or Promise and returns the latest value it has emitted. When a new value is emitted, the async pipe marks the component to be checked for changes. When the component gets destroyed, the async pipe unsubscribes automatically to avoid potential memory leaks. When the reference of the expression changes, the async pipe automatically unsubscribes from the old Observable or Promise and subscribes to the new one.
We use this design when we are using Onpush change detection strategy with state management library because change detection works when we get a brand new object.
It automatically subscribes and unsubscribes the observable or promise on component destruction.
cleaner and more readable as you can have numbers of async subs in the view.
Subscribe method
Here you have to unsubscribe manually, using take will unsubscribe the observable, but what if you need more data from that observable (it limits the data stream).
The major advantage for this method is you can run your logic when you receive data and can use that data at multiple places in the component.
We have used both the patterns based on the scenarios which suits well for specific situation.
Note: async pipe method needs to get a new reference of the data object passed to it in order to run the change detection and update the view.
I'm working with Material-UI components on the project and is using AutoComplete component in my app.
In the example from Material-UI team, I came across an interesting example of the AutoComplete Ajax data: https://material-ui.com/components/autocomplete/#asynchronous-requests
They are using React Hooks to fetch the data from the server:
React.useEffect(() => {
let active = true;
if (!loading) {
return undefined;
}
(async () => {
const response = await fetch('https://country.register.gov.uk/records.json?page-size=5000');
await sleep(1e3); // For demo purposes.
const countries = await response.json();
if (active) {
setOptions(Object.keys(countries).map((key) => countries[key].item[0]));
}
})();
return () => {
active = false;
};
}, [loading]);
Why do we use active variable here? Why we return a function that changes this variable to false? It is always true in the if-statement.
Thanks in advance for the answer
The function returned from useEffect is a cleanup function. It is called when the component un-mounts - and is usually used to unsubscribe to events, cancel pending promises etc that were used in the useEffect.
The active variable is used make sure that you aren't updating the state on something that doesn't exist anymore. It's somewhat like the isMounted anti-pattern that existed in class components.
When you try to update state on an un-mounted component, React will throw a warning -
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application.
Having the active variable prevents that in the following way:
Your component loads
The useEffect calls an async fetch - this will take time
Now say, before the server from the response is returned, you navigate away from the page (or perform some other action that un-mounts the component)
That will cause the component to unmount and the cleanup function to be called:
return () => {
active = false;
};
active is now set to false
Finally, we get our response from the server. And now, it'll encounter the false active value, and not update the state.
// active === false,
// this will skip `setOptions`
if (active) {
setOptions(...);
}
This is a pattern which is used to avoid two situations:
updating the state if component containing this hook has already unmounted before HTTP request could complete.
avoid race conditions
Function returned by callback function of useEffect hook is used to perform the clean up, like componentWillUnmount in class based components. This cleanup function runs when the component unmounts or before running the effect next time.
So if component is unmounted when the HTTP request was in progress, cleanup function of useEffect hook will run and will set active to false. After that, whenever the result of HTTP request returns, state won't be updated because active will be false.
See Effects with cleanup section of react docs to understand the cleanup function of useEffect hook and see Race Conditions to better understand the problem related to race conditions that is solved here by using active variable.
I need to get a service call value within app.component.ts file, i need this value before any other view gets loaded because they need this value from redux store and check this within theirsngOnInit() methods.
I've tried using a promise and perform logic within .then() method..., didn't work. I've also tried working with the observable methods
next: x =>, error: err => and complete: () => but the same issue persists: i don't get the expected behavior because when the other views initialize they get an empty value from store because the service hasn't finished returning at that moment.
I found the resolver() functionality, but since app.component.ts is not routed as other components i can't use this.
app.component.ts
ngOnInit() {
this.storeUserReportAccessService.checkUserReportAccess().subscribe((response) => {
this.store.dispatch(new StoreUserReportAccess(response));
}
}
service:
checkUserReportAccess(): Observable<boolean> {
return this.http.postAPI(AWS_GET_USER_REPORT_ACCESS_URL,
null).pipe(map(payload => {
let access = payload.json();
return access.HasAccess;
}));
}
I am using Angular 5 and have subscribed an observable using the subscribe() method. I want to know if only calling the unsubscribe() method on the subscription will be sufficient to cleanup everything, or should I also call remove() method?
code snippet:
`
// somewhere in a method
this.s1 = someObservable.subscribe((value) => {
//somecode
});
// in ngOnDestroy
this.s1.unsubscribe(); // should I also call .remove()
`
.remove remove the subscription from an internal list, but it does not unsubscribe.
.unsubscribe clean up everything, do the unsubscribe and remove the observer from the internal list. (There was a bug (fixed) that didn't remove the observer from the list)
.takeWhile keep alive the subscription whilst a certain situation continues to be false
example:
this.service.method()
.subscribe(res => {
//logic
});
this will never unsubscribe.
this.service.method()
takeWhile(() => this.isAlive) // <-- custom variable setted to true
.subscribe(res => {
//logic
});
ngOnDestroy(){
this.isAlive = false;
}
Automatic unsubscribe when the component is going to be destroyed.
this.s1 = someObservable.subscribe((value) => {
//somecode
});
public yourMethod(){
this.s1.unsubscribe();
}
this subscription will exists and be "alive" until yourFunction is not called.
--
I personally like to use the rxjs operator takeWhile to keep the code clean. In a very big project or single component having multiple subscription it's confusing having (IE) 30 variables: Subscription. So If you are asking when to use the takeWhile operator my answer is: (Taking as example one subscription) -> If you are sure that the unsubscribe needs to be done when the component is destroyed, use takeWhile. If you need to unsubscribe in a certain scenario where the component is still "alive", use the second example I wrote.
Hope to have clarified the argument.
Ok, this is a quick one, i'm kinda exhausted already and am confusing myself :D
I'm working with angular2 and RxJS Observables.
I have a service with a property "data", which is an Observable that get's set in the constructor, and a method to return this observable to subscribe to.
export class test{
private data: Observable<Object>
constructor(private http: Http){
this.data = this.http.get(url).map( res => res.json());
}
getData(): Observable<Object>{
return this.data
}
}
I have worked wit replaySubjects a while ago to always emit all values of the sequence to new subscribers. However, with the code above the Observable seems to emit it's latest value to new subscribers. Is this intended?
test(i: number) {
if (i > 0) {
setTimeout( () => {
this.dataService.getData().subscribe( (data) => {
this.debug.log(data);
this.test(i-1);
});
}, 2000);
}
}
test(4)
I get a value for every iteration. I am confused, 'cause a year ago when i wanted that behaviour, i got no new values when subscribing 'too late'.
Essentially, i just want to cache the result of the http.get, and deliver the same value to all subscribers, instead of making a new http request for every subscription (returning the http.get(url).. in getData())
I know this question is a bit old, but the answers seem to me quite confusing.
The Observable you return from the method getData() is just that, an Observable. So every time a consumer subscribes it gets the response. So it is working fine, but it is indeed making a new request every time.
In order to cache the result there are plenty of ways to do it depending on the behavior you want. To just cache a single request I would recommend t use the #publishBehavior operator:
export class test{
private data: Observable<Object>;
constructor(private http: Http){
this.data = this.http.get(url)
.map(res => res.json())
.publishBehavior([])
.refCount();
}
getData(): Observable<Object>{
return this.data;
}
}
The parameter passed to the publishBehavior is the initial value. With this two operators, the request will be made when the first subscriber arrived. Next subscribers will get the cached answer.
In others answers the use of Subjects has been suggested. The publishBehavior is using subjects under the hood. But to directly call next() it is consider bad practice unless there is no other remedy, and thats not the case for this scenario in my opinion. Even if you use Subjects directly, it will be wise to return an Observable to the Components by using the #asObservable() operator so the component won't have access to the next, error and complete methods.
No. You need to use Subject for this. It has a method next() to which you will send your newly arrived property so that it pushes it to the subscribers.
In addition to this, you should create a service that will be a singleton. Whenever your components instantiate it in a constructor, they will receive the object already formed with all the data. There will be no need to fetch the data every time.
Also, instead of instantiating your data in the constructor, implement OnInit and do the calls to the server from there.