I have a properties json file in location assets/constants/props.json
. In the json file I have one key names someValue
and its value is abc
.
The json file looks like this:
I have a service file constants.service.ts
and the 2 methods in it:
public getJson(url : string) : Observable<any> {
return this.http.get(url);
}
public someValue() : string {
return this.getJson("assets/constants/props.json").subscribe(async (data: any) => {
return await data.someValue;
});
}
And also in one of my components I use this value:
private someValue! : string;
ngOnInit() {
this.someValue = this.server.someValue();
}
In this way I get: Type 'Subscription' is not assignable to type 'string'.
error.
I have tried many ways and none of them worked:
make someValue()
method returns Promise<string>
make ngOnInit()
async method and use await
in it
using unsubsribe
, toPromise()
, toString()
...
And much more...
Every solution gave me a different error message, or gave me a wrong value [object object]
instead of "abc".
I have tried all the possible options, and I can't get the result I expect.
My question is how to use observables
, async
, await
, subscribe
correctly
There is no way you can directly convert either an Observable<string>
or a Promise<string>
into a string
, because it makes no sense semantically.
Both Observables and Promises represent values that are not available yet but may be provided at some point in the future. Because all JavaScript I/O APIs I am aware of are designed non-blocking, you can't let your process sit around , wait and do nothing (i.e. block) until an I/O operation completes.
Instead, you register a callback function that will get invoked at a later point in time when a value is available. This is what subscribe
in RxJS or then
in Promises is for.
The async
and await
keywords are just syntax sugar for creating Promises and registering callbaks on them, respectively. Under the hood, async it just transforms the code that follows it into as callback function and registers it at a Promise. So in your example, it does nothing useful at all.
There are several ways you can get a value from an Observable.
Here's the simplest:
ngOnOnit() {
this.server.someValue().subscribe(value => this.someValue = value);
}
But this is a bit unwieldy, and there are some caveats with that, such as potential memory leaks.
There is a better way: Just let Angular deal with it, using AsyncPipe. Just expose the Observable
directly to the template and let Angular subscribe to it:
someValue$: Observable<string>; // The "$" sign is just naming convention to tell Observables and plain values apart
ngOnInit() {
this.someValue$ = this.server.someValue();
}
<p>Your value: {{someValue$ | async}}</p>
As a sidenote, all of this will hopefully get much easier with Angular 16 when it gets support for Signals.