Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dialog with inline Template #10459

Open
benneq opened this issue Mar 17, 2018 · 14 comments
Open

Dialog with inline Template #10459

benneq opened this issue Mar 17, 2018 · 14 comments
Labels
area: material/dialog feature This issue represents a new feature or feature request rather than a bug or bug fix P4 A relatively minor issue that is not relevant to core functions

Comments

@benneq
Copy link

benneq commented Mar 17, 2018

Bug, feature request, or proposal:

feature request

What is the expected behavior?

I'd like to use something like this (just like mat-menu):

<button (click)="myDialog.open()">Open it</button>

<mat-dialog (result)="onResult($event)" #myDialog>
  <h1 mat-dialog-title>Dialog Title</h1>
    <mat-dialog-content>
        Content
    </mat-dialog-content>
    <mat-dialog-actions>
        <button mat-button [mat-dialog-close]="false">Cancel</button>
        <button mat-button [mat-dialog-close]="true">Confirm</button>
    </mat-dialog-actions>
</mat-dialog>

What is the current behavior?

Doesn't exist as fat as I have seen in the docs and source code.

What are the steps to reproduce?

I've created a small component which demonstrates what I'm looking for:
https://stackblitz.com/edit/angular-material2-issue-tp5aej?file=app%2Fapp.component.html
(I didn't get mat-dialog-close working with the projection, though I chose to just add a method to my dialog component)

What is the use-case or motivation for changing an existing behavior?

There are many use cases where I just want a very simple dialog, and don't want to write 30 lines of code.

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

Angular 6 beta
Material 6 beta

@EdricChan03
Copy link
Contributor

EdricChan03 commented Mar 19, 2018

This can be done using <ng-template>:

app.component.html:

<button mat-button (click)="openDialogWithRef(myDialog)">Open dialog with template ref</button>
<!-- or... -->
<button mat-button (click)="openDialogWithoutRef()">Open dialog without template ref</button>
<ng-template #myDialog>
  <h2 matDialogTitle>Dialog!</h2>
  <mat-dialog-content>
    <p>Dialog content goes here</p>
  </mat-dialog-content>
  <mat-dialog-actions align="end">
    <button mat-button matDialogClose color="primary">DISMISS</button>
  </mat-dialog-actions>
</ng-template>
<ng-template #secondDialog>
  <h2 matDialogTitle>Such dialog very wow!</h2>
  <mat-dialog-content>
    <p><em>*insert meme here*</em></p>
  </mat-dialog-content>
  <mat-dialog-actions align="end">
    <button mat-button matDialogClose color="primary">Shutdown dialog</button>
  </mat-dialog-actions>
</ng-template>

app.component.ts:

import { ViewChild, TemplateRef } from '@angular/core';
import { MatDialog } from '@angular/material';

// ...
export class AppComponent {
  @ViewChild('secondDialog') secondDialog: TemplateRef<any>;
  constructor(private dialog: MatDialog) { }
  openDialogWithRef(templateRef: TemplateRef<any>) {
    this.dialog.open(templateRef);
  }
  openDialogWithoutRef() {
    this.dialog.open(this.secondDialog);
  }
}

Demo
See the code in the snippet below for more info:

/**
* Opens a modal dialog containing the given component.
* @param componentOrTemplateRef Type of the component to load into the dialog,
* or a TemplateRef to instantiate as the dialog content.
* @param config Extra configuration options.
* @returns Reference to the newly-opened dialog.
*/
open<T, D = any>(componentOrTemplateRef: ComponentType<T> | TemplateRef<T>,
config?: MatDialogConfig<D>): MatDialogRef<T> {

@benneq
Copy link
Author

benneq commented Mar 19, 2018

Thank you.

But your solution doesn't support the Dialog results. Using dialogRef.afterClosed().subscribe(...) will make the code even larger, and I guess it's not that easy to say #myDialog1 should call onFirstResult($event) and #myDialog2 should call onSecondResult($event).

@josephperrott josephperrott added feature This issue represents a new feature or feature request rather than a bug or bug fix P4 A relatively minor issue that is not relevant to core functions labels Mar 27, 2018
@kimamula
Copy link

I would like to add that if you need MatDialogRef instance in your dialog component in the <ng-template> pattern @Chan4077 suggested above, it's as easy as:

<ng-template #myDialog let-dialogRef="dialogRef">
  <my-dialog [dialogRef]="dialogRef"></my-dialog>
</ng-template>

I like this usage and I think it would be nice if the usage is clearly documented.

@j2L4e
Copy link

j2L4e commented Mar 23, 2019

This has been solved in #9379 even before this issue came up.
A naive wrapper component could look like this

import {  ChangeDetectionStrategy,  Component,  ContentChild,  EventEmitter,  Output,  TemplateRef } from '@angular/core';
import { MatDialog, MatDialogRef } from '@angular/material';

@Component({
  selector: 'app-dialog',
  templateUrl: './dialog.component.html',
  styleUrls: ['./dialog.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DialogComponent {
  @ContentChild(TemplateRef) template: TemplateRef<{}>;
  @Output() result = new EventEmitter();

  dRef: MatDialogRef<any>;

  constructor (private dialog: MatDialog) {}

  open () {
    this.dRef = this.dialog.open(this.template);
    this.dRef.afterClosed().subscribe(val => this.result.emit(val));
  }
}

Use it like this:

<button (click)="d.open()">click me</button>
<app-dialog #d (result)="onResult($event)">
  <ng-template>
      <button [mat-dialog-close]="someProperty">leave me alone!</button>
  </ng-template>
</app-dialog>

caveat:
both DialogComponent and MatDialogClose have to be available in the template. A missing directive fails silently, so make sure to reexport MatDialogClose or import MatDialogModule

@almeidap
Copy link

almeidap commented Mar 29, 2019

@benneq Thanks for your solution for a temporary MatDialog implementation!

@j2L4e It looks like that [mat-dialog-close] inside a dialog TemplateRef fails even after exporting MatDialogClose and/or importing MatDialogModule in the closest parent module:

jit_nodeValue_3(...).dialogRef.close is not a function
    at Object.eval [as handleEvent] (TestComponent.html:17)
    at handleEvent (core.js:23009)
    at callWithDebugContext (core.js:24079)
    at Object.debugHandleEvent [as handleEvent] (core.js:23806)
    at dispatchEvent (core.js:20458)
    at core.js:20905
    at HTMLButtonElement.<anonymous> (platform-browser.js:993)

@arlowhite
Copy link

arlowhite commented May 14, 2019

IMO, this is a documentation issue. https://material.angular.io/components/dialog/overview needs an example with TemplateRef using implicit context. This is difficult to figure out if you're not comfortable looking at material2's source code.

The $implicit context is bound to MatDialogConfig.data, which I don't think most people know about. So in this code example, foo, which is arbitrary, is bound to $implicit, which is from MatDialogConfig.data, see material2/src/lib/dialog/dialog.ts _attachDialogContent()

<ng-template #myTemp let-foo>
  <h2 matDialogTitle>My Title</h2>
  <mat-dialog-content>
    <p>{{ foo.myProperty }}</p>
  </mat-dialog-content>
</ng-template>

Then, you need to bind TemplateRef in your component, or just pass it by reference when calling a function from the template.

      <button mat-stroked-button
              (click)="openDialog(myTemp, myData)">
          Open Dialog
      </button>

Component .ts

  openDialog(templateRef: TemplateRef<MyData>, myData: MyData) {
    this.dialog.open(templateRef, { data: myData });
  }

@tonyuifalean
Copy link

@arlowhite, didn't you get the error:
ERROR Error: No component factory found for undefined. Did you add it to @NgModule.entryComponents?
?

@EdricChan03
Copy link
Contributor

@tonyuifalean Could you show us your code?

If you're facing an unrelated issue, I suggest that you should either try to ask on StackOverflow or open a new issue on this repository.

@tonyuifalean
Copy link

tonyuifalean commented Mar 4, 2020

@EdricChan03: I am using Angular 8, and I am just testing @arlowhite's example.
In my template I have:
image

In the component I have a method, called in ngOnInit:
@ViewChild('myTemp', { static: false })
public myTemp: TemplateRef;
public openMyDialog() {
this.dialog.open(this.myTemp);
}

And in the module I import: MatDialogModule

@EdricChan03
Copy link
Contributor

Could you try moving the public myTemp line to the same line as the ViewChild(...) line? And is there any reason why you're marking all of the methods and properties as public?

@EdricChan03
Copy link
Contributor

EdricChan03 commented Mar 4, 2020

@arlowhite Actually, if you look closely in the documentation for TemplateRef, passing in the data interface as the T type parameter will not make any difference than passing in any as TemplateRef is defined to have a C type parameter which is the template's class.

So here's the modified version of your component code:

openDialog(templateRef: TemplateRef<any>, myData: MyData) {
  this.dialog.open(templateRef, { data: myData });
}

Oops, never mind. I did not read the bottom part that mentioned what the C type parameter is for:

The data-binding context of the embedded view, as declared in the <ng-template> usage.

@tonyuifalean
Copy link

Could you try moving the public myTemp line to the same line as the ViewChild(...) line? And is there any reason why you're marking all of the methods and properties as public?

Just for testing purposes. In the end I'll try to use a component directly, but thanks for you support!

@angular-robot
Copy link
Contributor

angular-robot bot commented Feb 1, 2022

Just a heads up that we kicked off a community voting process for your feature request. There are 20 days until the voting process ends.

Find more details about Angular's feature request process in our documentation.

@angular-robot
Copy link
Contributor

angular-robot bot commented Feb 22, 2022

Thank you for submitting your feature request! Looks like during the polling process it didn't collect a sufficient number of votes to move to the next stage.

We want to keep Angular rich and ergonomic and at the same time be mindful about its scope and learning journey. If you think your request could live outside Angular's scope, we'd encourage you to collaborate with the community on publishing it as an open source package.

You can find more details about the feature request process in our documentation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: material/dialog feature This issue represents a new feature or feature request rather than a bug or bug fix P4 A relatively minor issue that is not relevant to core functions
Projects
None yet
Development

No branches or pull requests

10 participants