I have a complex scenario where I need several observable to get some data.
I have a method like the following:
private collectData(): Observable<any> {
const defaultValues = {
prop3: 'textsdfsdf',
prop4: 122323,
};
let result: Observable<any>;
if (this.withDialog) {
result = this._dialog.open(MyDialogComponent).afterClosed();
} else {
result = of({});
}
return result.pipe(map((next) => ({ ...next, ...defaultValues })));
}
This method returns an observable with some kind of data. Some of the data can be entered in a dialog. But entering the data is optional so if this is not wanted only some default data will be returned to the subscriptors.
This method is called by a different method which will for example add some more data or communicates with the backend and it looks like this:
private doSth(): Observable<any> {
const sub = new Subject<any>();
const data = this.collectData();
data.subscribe((next) => {
// Get some data from the backend or do some other stuff
console.log(['Collected data', next]);
const newData = {...next, anotherProp: true};
sub.next(newData);
});
return sub.asObservable();
}
This method should return the data and all other stuff also by using a Observable and is called by a third method:
public click() {
this.doSth().subscribe((next) => {
console.log(['Got some data', next]);
});
}
And here is the problem. As long as I use the dialog everything works fine. But when I don't use the dialog and therefor not the afterClosed()
observable but instead the of({})
observable the last subscribe never gets the data.
You can find the full example here: https://stackblitz.com/edit/stackblitz-starters-qcayhz?file=src%2Fapp.component.ts
Instead of do chaining observables by emiting values from subscription handler, just use pipe
If you are going to use another observable (eg http call) to mutate original result in doSth()
you will then use switchMap
instead of map etc.
public click() {
this.doSth().subscribe((next) => {
console.log(`Got some data. With dialog? ${this.withDialog}`, next);
});
}
private doSth(): Observable<any> {
return this.collectData().pipe(
map(next=>({ ...next, anotherProp: true }))
)
}
private collectData(): Observable<any> {
const defaultValues = {
prop3: 'defaultProp3',
prop4: 9999999999,
};
const result= this.withDialog ? this._dialog.open(MyDialogComponent).afterClosed() : of({});
return result.pipe(map((next) => ({ ...defaultValues,...next })));
}
https://stackblitz.com/edit/stackblitz-starters-1vd2z5?file=src%2Fapp.component.ts
moreover you are covering actual data with default values, while you should do the other way around, therefore it should be
return result.pipe(map((next) => ({ ...defaultValues,...next })));
not the other way around
example output:
Got some data. With dialog? true
{prop3: "defaultProp3", prop4: 9999999999, prop1: "text", prop2: 123, …}
Got some data. With dialog? false
{prop3: "defaultProp3", prop4: 9999999999, anotherProp: true}