I am trying to turn the observable for params that I get off of the ActivatedRoute into a promise but haven't had any luck. I have been able to turn http requests into promises successfully:
this.riaService.getAdvisorsForState(this.activeState)
.then(rias => {
this.riasForState = rias.json();
console.log(this.riasForState);
});
// this all works ^
But I have not been able to turn the 'activatedRoute.params' into a promise:
export class InvestmentAdvisorStateComponent implements OnInit {
constructor(private activatedRoute: ActivatedRoute, private riaService: InvestmentAdvisorService) { }
getStateFromUrl() {
this.activatedRoute.params
.toPromise()
.then(params => {
console.log('HERE',params)
});
}
// this does not work for me ^
This is what I have right now:
getStateFromUrl() {
this.activatedRoute.params
.subscribe((param: any) => {
this.activeState = param['state'];
});
}
// works ^
I am hoping to implement this as a promise thought so I can .then off of it. Any help on how to do this is greatly appreciated.
The main difference between observable and promise is, that observable can emit multiple events while a promise only emits a single event.
To make your example work, I assume you are only interested in the first event.
getStateFromUrl() {
this.activatedRoute.params
.first()
.toPromise()
.then(params => {
console.log('HERE',params)
});
}
This way the promise created with toPromise() is completed with the first params event.
Related
I am trying to use a DataService property myData that is waiting for callback. But it is undefined when I call in DataComponent. How can I access and use it there?
export class DataService {
public myData;
constructor(private http: HttpClient) {
this.load().then((data) => {
this.myData = data
})
}
load() {
return new Promise((resolve) => {
this.http.get('https://reqres.in/api/users').subscribe(
(res: any) => {
console.log(res.data)
resolve(res.data)
},
(error) => {
console.log(error);
}
)
})
}
}
export class DataComponent implements OnInit {
constructor(private dataService: DataService) {
this.prepareData();
}
prepareData() {
console.log(this.dataService.myData)
}
ngOnInit(): void {
}
}
Here is the source code: https://stackblitz.com/edit/angular-ivy-kbpdpo
You are running into a race condition since this is an asynchronous function.
This change works: https://stackblitz.com/edit/angular-ivy-vf3llg
Consider reading up on https://angular.io/guide/http
Personally, I just have services return raw data and manipulate it elsewhere, but if needed you can tap into the response as I have shown i the updated example.
This question and answer are probably really a duplicate of this question...
What are pipe and tap methods in Angular tutorial?
your load() method is asynchronous, that means that it can return the response after 2 hours, so it will execute your callback after 2 hours, and you are asking myData synchronously which means that you are asking it right now, so it won't work.
you have to wait until the answer is returned, in your code there is no chance to accomplish this, so either remove yourData field and just subscribe it into the component, or create BehaviorSubject and emit value to the component
Imagine we have the following factory:
#Injectable()
export class DataClassFactory {
constructor(
private dataService: DataService,
) { }
public createThing(initialData?: InitialData): AsyncSubject<DataClass> {
let dataClass: AsyncSubject<DataClass> = new AsyncSubject<DataClass>();
if (!!initialData) {
dataClass.next(new DataClass(initialData));
dataClass.complete();
} else {
this.dataService.getData().subscribe((dataResponse) => {
dataClass.next(new ReportRequest(dataResponse));
dataClass.complete();
});
}
}
return dataClass;
}
}
We inject this factory, invoke the createThing method, and subscribe to the response in some component. I originally tried to use a plain Subject, but then I realized that in the case where we already have initial data, next() is called before the response is returned, so the subscriber in the component never gets that value.
My question is: is this correct situation in which to use an AsyncSubject, or is there a different/better way to handle this sort of method that has potential synchronous and asynchronous timelines?
I would do something along these lines
public createThing(initialData?: InitialData): Observable<DataClass | ReportRequest> {
if (!!initialData) {
const data = new DataClass(initialData);
return of(data);
} else {
return this.dataService.getData()
.pipe(map(dataResponse => new ReportRequest(dataResponse));
}
}
Whoever calls createThing would get an Observable to which it would have to subscribe.
This Observable would emit an instance of DataClass if initialData is not null, otherwise it would return and instance of ReportRequest as soon as dataService responds.
I am having trouble to return an observable. It seems like the codes inside the mergeMap is not running at all.
Codes:
book.service.ts
import {HttpClient, HttpHeaders} from '#angular/common/http';
export class bookService {
constructor(
private http: HttpClient,
...others
) {}
addNewBook(book): Observable<Book>{
##### Tried to use mergeMap otherwise the return type won't match
return this.tokenService.getToken().mergeMap((token: string) => {
console.log("Fire this...") <===== This is never output.
const myUrl = "www.testurl.com";
const parameters = {
bookTitle: book.name,
};
return this.http.post<Book>(myUrl, book);
})
}
token.service.ts
public token$: Subject<string>;
..others
public getToken(): Observable<string> {
return this.token$; <= return Observable<string> not Observable<Book>
}
book.component.ts that calls the addNewBook method.
...others
return Promise.resolve()
.then(() => {
return bookService.addNewBook(book);
}).then((result) => {
console.log(result);
})
I can't really change the token service because it's used on other place, I am not sure why the codes inside the mergeMap is not running. Can someone help me about it? Thanks a lot!
It won't work unless you subscribe to the results of bookService.addNewBook(book). Just returning it from the then callback won't subscribe. You need to at least add toPromise.
...others
return Promise.resolve()
.then(() => {
return bookService.addNewBook(book).toPromise();
}).then((result) => {
console.log(result);
})
In order for the mergeMap() to be be triggered, the token$ subject inside token.service.ts needs to emit a value (via .next()) after addNewBook() is subscribed to by a consumer.
One of the things to keep in mind with Subjects is that 'late subscribers' won't receive a value off of them until the next time .next([value]) is called on that Subject. If each subscriber, no matter how late, needs to immediately receive the last value generated by that source (Subject) then you can use BehaviorSubject instead.
From your short code example it is hard to see where the Observable generated by addNewBook() is being subscribed to though. Remember, a Observable won't execute until it has a subscriber.
I want to resolve an observable to it's underlying type. Yes, I am aware I should NOT be doing so, but at the moment I would like to test something without Fixing My architecture, and I would like to know if its possible.
This is a function in the base provider to call a GET request
getByRoute(url: string): Observable<T> {
return this.http.get(url).map(res => <T>res.json());
}
I had an only class returning a concrete object with just an Id on it.
//public currentUser = { id: 1 };
But now I'm trying to implement my provider to lazy load the current user, I don't have time right now to switch everything to use an observable, but I would like to know my other code works.
private _user: User;
get currentUser(): User {
if (this._user == null) {
this._user = this.getLoggedInUser();
}
return this._user;
}
set currentUser(user: User) {
this._user = user;
}
constructor(public usersProvider: Users) {
}
getLoggedInUser(): User {
return this.usersProvider.getByRoute(URL).single();
};
How can I Resolve my Observable to get the entity?
You will need to subscribe to the observable and then assign it to the property on your class.
this.usersProvider.getByRoute(URL).subscribe(next => {
this.currentUser = next;
});
Note that you will need to handle cleanup of this subscription on your own! (this thread will help with that: Angular/RxJs When should I unsubscribe from `Subscription`)
Also, you can't really "resolve" an Observable -- it is not a Promise! Check out this thread for more on that: Angular - Promise vs Observable
I want to apply an asynchronous transformation function on the value emitted by an observable.
#Injectable
export class ApiService{
constructor(private http: HttpClient){}
getSomething(url): Observable<any>{
return this.http.get(url);
}
}
In the code above, I want to apply a transformation function myFunc, which returns a promise, on the value emitted by this.http.get(url).
Normally I would use the map operator of RxJS, but since the transformation function returns a promise, I could not find a way to handle it.
For example, let my function be:
function myFunc(value){
return new Promise((resolve, reject) => {
// modify the value async
resolve(modifiedValue);
// ...
});
}
Is there an appropriate way to handle this task? I think the following is not suitable, am I right?
return this.http.get(url).map(myFunc);
Any help will be much appreciated.
Note: I'm using RxJS 5.5.2
Use the mergeMap operator to take in the response value, perform some modification asynchronously via another Observable operation, and then return the modified value. This operator will merge the values emitted by HttpClient and your modifier Observable and return a single Observable that emits the mutated values.
EDIT: Including the Observable.fromPromise bit from #bygrace's comment for a more complete answer.
i.e.
EDIT: For RxJs v5.5+
import { pipe } from 'rxjs/util/pipe';
import { mergeMap } from 'rxjs/operators';
#Injectable
export class ApiService{
constructor(private http: HttpClient){}
getSomething(url): Observable<any>{
return this.http.get(url).pipe(
mergeMap(myFunc)
);
}
private myFunc(x): Observable<any> {
// do some asynchronous modification that returns an Observable
return Observable.fromPromise(x);
}
}
Pre RxJs v5.5
#Injectable
export class ApiService{
constructor(private http: HttpClient){}
getSomething(url): Observable<any>{
return this.http.get(url)
.mergeMap(data => {
// do some asynchronous modification that returns an Observable
return Observable.fromPromise(data);
});
}
}
See: http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-mergeMap