tpdietz
tpdietz

Reputation: 1368

Error when passing part of an observable object as input to angular component?

I've got a question about a race case I'm seeing with my Observable. This is probably more of a general observable/angular problem. When transitioning from one route to the other, I'm seeing a cannot read 'property' of undefined error from a controller that is destroying as we transition to a new route. In my controller, I've got a method like so:

// this.setting$: Observable<object>;

get name$: Observable<string> {
    return this.settings$.pipe(map(settings => settings.name));
}

That above function is throwing the error. In the same components template, I've got:

<some-component [name]="name$ | async"></some-component>

When I transition to the new page, the this.settings$ selector returns undefined and my controller's code throws an error. The odd thing is my component has already destroyed when the console error occurs (tested via console logging ngOnDestroy).

I've found two solutions that fix my problem. solution A:

// Parse the name property in the template - which I think looks ugly
<some-component [name]="(settings$ | async).name"></some-component>

Or, solution B:

// Pass entire settings object into component and parse out name in some-component controller - yuck
<some-component [settings]="settings$ | async"></some-component>

I dont like either solution. Solution A I dont like because it looks clunky in the template- but maybe I should just deal with it. Solution B I dont like because its not exactly an elegant solution.

I dont quite understand why either solution fixes the problem. Can anyone pin point why this problem exists? Especially considering the component has been destroyed. This feels like an orphaned subscription, but I dont see how.

EDIT: I've created a stackblitz that reproduces the problem. https://stackblitz.com/edit/ngxs-repro-ndaco6 . There are three routes: /home, /profile, /settings. /profile and /settings depend on an account id being in the url. /home does not. When you route from /profile to /home, note there is no console error. However, when you route from /settings to /home, there is a console error. Can somebody help me understand why? Note, the SettingsComponent that throws the error is already destroyed when the error is thrown.

Upvotes: 0

Views: 439

Answers (2)

Adrian Brand
Adrian Brand

Reputation: 21648

account$ is emitting undefined before the component is destroyed, this is happening in your route selector during routing before the component is destroyed.

Try

age$ = this.account$.pipe(
  tap(val => {
    console.log('Account obs val is', val);
  }),
  map(account => account.age)
);

https://stackblitz.com/edit/ngxs-repro-ceetsi?file=src/app/components/settings.component.ts

You can see account$ emits undefined before the component is destroyed.

It only happens when you nav from settings to home, it is your route selector causing it to emit undefined.

Upvotes: 0

Adrian Brand
Adrian Brand

Reputation: 21648

Use

name$ = this.settings$.pipe(map(settings => settings ? settings.name : null));

It does a check to see if the settings has a value and reuses the same observable, no need to recreate the observable each time you access the getter.

Upvotes: 1

Related Questions