Skip to main content

Handling view and content queries (parent)

ViewChild example (rxLet)

Structural directives maintain EmbeddedViews within a components' template. Depending on the bound value as well as the configured RxRenderStrategy, updates processed by the @rx-angular/template directives can be asynchronous.

Whenever a template gets inserted into or removed from its parent component, the directive has to inform the parent in order to update any view or content query (@ViewChild, @ViewChildren, @ContentChild, @ContentChildren).

Take a look at the following example:

@Component({
selector: 'app-list-component',
template: `
<div #myDiv *rxLet="state$; let state"></div>
<button (click)="append()">append</button>
`,
})
export class AppListComponent {
@ViewChild('myDiv') myDiv: ElementRef<HTMLElement>;

append() {
this.myDiv.nativeElement.appendChild('span');
}
}

AppListComponent has a @ViewChild view query where it queries for an ElementRef<HTMLElement> with an rxLet applied to it. In this case AppListComponents state is dependent on its ViewChild, it needs to get informed about updates of its child views, otherwise its append() method will fail.

This is a known issue which has never been solved for structural directives. Especially in combination with CD OnPush see here: (https://github.com/angular/angular/pull/35428)

rxLet tries to automatically solve this issue for you by calling detectChanges on its parents ChangeDetectorRef whenever a view was inserted or removed from its template.

However, this is a potential performance bottleneck, as in most cases the parent component doesn't need to get updated. There are two ways to opt-out from parent notifications.

ContentChild example (rxFor)

@Component({
selector: 'app-list-component',
template: ` <ng-content select="app-list-item"></ng-content>`,
})
export class AppListComponent {
@ContentChildren(AppListItemComponent)
appListItems: QueryList<AppListItemComponent>;
}

The usage of AppListComponent looks like this:

AppListComponent has a contentOutlet where it expects AppListItemComponents to be inserted into. In this case AppListComponents state is dependent on its ContentChildren. This situation leads to the problem that AppListComponent needs to get informed about updates of its child views. This is a known issue which has never been solved for ngFor (or other structural directives) especially in combination with CD OnPush see here: (https://github.com/angular/angular/pull/35428) RxFor solves this issue for you by providing a simple input parameter parent: boolean. If value is set to true (default is true), *rxFor will run change detection for it's defining Component. This will also update its own view and content queries as well as those of its child components.

The usage of AppListComponent looks like this:

<app-list-component>
<app-list-item
*rxFor="
let item of observableItems$;
parent: true;
"
>
<div>{{ item }}</div>
</app-list-item>
</app-list-component>

parent Input

All structural directives (*rxLet, *rxFor, etc.) can be configured to omit the parent notifications with the parent flag.

Take a look at the following example:

@Component({
selector: 'app-list-component',
template: ` <div *rxLet="state$; let state; parent: false"></div> `,
})
export class AppListComponent {}

RX_RENDER_STRATEGIES_CONFIG

You can also set the parent config globally by providing a RX_RENDER_STRATEGIES_CONFIG. See more about configuration under render strategies especially the section usage-in-the-template

@NgModule({
providers: [{
provide: RX_RENDER_STRATEGIES_CONFIG,
useValue: {
parent: false // this applies to all RxLets
}
}]
})