35

I just started working on an Angular 4 project with material design.

I am currently working with the expansion component, the API states that a disabled expansion panel can't be toggled by the user, but can still be manipulated programmatically. I don't know however, how you can toggle your panel programmatically.

What is the preferred way in Angular to simulate this?

6 Answers 6

42

expanded is set to true to expand the expansion panel and set to false to close the expansion panel. In the following example, expansion panel is programmatically opened and closed. Please refer this link

TS file

import { Component } from '@angular/core';

/**
 * @title Expansion panel as accordion
 */
@Component({
    selector: 'expansion-steps-example',
    templateUrl: 'expansion-steps-example.html',
    styleUrls: ['expansion-steps-example.css']
})
export class ExpansionStepsExample {
    step = 0;

    setStep(index: number) {
        this.step = index;
    }

    nextStep() {
        this.step++;
    }

    prevStep() {
        this.step--;
    }
}

HTML file

<mat-accordion class="example-headers-align">
    <mat-expansion-panel [expanded]="step === 0" (opened)="setStep(0)" hideToggle="true">
        <mat-expansion-panel-header>
            <mat-panel-title>
                Personal data
            </mat-panel-title>
            <mat-panel-description>
                Type your name and age
                <mat-icon>account_circle</mat-icon>
            </mat-panel-description>
        </mat-expansion-panel-header>

        <mat-form-field>
            <input matInput placeholder="First name">
        </mat-form-field>

        <mat-form-field>
            <input matInput type="number" min="1" placeholder="Age">
        </mat-form-field>

        <mat-action-row>
            <button mat-button color="primary" (click)="nextStep()">Next</button>
        </mat-action-row>
    </mat-expansion-panel>

    <mat-expansion-panel [expanded]="step === 1" (opened)="setStep(1)" hideToggle="true">
        <mat-expansion-panel-header>
            <mat-panel-title>
                Destination
            </mat-panel-title>
            <mat-panel-description>
                Type the country name
                <mat-icon>map</mat-icon>
            </mat-panel-description>
        </mat-expansion-panel-header>

        <mat-form-field>
            <input matInput placeholder="Country">
        </mat-form-field>

        <mat-action-row>
            <button mat-button color="warn" (click)="prevStep()">Previous</button>
            <button mat-button color="primary" (click)="nextStep()">Next</button>
        </mat-action-row>
    </mat-expansion-panel>

    <mat-expansion-panel [expanded]="step === 2" (opened)="setStep(2)" hideToggle="true">
        <mat-expansion-panel-header>
            <mat-panel-title>
                Day of the trip
            </mat-panel-title>
            <mat-panel-description>
                Inform the date you wish to travel
                <mat-icon>date_range</mat-icon>
            </mat-panel-description>
        </mat-expansion-panel-header>

        <mat-form-field>
            <input matInput placeholder="Date" [matDatepicker]="picker" (focus)="picker.open()" readonly>
        </mat-form-field>
        <mat-datepicker #picker></mat-datepicker>

        <mat-action-row>
            <button mat-button color="warn" (click)="prevStep()">Previous</button>
            <button mat-button color="primary" (click)="nextStep()">End</button>
        </mat-action-row>
    </mat-expansion-panel>

</mat-accordion>
Sign up to request clarification or add additional context in comments.

5 Comments

Can do this in a loop too: <div *ngFor="let item of items; let i = index"><mat-expansion-panel [expanded]="step===i" (opened)="step=i">
@Kim T did you done this in a loop too: <div *ngFor="let item of items; let i = index"><mat-expansion-panel [expanded]="step===i" (opened)="step=i">
@KimT you solution works fine. thanks for the tip :)
This unfortunately doesn't work if you want multiple panel expanded
Can't modify state in the same digest here, angular will throw and error and it won't work. "[expanded]="step === 0" (opened)="setStep(0)"
19

Another very simple way to handle Mat Expansion panel programmatically is by using local references in Angular

<mat-accordion>
    <mat-expansion-panel #mep="matExpansionPanel">
    </mat-expansion-panel>
</mat-accordion>

Now in HTML(not accessible directly in TS file until we use @ViewChild) file use mep to expand or collapse Expansion panel using something like this:

<button (click)="mep.expanded = true;">Expand</button>
<button (click)="mep.expanded = false;">Collapse</button>

2 Comments

This is the right way to go for the accordion and for a set of expansion panels that are not on a complex loop without setting up anything on the component just to track states. On an accordion which is not multi you want to call the .closeAll() method of the accordion on successful interaction inside any panel. The API has methods for the accordion and for the expansion panels closeAll, open, close & toggle. I think the question is more on how to use those methods instead of knowing much about the template loop or nested loop of items.
Great solution. I had clickable components inside the Expansion Panel and I wanted to have the panel close if one of the components inside the panel was clicked. So I had the #mep reference as shown above and then I had the (click)="mep.expanded = false" defined for the component right inside the body of that same panel. Worked perfectly.
16

Simple implementation for array of panels

<mat-expansion-panel [expanded]="indexExpanded == i" disabled="true" *ngFor="...; let i = index">
    ...
    <button (click)="togglePanels(i)">Toggle</button>
    ...
</mat-expansion-panel>

And in code

indexExpanded: number = -1;

togglePanels(index: number) {
    this.indexExpanded = index == this.indexExpanded ? -1 : index;
}

Comments

12

You can set a var, in this case I used "isExpanded" then use it to open and close set the boolean value.

<mat-expansion-panel
    *ngIf="advertisements.objs"
    [expanded]="isExpanded"
>
</mat-expansion-panel>

If you want to use it with the integrated toogle button instead you must update the "isExpanded" var with the "(opened)" and "(closed)" hooks from mat-expansion-panel.

<mat-expansion-panel
    *ngIf="advertisements.objs"
    [expanded]="isExpanded"
    (opened)="isExpanded = true"
    (closed)="isExpanded = false"
>
</mat-expansion-panel>

1 Comment

This should be the preferred answer!!
9

app.component.html:

<button (click)='xpandStatus = !xpandStatus'>Toggle it</button>
<p>
    <mat-expansion-panel [(expanded)]="xpandStatus">
        <mat-expansion-panel-header>
            <mat-panel-title>This an expansion panel</mat-panel-title>
            <mat-panel-description>xpandStatus is {{xpandStatus}}</mat-panel-description>
        </mat-expansion-panel-header>
        Two-way binding on the expanded attribute gives us a way to store and manipulate the expansion status.
    </mat-expansion-panel>
</p>

app.component.ts

import { Component } from '@angular/core';

@Component({
    selector: 'my-app',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    xpandStatus = true;
}

Here it is live in StackBlitz: https://stackblitz.com/edit/angular-gtsqg8

2 Comments

It's suffiient to write (click)='xpandStatus=!xpandStatus - just for update
Agree @LeO, yes its cleaner
6

Suppose you are looking for the way to trigger open() method from the component on button click, this is what I've been able to get from the material API documentation.

import { ViewChild } from '@angular/core';
import { MatAccordion, MatExpansionPanel } from '@angular/material/expansion';
    
export class MyComponent implements OnInit {
    @ViewChild(MatExpansionPanel) pannel?: MatExpansionPanel; 
    @ViewChild(MatAccordion) accordion?: MatAccordion;
    
    constructor(){}
    
    closePannel() { // for expansion panel only
        if(!this.pannel) { return }
        this.pannel.close();
    }
    
    closeAll() { // for all panels in accordion
        if(!this.accordion) { return }
        this.accordion.closeAll();
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.