DEV Community

Ram Kota
Ram Kota

Posted on

ViewChild, ViewChildren and ContentChild, ContentChildren are undefined in Angular Material Dialog

I have a Angular Material Dialog Component that takes Inputs and uses them in header and footer of modal. I want to pass some html elements in modal's body section from another component.

I want to have a reference of child elements of a component in that component. I have tried both ElementRef, TemplateRef and QueryList with ViewChild, ViewChildren, ContentChild, ContentChildren but no luck.

dashboard.component.html displays a 'show/hide columns' button.

dashboard.component.html

<app-modal [title]="title" [btnText]="text'>
   <section #template>
    <mat-form-field appearance="outline" *ngIf="!showForm && viewParams.length > 0 && cn_hostname_index >= 0">
      <mat-label>Search Columns</mat-label>
      <input 
        matInput
        id="columnSearchTxt"
        placeholder="Search Columns"
        name="columnSearchTxt"
        [(ngModel)]="colSearch.title"
      />
      <button *ngIf="colSearch.title" (click)="colSearch.title = ''" mat-icon-button matSuffix (click)="searchHosts()" matTooltip="Filter on hostname">
        <mat-icon aria-hidden="false" aria-label="Clear">close</mat-icon>
      </button>
    </mat-form-field>

    <mat-slide-toggle
      *ngFor="let col of colDefs | filter: 'title':colSearch.title"
      color="primary"
      [name]="col.title"      
      [(ngModel)]="col.includeField"
      labelPosition= "before"
    >
      {{ col.title }}
    </mat-slide-toggle>
   </section>
</app-modal>
Enter fullscreen mode Exit fullscreen mode

I want a complete reference of section element along with its children as html elements. So that I can send the same ref to another component and display as innerHTML in dev.

modal.component.ts

import { Component, Inject, Input, TemplateRef, ViewChild, ContentChild, ElementRef } from '@angular/core';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';

export interface DialogData {
  title: string;
  text: string;
  template: TemplateRef<any>
}

@Component({
  selector: 'app-modal',
  template: `<button mat-raised-button (click)="openDialog()">{{ btnText }}</button>`,
  styleUrls: ['./modal.component.scss']
})
export class ModalComponent {

  @Input() btnText: string;
  @Input() id: string;
  @Input() modalTitle: string;
  @Input() okText: string;
  @Input() name: string;
  // @ContentChild('template') template: TemplateRef<any>;
  // @ContentChild('template') template: ElementRef<any>;
  // @ViewChild('template') template: TemplateRef<any>;
  @ViewChild('template') template: ElementRef;

  constructor(public dialog: MatDialog) { }

  openDialog(): void {
    const dialogRef = this.dialog.open(ModalDialogComponent, {
      data: {
        id: this.id, 
        title: this.modalTitle, 
        text: this.okText, 
        name: this.name,
        template: this.template
      },
    });
  }

}

@Component({
  selector: 'modal-dialog',
  templateUrl: './modal-dialog.component.html',
})
export class ModalDialogComponent {
  constructor(
    public dialogRef: MatDialogRef<ModalDialogComponent>,
    @Inject(MAT_DIALOG_DATA) public data: DialogData,
  ) {}

  onNoClick(): void {
    this.dialogRef.close();
  }
}
Enter fullscreen mode Exit fullscreen mode

modal-dialog.component.html

<section>
    <div>
        <mat-icon (click)="onNoClick()" aria-hidden="false" aria-label="close icon">close</mat-icon>
    </div>
    <h1 mat-dialog-title>{{ data.title }}</h1>
    <div mat-dialog-content>
        <!-- Want to display html elements from app-modal -->
    </div>
    <div mat-dialog-actions>
        <button mat-button (click)="onNoClick()">{{ data.text ? data.text : 'Ok' }}</button>
    </div>
</section>
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
akotech profile image
akotech

Hi Ram,

You cannot query for a reference defined in other component template, since each template is a black box to its relatives.
You have to add the following in the ModalComponent template <ng-template #template><ng-content></ng-content></ng-template> to capture the projected content you have defined inside the <app-modal> in the dashboard.

modal.component.ts

@Component({
  selector: 'app-modal',
  template: `
    <ng-template #template><ng-content></ng-content></ng-template>
    <button mat-raised-button (click)="openDialog()">{{ btnText }}</button>
  `,
   styleUrls: ['./modal.component.scss']
 })
export class ModalComponent {
  ...
  @ViewChild('template') template: TemplateRef<any>;
  ...
}
Enter fullscreen mode Exit fullscreen mode

Then you can pass the templateRef in the dialog data and print the data.template content in the modal-dialog component using a template-outlet like <ng-container *ngTemplateOutlet="data.template"></ng-container>

modal-dialog.component.html

<section>
    ...
    <div mat-dialog-content>
        <ng-container *ngTemplateOutlet="data.template"></ng-container>
    </div>
    ...
</section>
Enter fullscreen mode Exit fullscreen mode

Cheers