Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/angular/lifecycle.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ For more info on the Angular Component Life Cycle events, visit their [component

:::note

Components that use `ion-nav` or `ion-router-outlet` should not use the `OnPush` change detection strategy. Doing so will prevent lifecycle hooks such as `ngOnInit` from firing. Additionally, asynchronous state changes may not render properly.
If your pages keep state in plain fields rather than signals, the component hosting `ion-router-outlet` or `ion-tabs` needs eager change detection, as does every component between it and your application root. A change detection pass starts at the application root and skips a clean `OnPush` view along with everything below it, so an `OnPush` component above the outlet stops updates from reaching the routed pages under it. The pages themselves can use `OnPush`, as long as their state is a signal or they call `markForCheck()`.

On **Angular 18 through 21** this only affects you if you set `OnPush` on those components yourself, because a component that does not declare a strategy is eager.

**Angular 22** makes `OnPush` the default for components that do not declare one, so refer to [Change detection on Angular 22](/docs/angular/zoneless.mdx#change-detection-on-angular-22) for what your app shell has to declare.

:::

Expand Down
21 changes: 20 additions & 1 deletion docs/angular/zoneless.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ You do not need to change these. Angular schedules change detection for them in

:::note[Angular 22]

Angular 22 also makes `OnPush` the default change detection strategy. Under `OnPush`, synchronous state set as a plain field (including in the lifecycle hooks above) no longer re-renders on its own, even though Ionic notifies Angular. Signals still update the view. For the migration path, refer to the [OnPush Change Detection section of the Ionic 9 upgrade guide](/docs/updating/9-0.mdx#onpush-change-detection-on-angular-22).
Angular 22 also makes `OnPush` the default change detection strategy. Under `OnPush`, synchronous state set as a plain field (including in the lifecycle hooks above) no longer re-renders on its own, even though Ionic notifies Angular. Signals still update the view. Refer to [Change detection on Angular 22](#change-detection-on-angular-22) for what this means for your app shell, and to the [OnPush Change Detection section of the Ionic 9 upgrade guide](/docs/updating/9-0.mdx#onpush-change-detection-on-angular-22) for the migration steps.

:::

Expand Down Expand Up @@ -153,6 +153,25 @@ export class AppComponent {
}
```

## Change detection on Angular 22

On Angular 22 a component that does not declare a strategy is `OnPush`. If your pages keep state in plain fields rather than signals, every component from your application root down to the one hosting `ion-router-outlet` or `ion-tabs` (your app shell) must stay eager. A tick starts at the application root and skips a clean `OnPush` view and everything below it, so an `OnPush` ancestor strands the page even when the page itself is eager:

```ts
import { ChangeDetectionStrategy, Component } from '@angular/core';

@Component({
selector: 'app-root',
changeDetection: ChangeDetectionStrategy.Eager,
template: '<ion-router-outlet></ion-router-outlet>',
})
export class AppComponent {}
```

If other components sit between your application root and `ion-router-outlet`, each of them needs the same declaration. Pages that set state through signals, or that call `markForCheck()`, are unaffected: both mark the ancestor chain, so a tick reaches them whatever the shell declares. Converting your pages that way is the alternative to keeping the shell eager.

Hosting an `ion-nav` is fine either way, because its pages are attached as root views and are checked independently of the component hosting them.

## Staying on Zone.js

If you are not ready to adopt zoneless change detection, you can opt back into Zone.js with `provideZoneChangeDetection()`. Refer to the [Keeping Zone.js section of the Ionic 9 upgrade guide](/docs/updating/9-0.mdx#keeping-zonejs) for the exact configuration.