I've read that Angular 2 change detection... gets stable after a single pass
Angular 2 doesn't "get stable". With Angular 2 apps, we are responsible for writing our app in such a way that it is always stable after a single pass.
By default (e.g., you are not using the OnPush
change detection strategy on any components, nor did you detach() any components), change detection works as follows:
- A Zone.js monkey-patched asynchronous event fires – e.g., a
(click)
event, an XHR response, a setTimeout()
timer. The callback associated with that event runs, which can change any view or application data in our app. Then, because of the monkey-patch, Angular change detection runs. In other words, by default (e.g., you are not manually triggering change detection), only a monkey-patched asynchronous event triggers change detection.
- Starting from the root component, and traversing down through the component tree (depth-first traversal), each data binding is checked for change. If a change is found the change is "propagated". Depending on the template binding type, propagation may
- propagate the changed value to the DOM. E.g., when
{{}}
binding is used, the new value is propagated to the textContent
property of the appropriate DOM element.
- propagate the changed value to a child component. E.g., when using input property binding (
[childInputProperty]="parentProperty"
), the new value is propagated to the child input property.
- If you are in dev mode, all of the components are dirty checked again, but no propagation occurs. This second dirty check helps us find problems with our code, e.g., if we violated the idempotent rule, which is fancy way of saying that one of our bindings (its template expression) has side effects. In other words, the extra dev mode checks let us know if if our code is not stable after a single pass.
Side effects are not allowed in Angular 2. In reference to your question, a child component therefore must not modify a parent property as a result of input property propagation. So, you could say that Angular 2 "resolves" the situation you asked about by not allowing it.
This is not as bad as it might sound. The only way I'm aware of that an input property propagation can change a parent property is if the child component implements a setter method for the input property which modifies another property that the parent displays in its template. (Here's an old plunker that does this -- see the @Input set backdoor()
method.) Normally you wouldn't do this. If you do need to do this, then Günter's comment is spot on: do the change inside a setTimeout()
, hence it will be part of the next change detection cycle.
I want to reemphasize: event handlers run before change detection, so they are free to change any data in our app -- local/component view data, application data, whatever. So in an event handler, a child component is free to change parent data. E.g., suppose both parent and child have a reference to the same array. When an event handler runs, the parent and/or child component can modify that array.
So if you make changes in event handlers, no problem. There is only a problem if your setter does something odd.