Table Component with Model-View-Presenter
Hi Ganea, thank you for your kind words 😊
In this diagram, I have modeled an example of the different classes we could have in a use case like that.
Some Service
for retrieving and sending data, for example by GET and POST HTTP requests. A TableContainerComponent
that retrieves data from the service, probably using some Observable
. A presentational TableComponent
which could have an *ngFor
directive iterating over the data and rendering a RowComponent
for each entry.
Each row component could have one or more presenters. In the diagram, I have added a single presenter per row component with a class named RowPresenter
. I would probably additionally have another presenter per row component for handling form controls and validation, called RowForm
for lack of a specific data type. If we were editing heroes, I would call the presenter class HeroForm
.
The user edits a field in the browser, which triggers a native input
event. The presentational row component delegates to the row presenter (or form presenter). Maybe the user has to press a save button.
If the form is valid, a Subject
emits an object representing the submitted form — a simple data structure, not an NgForm
or FormGroup
.The presentational row component is notified of the emitted value and itself emits it through an output property — an EventEmitter
.
The presentational TableComponent
gets notified in a method, it could for example be called onFormSave
or onRowSave
if the output property is named save
. The presentational table component simply emits the submitted form object through its own output property.
Finally, the TableContainerComponent
is notified of the value emitted from TableComponent
and delegates to the service. The service sends for example a POST HTTP request to the back-end which updates the data.
At some point, we detect the state change and, the observable emits a new array of entries, and the table row text is updated. Or if the server state update failed, we could notify the user and roll back the row text.
This is a pretty shallow component tree, so the bucket-brigading (or prop drilling) of emitted value from output to event handler to output is not too bad. If we had a presentational component per cell and a lot of different columns, things could start to become messy and tedious.
In that case, we could choose to create a TableService
whose only purpose is to communicate between the table container component and the row/cell components. However, to use a service other than a presenter (all injectable dependencies fall under the Angular umbrella term service), we would have to introduce a row/cell container component since presentational components should only communicate with the rest of the app through input and output properties.
Adding a container component to a <tr>
, <td>
, or <th>
might simply not be structurally possible with regards to the HTML. In this case, we can replace the row/cell container component with a provider directive.
For details on provider directives, see slides from my talk “Model-View-Presenter with Angular”. I will write an article about them at some point.