如何创建一个与Angular 4兼容的模态弹出窗口

19

我希望能够创建一个弹出窗口,当选中单选按钮时,它会加载我的某个Angular 4组件。

根据这个问题中提供的方法似乎只适用于Angular 2。

我不知道从哪里开始,希望能得到任何帮助!


1
请查看Angular Material Dialogue,这里是Plunker - Madhu Ranjan
太棒了,这似乎正是我在寻找的 :) - Luca Guarro
很高兴能帮到你,你可以接受答案,祝好! - Madhu Ranjan
3
请注意,如果一个解决方案适用于Angular v2,那么它几乎肯定也适用于Angular v4。 - snorkpete
2个回答

21
接受的答案为了解决一个小问题而引入了一个庞大的依赖。模态(和非模态)对话框基本上是由一个或两个CSS类组成的。请尝试这个“重命名…”示例:
1)将父级和子模态视为非模态,而只是带有*ngIf附加的内联表单。
使用子元素的父级HTML:
<div>
    A div for {{name}}.
    <button type="button" (click)="showModal()">Rename</button>
    <my-modal *ngIf="showIt" [oldname]="name" (close)="closeModal($event)"></my-modal>
</div>

父类。为了简洁省略了@Component修饰符。(name属性属于父类,即使我们没有表单来修改它,它也会存在。)

export class AppComponent {
    name = "old name";

    showIt = false;
    showModal() {
        this.showIt = true;
    }
    closeModal(newName: string) {
        this.showIt = false;
        if (newName) this.name = newName;
    }

}

即将成为模态组件的子组件。@Component装饰器和导入再次省略。

export class MyModalComponent {
    @Input() oldname = "";
    @Output() close = new EventEmitter<string>();
    newname = "";

    ngOnInit() {
        // copy all inputs to avoid polluting them
        this.newname = this.oldname; 
    }

    ok() {
        this.close.emit(this.newname);
    }

    cancel() {
        this.close.emit(null);
    }
}

在将其转换为模态框之前,先处理子HTML。

<div>
    Rename {{oldname}}
    <input type="text" (change)="newname = $event.target.value;" />
    <button type="button" (click)="ok()">OK</button>
    <button type="button" (click)="cancel()">Cancel</button>
</div>

2) 这是子元素的CSS,但它可以放在全局样式表中以便在您的应用程序中复用。它是一个名为modal的单一类,旨在用于<div>元素。

.modal {
    /* detach from rest of the document */
    position: fixed;

    /* center */
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);

    /* ensure in front of rest of page -- increase as needed */
    z-index: 1001;

    /* visual illusion of being in front -- alter to taste */
    box-shadow: rgba(0,0,0,0.4) 10px 10px 4px;

    /* visual illusion of being a solid object -- alter to taste */
    background-color: lightblue;
    border: 5px solid darkblue;

    /* visual preference of don't crowd the contents -- alter to taste */
    padding: 10px;
}

但是modal CSS类不会阻止与下面页面的交互。 (因此,它在技术上创建了一个非模态对话框。)因此,我们在modal下方放置一个overlay来吸收和忽略鼠标事件。 overlay也适用于<div>元素。

.overlay {
    /* detach from document */
    position: fixed;

    /* ensure in front of rest of page except modal */
    z-index: 1000;

    /* fill screen to catch mice */
    top: 0;
    left: 0;
    width: 9999px;
    height: 9999px;

    /* dim screen 20% -- alter to taste */
    opacity: 0.2;
    background-color: black;
}

3) 在子HTML中使用modaloverlay

<div class="modal">
    Rename {{oldname}}
    <input type="text" (change)="newname = $event.target.value;" />
    <button type="button" (click)="ok()">OK</button>
    <button type="button" (click)="cancel()">Cancel</button>
</div>
<div class="overlay"></div>

就是这样了。只需要2个CSS类,您就可以使任何组件成为模态窗口。实际上,您可以通过使用ngClass[class.modal]="showAsModalBoolean"更改CSS类的存在来在运行时以内联方式或模态方式显示组件。

您可以修改此设置,以便子级控制显示/隐藏逻辑。将*ngIf、showIt和show()函数移到子级中。在父级中添加@ViewChild(MyModalComponent) renameModal: MyModalComponent;然后父级可以命令式地调用this.renameModal.show(this.name);并按需重新连接初始化和包含div。

与上面所示的一样,子级模态框可以向父级函数返回信息,或者子级的show()方法可以根据喜好接受回调或返回Promise。

需要知道的两件事:

如果<my-modal>上有*ngIf,this.renameModal.show(..);将不起作用,因为它不会存在以开始公开函数。*ngIf删除整个组件,包括show()函数等,因此如果出于某种原因需要此功能,请改用[hidden]

由于所有模态窗口共享相同的z-index,因此模态窗口的z-index会出现问题。这可以通过[style.z-index]="calculatedValue"或类似方法解决。


5
我非常喜欢这个答案,因为它简洁灵活,旨在教会你某些东西,而不是使用一个庞大的框架来完成一个微小的任务。 - MaxAxeHax
我也在尝试跟踪这段代码,但不明白应该由谁捕获发出的事件?是父HTML的某个部分处理吗? - user5326354
父级 HTML 代码中有 <my-modal (close)="closeModal($event)"> ,这是子组件触发的 close 事件被捕获并传递到父级控制器的 closeModal 函数的地方。 - Ron Newcomb
说实话,对我来说,知道我们可以使用Angular Material来完成这个任务几乎是无用的,甚至是有害的。然而,这个答案缺少的是通常人们不会将模态框作为嵌入式组件使用,而是总是作为服务使用,例如modalObject.popup(),其中这个modalObject可以在运行时初始化。因此,在应用程序模板中不应该留下这个组件的任何痕迹。 - windmaomao
喜欢这个答案。简单易用,可定制性强,不依赖其他库。谢谢。 - Matrim
@windmaomao 我很惊讶地发现很多React开发人员更喜欢上述的内联方法。我写了一篇文章,介绍何时应该优先选择其中之一 https://dev.to/ronnewcomb/modal-dialogs-as-a-promise-versus-inline-1d4l - Ron Newcomb

13

查看Angular Material Dialogue,这里是Plunker

的链接。

import {Component} from '@angular/core';
import {MdDialog, MdDialogRef} from '@angular/material';


@Component({
  selector: 'dialog-result-example',
  templateUrl: './dialog-result-example.html',
})
export class DialogResultExample {
  selectedOption: string;

  constructor(public dialog: MdDialog) {}

  openDialog() {
    let dialogRef = this.dialog.open(DialogResultExampleDialog);
    dialogRef.afterClosed().subscribe(result => {
      this.selectedOption = result;
    });
  }
}


@Component({
  selector: 'dialog-result-example-dialog',
  templateUrl: './dialog-result-example-dialog.html',
})
export class DialogResultExampleDialog {
  constructor(public dialogRef: MdDialogRef<DialogResultExampleDialog>) {}
}

2
谢谢。经过一番搜索,我找到了解决方案。我不得不添加"entryComponents: [DialogResultExampleDialog]"而不是引导它。 - Luca Guarro
1
这个不起作用。找不到模块“@angular/material”。 - user2136053
@ritesh,你有npm安装Angular材料吗?参考此Angular材料入门指南 - Madhu Ranjan
2
Plunker 对我来说无法加载。 - pauloya
1
@pauloya,谢谢你让我知道了,已经更新了URL,干杯!! - Madhu Ranjan
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接