Get a Value from a Observable<Value> Object

RxJs get value from observable

To get data from an observable, you need to subscribe:

this.singleEvents$.subscribe(event => this.event = event);

In the template you can directly bind to observables using the async pipe:

{{singleEvents$ | async}}

How to get value from Observable

You need to subscribe to the items$ Observable to get the data.

jobsLength() {
this.searchLogic.items$.subscribe((value: any[]) => {
let jobs: any[] = value;
console.log(jobs);
console.log(jobs.length);
});
}

Sample Solution on StackBlitz



References

Observable (Subscribing)

Get value of Observable<SomeObject> in an object

Angular 8 How to get value from observable in ngOnInit and how do they behave

The getLights function returns an Observable. The Observable itself doesn't block the execution, it only holds the information if a result is available. To get notified when a result is available a subscription with a callback function will be registered on the observable by the subscribe function. Within the callback the resulting data will be present.

As mentioned above the observable does not block the execution of your code, nor does the subscribe call. That means:

  1. A callback will be registered which will print Test: 1.1 but isn't fired yet, because the http request didn't complete yet. So nothing is printed.
  2. The code execution continues and prints Test: 1.2 with undefined as result, because the previous subscription didn't fire and so didn't set a value for this.test1.
  3. Some time later the callback of the subscription is executed because a result arrived and so Test: 1.1 will be printed with the result.

If you want to explicitly block execution after registering a subscription to retrieve the result first, you can do the following:

  async ngOnInit(): void {
console.log("----- ngOnInit -----");
this.test1 = await this.ExperimentalService.getLights().toPromise();
console.log("Test: 1.2", this.test1); // --- 1.2 ---
}

Note: It's important that the ngOnInt itself is awaitable.

This will implicitly register a subscription which will be converted into a Promise. The Promise will hold the last value emitted, after the Observable has completed. Because Angular's Http-Client completes the returned Observable after it received a value, the resulting Promise will be resolved when the request was successful or rejected on an error.

You can turn any Observable into a Promise, but you must ensure that the Observable is completed at some point in the future or it will wait (block) forever. To ensure this, mostly a pipe is registered with the first() operator:

  async ngOnInit(): void {
console.log("----- ngOnInit -----");
this.test1 = this.ExperimentalService.getLights().pipe(first()).toPromise();
console.log("Test: 1.2", this.test1); // --- 1.2 ---
}

Note: first() [doc] will complete the Observeable when the first value is emitted and passes it through to the subscription.
For a http request first() is redundant, because the Observable will be completed at the latest when the http request runs into a timeout.

Get value from Observable on object property

You can use ResultSelector function from a flattening operator (like switchMap, flatMap etc.):

obs = of({ prop1: 'foo', prop2: of('bar') }).pipe(
switchMap(val => val.prop2,
(a, b) => ({a, b})
).subscribe(console.log)

How can I get value of RxJs Observable in a nested array of objects to resolve?

You need to use a "Higher order mapping operator" (switchMap) which will subscribe to your inner observable source and emit the data you need. In your case, you need to make many calls (one for each file), so you can map your file list to an array of observables that emit the desired data. You can use forkJoin again to wrap all these calls into a single observable:

function getData() {
return forkJoin([filesApiRequest, userApiRquest]).pipe(
switchMap(([files, userInfo]) => forkJoin(
// return array of observables
files.getFilesList().map(f => toDataWithImportantInfo$(f, userInfo))
))
);
}
function toDataWithImportantInfo$(file, userInfo) {
const name = file.getname();

return importantInfoCall(userInfo.name, name).pipe(
map(importantInfo => ({
data1,
data2,
name,
...,
hasImportantInfo: importantInfo
})
);
}

forkJoin isn't the best solution when you have a large number of requests, because it will execute them all at the same time (maybe you don't want to kick off 500 http requests at once).

In order to limit the number of requests, we can use merge instead, since it provides a concurrency parameter:

function getData() {
return forkJoin([filesApiRequest, userApiRquest]).pipe(
switchMap(([files, userInfo]) => merge(
files.getFilesList().map(f => toDataWithImportantInfo$(f, userInfo))
, 5)), // MAX 5 CONCURRENT
toArray()
);
}

forkJoin emits an array of all results when all sources complete. merge emits each result individually, so toArray is needed if you want to emit a single array once all the inner sources complete (rather than emitting individually).



Related Topics



Leave a reply



Submit