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;
}));
}
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.
Given this method:
public logIn(data:any): Observable<any> {
this.http.get('https://api.myapp.com/csrf-cookie').subscribe(() => {
return this.http.post('https://api.myapp.com/login', data);
});
}
I would like it to return that nested observable, so that my calling code can use it like so:
this.apiService.logIn(credentials).subscribe(() => {
// redirect user to their dashboard
});
without needing to know about the first /csrf-cookie request. Obviously the above doesn't work - but I'm struggling to understand how to make the inner HTTP request wait for the outer one to finish AND be returned by the method.
you should use switchMap see the documentation on switch map
public logIn(data:any): Observable<any> {
return this.http.get('https://api.myapp.com/csrf-cookie').pipe(
switchMap(x => this.http.post('https://api.myapp.com/login', data))
);
}
with rxjs nested subscribes are generally not a good idea. There are many great operators within the library that will get you around it. In this case above where one call depends on another switchMap(...) is the best fit.
Also the code has been modified to return the observable not the subscription
I am new to Angular and I am working on fixing something that is written in Angular 2. In the method we have a call to to observable method after that we are assigning a value to a variable. I need the obersavable method to finish execution before assigning the value. Given below is the code for your reference.
Method1(): void {
this.Service.getData()
.subscribe(
res => {
},
error => this.errorMessage = <any>error);
this.gotData = true;
}
So, As shown above, I need to assign true to gotData variable after the getData Observable method is finished execution. But gotData is getting assigned true even before we actually get the data.
Please help me who to wait until the observable method is finished execution. Thank you !
Sounds like you need to read up on Observables and Subscriptions.
Angular Docs
Alternatively, the subscribe() method can accept callback function definitions in line, for next, error, and complete handlers. For example, the following subscribe() call is the same as the one that specifies the predefined observer:
In your case the callback you have named res will be called after your observable method is finished.
So your code could be:
Method1(): void {
this.Service.getData()
.subscribe(
res => {
this.gotData = true;
},
error => this.errorMessage = <any>error
);
}
I'm banging my head against the wall with observables. Almost all of the documentation I can find is in the older rxjs syntax.
I have an API call which is an observable. I'm calling it elsewhere and subscribing to it - trying to populate a table with the data from this GET request.
If I simply console.log my getData function, it logs the subscription rather than my data.
I can successfully console.log data within the .subscribe function, but I want to use data outside of .subscribe().
How do I extract data out of the .subscribe() function and use it elsewhere? Or, must all of my logic be contained within the .subscribe() function to use data?
getData2() {
return this.m_dbService.get('api/myApiPath').subscribe(
data => (console.log(data)), //This properly logs my data. How to extract `data` out of here and actually use it?
error => { throw error },
() => console.log("finished")
);
}
workbookInit(args){
var datasource = this.getData2(); // this returns the subscription and doesn't work.
}
just return the HTTP req from getData() and subscribe it inside the workbookInit function.
getData2() {
return this.m_dbService.get('api/myApiPath')
}
workbookInit(args){
this.getData2().subscribe(
data => {
var datasource = data
},
error => { throw error },
() => console.log("finished")
}
What you probably want to do is to populate another Observable with the data so that you can access it elsewhere in your project without the need for calling the API more than once.
To do this, you create what is known as a Subject (in this case a BehaviorSubject) and you can populate that with data when your API call returns a response.
Then, in order to access this data elsewhere, you can create a "get" function to return the Subject (which is itself an Observable) whenever you need the data.
Here is an example:
my-data.service.ts
myData: BehaviorSubject<number> = new BehaviorSubject<number>(0);
callApi() {
this.dbService.get('apiUrl').subscribe(
(data) = > this.myData.next(data) // Assuming data is a 'number'
);
}
getMyData() {
return this.myData.asObservable();
}
Now to use this in a component:
this.myService.getMyData().subscribe(
(data) => { /* Use the value from myData observable freely */ }
);
Or you could rely on the Angular async pipe (which is a very convenient method for dealing with observables in your code).
You should not subscribe to the Observable inside getData2. Return it as is instead, then do the following:
var dataSource;
this.getData2().subscribe(res => dataSource = res);
Please note that the variable dataSource will be set when the request is done (asynchronously), so you can't use it immediately in the same block scope.
If you want to use it immediately, then put your code inside the subscription.
If you have an observable that provides data to populate a table, the best way is not to use subscribe(), but use the observable directly in your html template by using the async pipe. You'll have less to worry about and your code will be much simpler.
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