bug(rx-controls): state changes in complex trees of linked controls can overwrite each other
Apologies if this doesn't make much sense to other readers, this is a bit of a brain dump so that I don't forget it:
The current control event propagation implementation for this library has a flaw when syncing events in a complex tree of linked controls. Basically, it's possible for a control state to change, emit a change event which starts to propagate through a tree of linked controls, one of those controls early in the process could respond to that state change event by again changing the state of the control which initially kicked off this process, that new state change starts propagating through the tree at the same time as the original state change is still finishing it's propagation through the tree of linked controls. When this happens, the original state change is now stale. If the new, non-stale, state change finishes propagating first, then the stale state change has an opportunity to undo the changes of the non-stale state change. The end result is that some controls receive the correct state change and some controls receive a stale state change.
The best solution I can come up with for this involves a two step process. Controls will have two event sources (i.e. rxjs Subject
). The first source is intended for state syncing and end users are instructed not to use it for any other purpose. I.e. you cannot subscribe to the state syncing observable in order to perform validations or anything other than syncing state.
State change syncing happens first and these changes propagate to completion between all linked controls. After this process is complete, the second source starts emitting the same control events. If a subscription to the second source mutates a control, then the second source emissions are paused and control syncing again happens to completion. After syncing has finished, the second source resumes emitting control events. If a single control has undergone multiple state changes since its second source last emitted, those state changes are merged together into a single state change before being emitted by the second source.
The second source can be subscribed to for business logic, validation, etc. To make this invisible to users, a new syncControl
method can be added. I expect most users will not need to know about this two step syncing process, and will instead simply sync controls using the syncControl
method and subscribe to the second source for responding to control events with business logic.
Implementing this change shouldn't be too hard or add too much size to this library, but it will have some impact on control performance (which may or may not be negligible). More broadly though, allowing controls to sync state in an isolated manner might provide some larger opportunities to simplify the IControlEvent
API.