Search code examples
angularangular-material

Implementation of a close button for a component within a dialog


I have a component A that should normally be displayed without a close button. However, when I open component A in a dialog, I want to add a Close Button to close the dialog.

How can I implement this behavior in Angular? Is there an elegant way to determine if the component is open in a dialog so I can add a Close Button accordingly?

Thanks in advance for your assistance!

Here is my code: https://stackblitz.com/edit/stackblitz-starters-p4pfnr?file=src%2Fapp%2Fb%2Fb.component.ts

FULL CODE:

popup html

<form [formGroup]="form" class="">
  <h1>App A</h1>

  <mat-form-field>
    <mat-label>Email</mat-label>
    <input
      matInput
      type="email"
      formControlName="email"
      placeholder="Ex. pat@example.com "
    />
  </mat-form-field>

  <button mat-button type="button" color="primary">Save</button>
</form>

popup ts

import { Component } from '@angular/core';
import { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button';
import { FormControl, FormGroup } from '@angular/forms';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';

@Component({
  selector: 'app-a',
  standalone: true,
  imports: [
    FormsModule,
    ReactiveFormsModule,
    MatFormFieldModule,
    MatInputModule,
    MatButtonModule,
  ],
  templateUrl: './a.component.html',
  styleUrl: './a.component.css',
})
export class AComponent {
  form: FormGroup;

  constructor() {
    this.form = new FormGroup({
      email: new FormControl(''),
    });
  }
}

calling ts

import { Component } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { AComponent } from '../a/a.component';

@Component({
  selector: 'app-b',
  standalone: true,
  imports: [],
  templateUrl: './b.component.html',
  styleUrl: './b.component.css',
})
export class BComponent {
  constructor(public dialog: MatDialog) {}

  public openDialog() {
    this.dialog
      .open(AComponent, {})
      .afterClosed()
      .subscribe((res) => {
        console.log(res);
      });
  }
}

calling html

<button (click)="openDialog()" mat-raised-button color="primary" type="button">
  Open Dialog
</button>

Solution

  • Yes we can use the data property of the second argument of open method. we set a property fromPopup as true and then read it on our component!

    I use the angular decorator @Optional so that even if we use this component outside of a popup, we still will not get dependency injection error for trying to get MAT_DIALOG_DATA

    b ts

    import { Component } from '@angular/core';
    import { MatDialog } from '@angular/material/dialog';
    import { AComponent } from '../a/a.component';
    
    @Component({
      selector: 'app-b',
      standalone: true,
      imports: [AComponent],
      templateUrl: './b.component.html',
      styleUrl: './b.component.css',
    })
    export class BComponent {
      constructor(public dialog: MatDialog) {}
    
      public openDialog() {
        this.dialog
          .open(AComponent, {
            data: { fromPopup: true },
          })
          .afterClosed()
          .subscribe((res) => {
            console.log(res);
          });
      }
    }
    

    b html

    <button (click)="openDialog()" mat-raised-button color="primary" type="button">
      Open Dialog
    </button>
    
    <app-a />
    

    a ts

    import { Component, Inject, Optional } from '@angular/core';
    import { MatInputModule } from '@angular/material/input';
    import { MatButtonModule } from '@angular/material/button';
    import { FormControl, FormGroup } from '@angular/forms';
    import { FormsModule, ReactiveFormsModule } from '@angular/forms';
    import { MatFormFieldModule } from '@angular/material/form-field';
    import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
    import { CommonModule } from '@angular/common';
    
    @Component({
      selector: 'app-a',
      standalone: true,
      imports: [
        FormsModule,
        ReactiveFormsModule,
        MatFormFieldModule,
        MatInputModule,
        MatButtonModule,
        CommonModule,
        MatDialogModule,
      ],
      templateUrl: './a.component.html',
      styleUrl: './a.component.css',
    })
    export class AComponent {
      form!: FormGroup;
      fromPopup = false;
    
      constructor(
        @Optional() @Inject(MAT_DIALOG_DATA) public data: { fromPopup: boolean }
      ) {
        this.fromPopup = !!data?.fromPopup;
        this.form = new FormGroup({
          email: new FormControl(''),
        });
      }
    }
    

    a html

    <form [formGroup]="form" class="">
      <h1>App A</h1>
    
      <mat-form-field>
        <mat-label>Email</mat-label>
        <input
          matInput
          type="email"
          formControlName="email"
          placeholder="Ex. pat@example.com "
        />
      </mat-form-field>
    
      <button mat-button type="button" color="primary">Save</button>
    </form>
    <button *ngIf="fromPopup" mat-button [mat-dialog-close]="true">
      shows only for popup!
    </button>
    

    Stackblitz Demo