Skip to content

Commit

Permalink
docs: Add after*Render to Component Lifecycle guide
Browse files Browse the repository at this point in the history
  • Loading branch information
devknoll committed Aug 7, 2023
1 parent f12f906 commit 5ddfbdc
Showing 1 changed file with 86 additions and 0 deletions.
86 changes: 86 additions & 0 deletions aio/content/guide/lifecycle-hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,92 @@ data$ = http.get('...').pipe(takeUntilDestroyed());

By default, `takeUntilDestroyed` must be called in an [injection context](/guide/dependency-injection-context) so that it can access `DestroyRef`. If an injection context isn't available, you can explicitly provide a `DestroyRef`.

## Reading and writing the DOM

Sometimes it's necessary to use browser-only APIs to manually read or write the DOM. Angular provides `afterRender` and `afterNextRender` for this purpose.

<div class="alert is-important">

`afterRender` and `afterNextRender` are available for [developer preview](/guide/releases#developer-preview). They are ready for you to try, but they might change before they are stable.

</div>

### One-time initialization

Generally, you will want to use `afterNextRender` to perform any one-time initialization for a third-party library.

```ts
@Component({
selector: 'my-chart-cmp',
template: `<div #chart>{{ ... }}</div>`,
})
export class MyChartCmp {
@ViewChild('chart') chartRef: ElementRef;
chart: MyChart|null;

constructor() {
afterNextRender(() => {
this.chart = new MyChart(this.chartRef.nativeElement);
});
}
}
```

You should use built-in browser APIs like `ResizeObserver` and `IntersectionObserver` wherever possible, instead of attempting to recreate their behaviors yourself. You should use `afterNextRender` to safely initialize such APIs on the browser only.

```ts
@Component({
selector: 'my-cmp',
template: `<span #content>{{ ... }}</span>`,
})
export class MyComponent {
resizeObserver: ResizeObserver|null = null;
@ViewChild('content') contentRef: ElementRef;

constructor() {
afterNextRender(() => {
this.resizeObserver = new ResizeObserver(() => {
console.log('Content was resized');
});

this.resizeObserver.observe(this.contentRef.nativeElement);
});
}

ngOnDestroy() {
this.resizeObserver?.disconnect();
this.resizeObserver = null;
}
}
```

<div class="alert is-important">

As a rule of thumb, `afterNextRender` should be used to observe _discrete_ changes to the DOM, such as element creation or deletion. For reading layout data such as size or location manually, you should generally prefer to use `afterRender` instead.

</div>

### Handling synchronization

As an escape hatch for when the browser does not provide a better API, you can use `afterRender` to read or write the DOM.

```ts
@Component({
selector: 'my-cmp',
template: `<span #content>{{ ... }}</span>`,
})
export class MyComponent {
@ViewChild('content') contentRef: ElementRef;

constructor() {
afterRender(() => {
const elem = this.contentRef.nativeElement;
console.log(`content position: (${elem.offsetLeft}, ${elem.offsetTop})`);
});
}
}
```

## General examples

The following examples demonstrate the call sequence and relative frequency of the various lifecycle events, and how the hooks can be used separately or together for components and directives.
Expand Down

0 comments on commit 5ddfbdc

Please sign in to comment.