As the title states, it seems like the mat-header-cell's interpolated content isn't recalculated when properties change.
That the id0 column generated by the *ngFor would behave like the id column and dynamically update when the week array changes.
For comparison, the <span> within the chevron buttons is also generated using an *ngFor and updates without issue.
The id0 column's header content is not re-evaluated when the week array changes.
https://stackblitz.com/edit/material2-rc0-vb1rjf
Use the chevron buttons to navigate forward and backward. The date of the second id column doesn't change, while the first does.
My use case is that I'd like to have a table based around a week-view, where users can navigate forward and backward. It'd be nice if the mat table headers could dynamically update.
Latest versions of Angular, Material, Typescript, Windows 10, and Chrome.
_tl;dr: Add an index trackBy function to your ngFor_
Lots going on here in the template, I'll try to distill it a bit to show what the issue is.
ngFor is creating a set of columns (in this case, just one column). The table picks up this column id0 and grabs the template to stamp out into the DOM. This template will render changes according to your AppComponent's interpolation checks since it lives in its view.
When you change week, you are telling the ngFor to destroy the id0 column template and create a new one. This is because it assumes that due to a reference change (different week array) that the template is no longer valid and needs to be remade.
The table is told about the new column id0 and so it does register this new template. However, the mat-header-row was told to render the columns data, id, and id0, which it is already doing. As far as it knows, it doesn't need to change and so the table does not re-render with the new template constructed by the ngFor.
So now we are in a state where the table has stamped a particular set of content to the DOM that has been orphaned by its view. The table never re-stamped out the new ngFor column template, which would be updated by the AppComponent view.
So we have two options - (1) tell the table that it should re-stamp out the header cells by making a change to the columns we want to render or (2) do not destroy/build new column definitions when the week array changes.
Since option (1) means we have to actually change the columns (table will see there's no diff), let's go with option (2).
How do we tell ngFor that the template doesn't need to be changed if it already build a template for each array index? By adding a trackBy function that tells the ngFor to identify templates based on their index rather than by object reference.
In the AppComponent, create a TrackByFn that returns the array index:
trackByIndex = i => i;
If the syntax looks odd, this is the same as
trackByIndex = function(i) { return i; }
Now, tell the ngFor about this trackBy:
*ngFor="let d of week; let i = index; trackBy: trackByIndex"
And voila, now the template will be provided to the table and not re-created. Its content will be updated when the view's change detection is run and the table will be consistent with showing this template.
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
_This action has been performed automatically by a bot._
Most helpful comment
_tl;dr: Add an index trackBy function to your
ngFor_Lots going on here in the template, I'll try to distill it a bit to show what the issue is.
ngForis creating a set of columns (in this case, just one column). The table picks up this columnid0and grabs the template to stamp out into the DOM. This template will render changes according to yourAppComponent's interpolation checks since it lives in its view.When you change
week, you are telling thengForto destroy theid0column template and create a new one. This is because it assumes that due to a reference change (differentweekarray) that the template is no longer valid and needs to be remade.The table is told about the new column
id0and so it does register this new template. However, themat-header-rowwas told to render the columnsdata,id, andid0, which it is already doing. As far as it knows, it doesn't need to change and so the table does not re-render with the new template constructed by thengFor.So now we are in a state where the table has stamped a particular set of content to the DOM that has been orphaned by its view. The table never re-stamped out the new
ngForcolumn template, which would be updated by theAppComponentview.So we have two options - (1) tell the table that it should re-stamp out the header cells by making a change to the columns we want to render or (2) do not destroy/build new column definitions when the week array changes.
Since option (1) means we have to actually change the columns (table will see there's no diff), let's go with option (2).
How do we tell
ngForthat the template doesn't need to be changed if it already build a template for each array index? By adding atrackByfunction that tells thengForto identify templates based on their index rather than by object reference.In the
AppComponent, create aTrackByFnthat returns the array index:If the syntax looks odd, this is the same as
Now, tell the
ngForabout this trackBy:And voila, now the template will be provided to the table and not re-created. Its content will be updated when the view's change detection is run and the table will be consistent with showing this template.