- Using Observables with
async
pipe is a perfectly valid option which I use quite often. Angular is able to check when an Observable emits a value and it marks your component to detect changes.
- Immutable.js is as well a valid option, however I personally find a little bit too verbose. On my current project I prefer to use ES6 spread operator. For example to update a property use
const newObj = {...obj, prop: newValue}
, to insert an item to an array const newArray = [...array, newItem]
. However you have to make sure that the whole team adopts this approach as there is no way to enforce the immutability, as with Immutable.js.
My team is using page - component
pattern. For example we need to create a feature to update user details. In this case I'd create user-page.component
and user-form.component
. user-page.component is a container for user-form.component.
user-form.component has ChangeDetectionStrategy.OnPush and it does not interact with the services, it just has a number of input properties (let's say @Input user: User
) and emits @Output events (for example @Output update = new EventEmitter<User>()
). It is a 'dumb' component which just updates the form and emits events.
user-page.component is the one who interacts with the services, sets input properties and listens for user-form.component
events. It's template markup usually only contains child components:
<app-user-form [user]="user$ | async" (update)="onUpdate($event)"></app-user-form>
user$ is an Observable which comes from the service or the store and it is passed into user-form.component through async pipe. onUpdate is function which is called when user clicks Update button on user-form.component and it calls the API service to update the user details.
Following this pattern gives the separation of the components which interacts with the services and the components which only display data, allowing you to have the control of the change detection. By the way NGRX official example app uses the same approach.