Derek Zeng

Angular data model and Redux

We know that Angular uses component based architecture. Each component is an
independent piece of UI that consists of its own state management, styles and
templates. A component can be composed of other components. Descendent components
may inherit styles from ancestor component due to the use of shadow DOM.

The key idea is simple. We should keep the component as independent as
possible, like pure functions, so that it can be composed, reused and tested easily.

Each component despite having private states, it can declare inputs and outputs.
Both inputs and outputs can be communicated through parent component as well as
service injection.

E.g.

exportclassTimerComponent{// attribute getter that proxies to service// it will detect change every time when service value changesgettimeNow() {returnradioService.timeNow;}constructor(privateradioService: RadioService){}}

Rendered template should always be in sync with the private state of the component controller.

For the example above, the change of radioService.timeNow will cause the UI to be updated
and show the correct time.

There are only a handful of things that can affect private state of a component.

change of inputs

user interaction

If the private state of a component and its decendants only depends on their inputs,
Angular provides an optimization option to skip all the change detection for itself and descendants
unless the source input changed. It's well discussed here by
former Angular team member Victor Savkin. To use this option, just specify change detection strategy.

In Angular change detection, every component has an change detector and they are
run in the end of every event thread (I'll devote another post to talk about how angular decide to trigger change detection). E.g. UI events, ajax events, websocket events, setTimeout
events etc. These are managed by so-called zones.
Change detector for simple objects and primitive values compares the changes by value.
Meaning that, if a=1 previously and a=2 now, it will mark the state dirty and the UI
will be updated later. It also means object change b={time:100} then b['time']=121
won't trigger any change. To remedy this, we can use immutable.js.

If the app uses immutable data type throughout, all the non-interactive component (e.g. reporting table) can be optimized.
Some people think immutable js is too verbose and the API is unfriendly, they rather use the following form:

...returnObject.assign({},state,{change:123});...

This is also valid because the state is re-assigned to a new object, thus considered changed. The only downside of it is when the state object is complicated, this form can be complicated too.

We also can use Observables as source of changes. Angular knows how to subscribe it and listen for changes.

Note the data flow here. Since we can pass input from dependency injection,
we can also send messages directly to injected services. This process of generating
output should not affect the private state of component. When the message is received
by the service, it should update the state of its own and then propagate to all component
that uses the value.

So the changes is modelled as a stream of events, and the events flow back to the source and dispatched to subscribers as their stream of events. Everything seems to be reactive now. There are only observers and subscribers in the system. And most subscriber is also an observer.

@Output in Angular is just one form of stream binding that make the component less dependent on this shared service.

Using service injection to manage global states seems to be a viable solution for large projects.

I was reading the complete redux book recently. The idea of redux is surprisingly
similar to what we have here. Global state management with uni-directional data flow. Every change to
the global state produces a new instance of global state. All listeners
to the changes get notified when it happens. Sources of changes are predictable since
all of them have to go through reducers (redux equivalent of controller).

In Angular, global states are managed by shared services. Global states affect components' private states.
Components are listening to the global state change from @Inputs.
Uni-directional data flow manifests in the form of events output by components and eventually
consumed by shared services and changes broadcast to all listeners.

So, do we need redux to work with Angular? Probably not. Redux's idea is simple. As
long as you have understood it, it's okay to come up with your own shared service
architecture. It's not necessary to include redux styles even if it's not
harmful at all. Redux's functional styles could interfere with Angular's classical style after all.