Necrolyte
Necrolyte

Reputation: 53

Angular Material Button Toggle to switch between layout/views

I'm building an Angular 7 site with Angular Material (I'm kind of new in web dev). I'm stuck with something that seems ridiculously trivial: in my products.component I want to use the Angular Material Button Toggle to switch between a grid view (default) and a list view of my products. A feature we see on so many ecommerce. Here is my simplified code:

<mat-button-toggle-group value="grid">
    <mat-button-toggle value="grid">
        <mat-icon>view_module</mat-icon>
    </mat-button-toggle>
    <mat-button-toggle value="list">
        <mat-icon>view_list</mat-icon>
    </mat-button-toggle>
</mat-button-toggle-group>

The grid view of the products (has to be the default view):

<div *ngFor="let product of products">
    <a>
       ...
    </a>
</div>

The list view of the products:

<div>
    <ul>
        <li *ngFor="let product of products"></li>
    </ul>
</div>

I've tried with ViewChild, NgModel, NgSwitch, TemplateRef, all kind of solutions... I'm missing something and I have spend a considerable time on this so I'm relying on you! Thanks in advance


Edit: without routing implemented my app.component looks like this:

<app-header>
<app-products>
<app-footer>

My products.component is built like this (simplified)

<mat-toolbar>
    <mat-button-group>
    ...
    </mat-button-group>
</mat-toolbar>
<div class="container">
    <div *ngFor="let product of products">
        <a>
        ...
        </a>
    </div>
    <div>
        <ul>
            <li *ngFor="let product of products">
            ...
            </li>
        </ul>
    </div>
</div>

So all in one component. Create two child components for the grid view and the list isn't the way to go, right ? I've tried anyway by moving the code inside the div.container in another component where I've experiment with this:

@Component({ 
templateUrl:
`
    <div #listTemplate>
      <p>List View</p>
    </div>

    <div #gridTemplate>
      <p>Grid View</p>
    </div>
`
})
export class ... {
    @ViewChild('listTemplate') listTemplate: TemplateRef<any>;
    @ViewChild('gridTemplate') gridTemplate: TemplateRef<any>;
}

But I've failed to bind this with the click on the button toggle. The Material Button Toggle emits a change event on click so it should let me use ngModel. I've tried something like this:

<mat-toolbar>
    <mat-button-group>
        <mat-button-toggle (click)="change(view.value)">...</mat-button-toggle>
        <mat-button-toggle (click)="change(view.value)">...</mat-button-toggle>
    </mat-button-group>
</mat-toolbar>
<div class="container">
    <div *ngFor="let product of products" [(ngModel)]="grid" #view>
        <a>
        ...
        </a>
    </div>
    <div [(ngModel)]="list" #view>
        <ul>
            <li *ngFor="let product of products">
            ...
            </li>
       </ul>
    </div>
</div>

But don't know how insert the appropriate template with change(value). ngTemplateOutlet could be an appropriate solution but I haven't tried it. Obviously I'm lacking some understanding of this Angular features but I've succeeded in implemeting other things in my site, so I want to achieve this one.

Upvotes: 3

Views: 9841

Answers (1)

zer0
zer0

Reputation: 5017

Here is a working sample of the Toggle button on StackBlitz.

<mat-button-toggle-group [value]="toggle" (change)="toggleView($event)">
  <mat-button-toggle [value]="true">
      Grid
  </mat-button-toggle>
  <mat-button-toggle [value]="false">
      List
  </mat-button-toggle>
</mat-button-toggle-group>
<span>Current Value: {{toggle}}</span>
<div *ngIf="toggle">Grid</div>
<div *ngIf="!toggle">List</div>

TypeScript:

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

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

  toggleView(change: MatButtonToggleChange){
    this.toggle = change.value;
  }
}

Upvotes: 3

Related Questions