Smart Mat-Table part 3 : Inject column
This is the second article of a small series in order to created highly reusable and customizable mat-table components while reducing boilerplate.
In this article we will see how we can make our mat-table component more customizable by providing the possibility to inject a custom column.
The code showed on this article is based on the first part of the serie, but it can be understood without reading the first part.
Introdution
Let’s say we want to display a table of users (with firstname, lastname, mail and job). We will create a UsersTableComponent that will contains the mat-table. This component take as input the datasource and the list of columns to display. See part 1 for more detail about table component.
This UsersTableComponent must be reusable in different contexts. But let’s say in the admin part you want to display the list of users and you want to be able to delete a user. You’ll need a column in the mat-table with a button to delete the user.
As seen in part 1, we dont want our UsersTableComponent to be smart and so we don’t want it to handle the user deletion, it the responsibility of the parent.
We could define a column with the delete button and call an output event when this button is clicked. The parent would receive this event and delete the user. This is a good idea if the delete button is used in multiple time in the app.
But I don’t really like this solution because if we need another button in another page (like an edit button) we will have to define one column and one output event for each specific action. Our UsersTableComponent could quickly become complicated (with many output events and many configurations).
Inject column
One good solution would be to let the parent component inject any HTML template in a custom column. As there is multiple lines, we can’t use a simple ng-content but we will have to use template (because our tempalte will be rendered multiple times).
If you are not confortable with multiple content projection in angular you can check this article. But basically we will use the same mechanism as the mat-table (for header and cell templates).
The solution is quite simple, we will use TemplateRef.
So what is happening here ?
In the HTML we define a column called ‘customTemplate’, this is the column where we will be able to inject content.
We don’t define anything in the column header, we will not need it for now.
The important part is in the mat-cell and in the ts file. In the typescript file we retrieve the template defined by the parent with the @ContentChild decorator. Then we inject this template in the mat-cell with the following line :
<ng-container *ngTemplateOutlet="templateRef; context: getUserViewContext(user)"></ng-container>
We provide a context so the template will be able to access the user.
Et voilà, we can now inject content in the columns cell ! 🍾
Here how we call our TableWithInjectableColumnComponent.
In the ts file we define our datasource and the list of columns we want to display (we need to include the ‘customTemplate’ column to show it in the table component).
In the HTML we define the template we want in the customTemplate column by using the <ng-template>.
We use the let-user to access the user that was injected in the content by the TableWithInjectableColumnComponent.
Ant that it ! We will render this template on each cell of the customTemplate, and the action is manage by our AppComponent (the parent).
Here is a working example on stackblitz.
Note that we need to manually inject the template and we can’t directly use the matColumnDef in the parent component because the mat-table won’t be able to access it.
Improvement
In the example we didn’t have anything in the customTemplate header. We could set a simple input string in the table component to provide a header. If you want to be able to fully customize the header, you can also provide a custom template for it. You’ll need to use directive to differentiate the header and cell template. You can find how to do this in the following article : multiple-content-projections-in-angular
Conclusion
Here how we can provide the possibility to inject a custom template in a table component.
This was the last part of the series about mat-table to make them more reusable, customizable while reducing the boilerplate code.
I hoped this helped some people.
Thank you for reading :) !