NOTE: for simplicity consider the component depths as:
- Smart (grand)parent level 0
- dumb child level 1
....
- dumb grandchild level 2
....)
(UPDATE: 02-07-2019: This post was getting dated--added the 'store/ngrx' pattern)
So after looking into this further, when it comes to how best to communicate down and up a nested component chain, there seems to be really only two options -- a Faustian bargain between:
EITHER
OR
OR:
I'm personally a proponent of utilizing smart and presentational ('dumb') components. Adding a 'store' should also be done selectively as it significantly increases the costs of the process ranging from architecture, consistent implementation patterns, development, and maintenance to on-boarding of new personnel. Nominally, a 'dumb' component only needs @Inputs and @Outputs and that's it. It does not care how deep or shallow it is in a component tree--that's the applications problem. In fact it doesn't care what application uses it in the first place. Meanwhile, a deep down component isn't very dumb or transportable if an application specific service is injected into it. BTW, the counter-part 'smart' component is really providing intermediary services (via a first class @Injectable service or redux-like store) to whichever dumb component in its family tree that needs it. The smart component also doesn't care about components beyond its immediate child's @Inputs as long as the grandchildren somehow signal up a service/store action needs to be taken (again via the @Input/@Output chain). This way a smart component also becomes transportable across application lines.
Given this, the Faustian bargain, IMO, leans towards utilizing an @Input/@Output chain with all the mentioned issues it brings with it. That said, I'm keeping an eye on this and welcome clean and decoupled alternatives if anyone knows of any.