NOTE: for simplicity consider the component depths as:
- Smart (grand)parent level 0
- dumb child level 1
....
- dumb grandchild level 2
....)
Input() and Output() bindings are also a perfectly legitimate way to handle this. Let the smart component handle the logic of generating the values, and then use Input() and Output() to simply pass and receive the values along the component chain.
Of course, this points to one of the downsides of the smart/view approach: more files; more boilerplate. That's why I wouldn't argue for a single approach that's one-size-fits-all. Rather, choose an approach that makes sense in your current context (both for the app and for your organization).