|
2 | 2 |
|
3 | 3 | Here is a set of best practices using Angular.
|
4 | 4 |
|
| 5 | +## Architecture: Smart Components and Presentational Components |
| 6 | + |
| 7 | +The overall Angular application could be organized into two types of components: [smart components and presentational components](https://blog.angular-university.io/angular-2-smart-components-vs-presentation-components-whats-the-difference-when-to-use-each-and-why/). |
| 8 | + |
| 9 | +- Smart component: aslo called application level component or container component. A smart component usually retrieves data using services and may use presentational components to display data. |
| 10 | +- Presentational components: also known as pure component or dumb component. It receives its data from parent smart component using `@Input()` binding. It uses `@Output()` event to communicate with its parent component. |
| 11 | + |
| 12 | +If there are multiple level between a smart component and a presentational component, use RxJS's `subject` to communicate via a service. This should be carefully documented in all components involved in the communication. |
| 13 | + |
| 14 | +The `ngrx/store` is not recommended because the depency injection and RxJS can solve most problems in a more elegent way. |
| 15 | + |
| 16 | +## Change Detection Strategy |
| 17 | + |
| 18 | +It is recommended to use the `ChangeDetectionStrategy.OnPush` strategy for better performance. Correspondingly, use `@Input` with immutable objects and the `async` pipe in components. |
| 19 | + |
| 20 | +The default change detection strategy `ChangeDetectionStrategy.Default`runs change detection for all possible operations, thus it is not efficient in many cases. When use `ChangeDetectionStrategy.OnPush` change detection strategy, Angular runs change detection only in two cases: 1) The `@Input` reference changes (the `===` comparison changes), and 2) A DOM event such as a button click originated from the component or one of its children. It ignores all timers, promises and HTTP events. An exception is that when you use the `async` pipe to display data from asynchrounous operations like HTTP requests or promises, the `async` pipe marks the component to be checked by calling the `ChangeDetectorRef.markForCheck()` method. |
| 21 | + |
| 22 | +Use asynchronous methods to update properties and know the `NgZone` concept to avoid the `ExpressionChangedAfterItHasBeenCheckedError` error. |
| 23 | + |
5 | 24 | ## Constructor and Lifecycle Hooks
|
6 | 25 |
|
7 | 26 | For property bindings and change detection theories, check [these blogs](https://blog.angularindepth.com/these-5-articles-will-make-you-an-angular-change-detection-expert-ed530d28930).
|
8 | 27 |
|
9 | 28 | Simply, follow the following rules:
|
10 | 29 |
|
11 | 30 | - Use `constructor()` only for dependency injection.
|
12 |
| -- Use `ngOninit()` to initialize data-bound properties. |
13 |
| -- Use `ngAfterViewInit()` when you need to do something after the component view is initialized. The `@ViewChild` fields are initialized when this hook is called. |
| 31 | +- Use `ngOninit()` to initialize data-bound properties or subscribe to third-party widget events. |
14 | 32 | - Use `ngDestroy()` to clear resources such as unsubscribing from observables.
|
15 |
| -- Use asynchronous methods to update properties and know the `NgZone` concept to avoid the `ExpressionChangedAfterItHasBeenCheckedError` error. |
16 | 33 |
|
17 |
| -## Change Detection Strategy |
| 34 | +Use the other hooks only if you fully understand the consequences: |
18 | 35 |
|
19 |
| -It is recommended to use the `ChangeDetectionStrategy.OnPush` strategy for better performance. Correspondingly, use `@Input` with immutable objects and the `async` pipe in components. |
20 |
| - |
21 |
| -The default change detection strategy `ChangeDetectionStrategy.Default`runs change detection for all possible operations, thus it is not efficient in many cases. When use `ChangeDetectionStrategy.OnPush` change detection strategy, Angular runs change detection only in two cases: 1) The `@Input` reference changes (the `===` comparison changes), and 2) A DOM event such as a button click originated from the component or one of its children. It ignores all timers, promises and HTTP events. An exception is that when you use the `async` pipe to display data from asynchrounous operations like HTTP requests or promises, the `async` pipe marks the component to be checked by calling the `ChangeDetectorRef.markForCheck()` method. |
| 36 | +- Use `ngAfterViewInit()` when you need to do something after the component view is initialized. The `@ViewChild` fields are initialized when this hook is called. |
| 37 | +- Use `ngOnChanges()` if you want to track parent bound `@Input` properties. |
| 38 | +- Use `ngDoCheck()` if you want to track self-component peroperties and calculated properties. For example, `ngDoCheck() { this.time = Time.getCurrentTime() }`. |
22 | 39 |
|
23 | 40 | ## Forms
|
24 | 41 |
|
@@ -48,6 +65,6 @@ createForm() {
|
48 | 65 |
|
49 | 66 | ## Dependency Injection
|
50 | 67 |
|
51 |
| -- Use `providedIn: 'root'` for all services which should be available as singletons. |
| 68 | +- Use `providedIn: 'root'` for all services, either global or local, which should be available as singletons. |
52 | 69 | - Use file structure to scope services. A module can only use services that are in its subfoler or share the same parent folder.
|
53 | 70 | - Use `providers: []` for services that 1) need initialization (such as `myService.forRoot()`) or 2) inside `@Component` or `@Directive` for scoped mulitple-instance services.
|
0 commit comments