Skip to main content

Passing observables directly

In this section we will compare the different ways to pass a value to components. The 2 discussed ways are:

  • Passing single values, done via the async pipe
  • Passing the whole observable

Passing values

Here we subscribe to an Observable over the async pipe. Every emitted value triggers the change detection and re-renders the parent component.

This passes the value to the child component. In the child's @Input binding the set method get used to forward the value which is bound to the template by another async pipe.

This in turn triggers another re-rendering, and the value get displayed in the child component.

@Component({
selector: 'parent',
template: ` <user-list [users]="users$ | async"></user-list> `,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ParentComponent {
users$ = userService.users$;
constructor(private userService: UserService) {}
}

@Component({
selector: 'user-list',
template: `
<div *ngFor="let user of users$ | async">
{{ user }}
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ChildComponent {
users$ = this.select('users');
@Input()
set users(users) {
this.set({ users });
}
}

Passing observables

This example in comparison passes the Observable directly to the child component.

Only the first time the parent triggers the change detection and re-renders.

This passes the value to the child component. In the child's @Input binding the connect method get used to forward the value which is bound to the template by another async pipe.

This in turn triggers another re-rendering, and the value get displayed in the child component.

If we compare the number of change detections with the above example where we passed the single values we save 1 rendering per emission. In a real life application only a few of those changes at the right place gives a big impact in performance.

Be careful, passing an Observable as Input does not come without risks

If you override the initial reference with a new Observable, the initial Observable is still connected to state and emissisions of this Observable will still affect state and eventually leading to unexpected changes in your component's view.

As a best practice only pass Observables as Inputs, if the reference is not changing.

@Component({
selector: 'parent',
template: ` <user-list [users]="users$"></user-list> `,
})
export class ParentComponent {
users$ = userService.users$;
constructor(private userService: UserService) {}
}

@Component({
selector: 'user-list',
template: `
<div *ngFor="let user of users$ | async">
{{ user }}
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ChildComponent {
users$ = this.select('users');
@Input()
set users(users$) {
this.connect('users', users$);
}
}