How to change toggle icon of mat-expansion-panel?
Asked Answered
C

7

18

The default icon for toggling a mat-expansion-panel is >. Setting hideToggle true just hides the toggle icon. Is there any way to change it? I found nothing in the official documentation. I want to use + or - icon if the state is closed or opened respectively.

Caspar answered 16/6, 2019 at 13:19 Comment(0)
A
21

As stated on the Angular Material Expansion Panel documentation, we can customise the stylings of the mat-extension-panel-header, which will in turn allow you to customise the icons.

First and foremost, you hide the original icon by setting the hideToggle attribute to true. In addition, we assign the event bindings (opened) and (closed) to the panelOpenState property. You may check out the other properties of mat-expansion-panel over here.

<mat-expansion-panel (opened)="panelOpenState = true" (closed)="panelOpenState = false"hideToggle="true">

Next, on your mat-panel-description, you include the custom icons required. The icons shown will be dependent on the state of the panel. For this example, we are using the icons from Material Icons.

<mat-panel-description>
  <mat-icon *ngIf="!panelOpenState">add</mat-icon>
  <mat-icon *ngIf="panelOpenState">remove</mat-icon>
</mat-panel-description> 

I have edited the original sample from the Angular documentation, such that it is using the custom + and minus icons to denote the expanded/collapsed state of the mat-extension-panel. You may access the demo over here.

Ailin answered 16/6, 2019 at 13:39 Comment(5)
The solution works fine for one mat-expansion-panel. What to do if I have multiple expansion panels? The panelOpenState is a variable that will be shared by every expansion panels. One panel expands, all the icon changes from + to zero because every expansion panels share the same variable panelOpenState. I thought one solution: to give every panel a hardcoded id (like panel1, panel2, etc.) and using opened and closed event I can change the value in the <mat-icon> tag. But I think its a very tidious idea. There must be a better generic way than hard-coded solution.Caspar
Hmm.. I see..Yeahh you got a point!! I didn't think of it when I came up with that solution!Ailin
In addition to the limitation described by @samiul-alam, I find another problem with this approach: it does not work for togglePosition="before"Pother
Another tweak is that you don't need the panelOpenState property if you use a template reference: <mat-expansion-panel #panel "hideToggle="true"> with <mat-panel-description><mat-icon *ngIf="!panel.expanded">add</mat-icon><mat-icon *ngIf="panel.expanded">remove</mat-icon></mat-panel-description>. By defining panel as a template reference, you can access the expanded state of the mat-expansion-panel directly, removing the need for a variable in the component.Vesuvianite
This worked like a charm! Great comments as well!!! I did not use the template variable but just used one arrow icon and did a transform: rotate(...deg); to get it to be the direction I needed it to be depending on if the dropdown is expanded or not. I used the .expanded class on the mat-expansion-panel-header to determine when to apply rotate.Battle
D
12

There is a class .mat-expanded which is applied to a <mat-expansion-panel>

So you can just use CSS to change the icon, no JS required.

  • To change arrow down to up:
.mat-expanded .mat-expansion-panel-header-title .material-icons{
  transform: rotate(180deg);
}
    <mat-panel-title>
      <span class="material-icons">expand_more</span>
      Panel with icon `transform`
    </mat-panel-title>
  • To swap icons:
.mat-expansion-panel-header-title .open,
.mat-expanded .mat-expansion-panel-header-title .close{
  display: inline-block;
}
.mat-expanded .mat-expansion-panel-header-title .open,
.mat-expansion-panel-header-title .close{
  display: none;
}
    <mat-panel-title>
      <span class="material-icons open">open_in_full</span>
      <span class="material-icons close">close_fullscreen</span>
      Panel with open/close
    </mat-panel-title>

Here is a link to a demo https://stackblitz.com/edit/angular-e2fnlm

CSS solution works with multiple Material Expansion Panels

Dire answered 8/8, 2020 at 22:49 Comment(0)
B
7

Give the name to the mat-expansion-panel say panel in the example. Use expanded property of the mat-expansion-panel to show or hide the + or - icon.

 <mat-expansion-panel #panel  hideToggle>
        <mat-expansion-panel-header>
             <mat-panel-title>
                        Buttons
             </mat-panel-title>
             <mat-icon >{{panel.expanded? 'remove' : 'add'}}</mat-icon>
                        
        </mat-expansion-panel-header>
                   
  </mat-expansion-panel>
Brashy answered 20/7, 2020 at 18:44 Comment(0)
S
2

For those interested, here is an example that works with multiple Expansion Panels:

View:

<mat-accordion>
  <mat-expansion-panel *ngFor="let panel of panels; let i = index"
      (opened)="panelOpenState[i] = true" (closed)="panelOpenState[i] = false"
      hideToggle>
    <mat-expansion-panel-header>
      <mat-panel-title>
        {{panel.title}}
      </mat-panel-title>
      <span *ngIf="!panelOpenState[i]">Show</span>
      <span *ngIf="panelOpenState[i]">Hide</span>
    </mat-expansion-panel-header>

    Panel Content
  </mat-expansion-panel>
</mat-accordion>

Component:

export class PanelComponent implements OnInit {
  panels: [];
  panelOpenState: boolean[] = [];

  ngOnInit() {
    // loop to add panels
    for (let i = 0; i < 5; i++) {
      this.panels.push({ title: i });
      this.panelOpenState.push(false);
    }
  }
}
Solita answered 8/5, 2020 at 17:47 Comment(0)
M
1

You can use mat-icon in the mat-expansion-panel-header.

Please check this working example on stackblitz.

For a + icon you can use <mat-icon>add</mat-icon>

<mat-accordion>
  <mat-expansion-panel hideToggle>
    <mat-expansion-panel-header>
      <mat-panel-title>
        Personal data
      </mat-panel-title>
      <mat-icon>add</mat-icon>
  </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>
Misalliance answered 16/6, 2019 at 13:39 Comment(1)
HI, thanks for sharing code, it is working fine when we use single accordion, but if we have multiple expansion panel, then it does not work properly.it does not open and close properly.it will be great help if you can suggest me some hacks or where is mistake i just copy and pasted your codeReal
T
0

This works even when you iterate it in for loop all together with the mat-expansion-panel

  1. set your icons - e.g. "add" and "remove" aka + and -

  2. decorate them with classes ('close' on add (+) if you want to show + on closed panel) so it looks like this

     <mat-expansion-panel-header>
       <mat-icon class="close">add</mat-icon>
       <mat-icon class="open">remove</mat-icon>
       ...
    
  3. add this style in scss (in case of need edit selector. I have structure as above directly under 'mat-expansion-panel-header'

     ::ng-deep mat-expansion-panel-header.mat-expanded > span > mat-icon.open {
         display:block;
     }
     ::ng-deep mat-expansion-panel-header:not(.mat-expanded) > span > mat-icon.open {
         display:none;
     }
    
     ::ng-deep mat-expansion-panel-header.mat-expanded > span > mat-icon.close {
         display: none;
     }
    
     ::ng-deep mat-expansion-panel-header:not(.mat-expanded) > span > mat-icon.close {
         display: block;
     }
    
Thither answered 25/6 at 11:4 Comment(0)
V
-2

add hideToggle to mat-expansion-panel :

<mat-expansion-panel (opened)="panelOpenState = true"
                             (closed)="panelOpenState = false" hideToggle>

and add icon:

 <mat-expansion-panel-header>
            <mat-icon>add</mat-icon>
Verb answered 25/8, 2020 at 10:43 Comment(1)
This seems like an incomplete thought and just a copy of the other answers.Battle

© 2022 - 2024 — McMap. All rights reserved.