Reputation: 123
I need to dynamically render a list of expansion panels inside an accordion.
The markup is pretty straight forward:
<mat-accordion *ngIf="currentVenue">
<mat-expansion-panel *ngFor="let gig of currentVenue.gigs" expanded="false">
<mat-expansion-panel-header>
<mat-panel-title>
{{gig.name}}
</mat-panel-title>
<mat-panel-description>
Type your name and age
</mat-panel-description>
</mat-expansion-panel-header>
</mat-expansion-panel>
</mat-accordion>
Unfortunately this results in an unusable control. The header is not opening the panel and the whole accordion functionality is messed up as well. I need to click outside of the control and then randomly it opens one child expansion panel (or not).
If I in turn use a "static" approach (I copied this from the samples' code), everything works as intended:
<mat-accordion>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Personal data
</mat-panel-title>
<mat-panel-description>
Type your name and age
</mat-panel-description>
</mat-expansion-panel-header>
<mat-form-field>
<input matInput placeholder="First name">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Age">
</mat-form-field>
</mat-expansion-panel>
[...]
</mat-accordion>
My guess is, that it has to do with *ngIf
and the way the controls are created.
I'm using Angular Material 6.4.7 and Angular 6.1.10
Upvotes: 2
Views: 12517
Reputation: 7427
You're right about *ngIf
doing some funny stuff here. It's a structural directive, so at a low level, Angular renders it differently from other components. This rendering can interfere with other structural directives, so Angular only allows a template to be bound to one structural directive at a time.
But good news! The asterisk in the name is just syntactic sugar for what structural directives really do. If we de-sugar the name and bind it to a template explicitly, enveloping the accordion itself, Angular will be able to render the nested directives with this template context instead of using the component's template:
<ng-template [ngIf]="currentVenue">
<mat-accordion>
<mat-expansion-panel *ngFor="let gig of currentVenue.gigs" expanded="false">
<mat-expansion-panel-header>
<mat-panel-title>
{{gig.name}}
</mat-panel-title>
<mat-panel-description>
Type your name and age
</mat-panel-description>
</mat-expansion-panel-header>
</mat-expansion-panel>
</mat-accordion>
</ng-template>
Note how the ngIf
is now bound like a regular directive. This can only be used on ng-template
tags. Without the asterisk, Angular won't try to bind a different template to it and nested directives will work.
We could also give the repeated items their own ng-template
and ngForOf
directives, but the syntax with [ngIf]
is much cleaner.
Upvotes: 2