Angular 2.0 和模态对话框

Posted

技术标签:

【中文标题】Angular 2.0 和模态对话框【英文标题】:Angular 2.0 and Modal Dialog 【发布时间】:2016-04-03 11:39:14 【问题描述】:

我试图找到一些关于如何在 Angular 2.0 中执行确认模式对话框的示例。我一直在使用 Angular 1.0 的 Bootstrap 对话框,但无法在网络上找到 Angular 2.0 的任何示例。我还检查了 Angular 2.0 文档,但没有运气。

有没有办法在 Angular 2.0 中使用 Bootstrap 对话框?

【问题讨论】:

我找到了这个例子。也许它会帮助你angularscript.com/angular2-modal-window-with-bootstrap-style 我正在使用这个 RC3 并且非常满意:valor-software.com/ng2-bootstrap/#/modals 感谢@Sam,我有了一个好的开始。但是,我注意到调用组件不知道单击了哪个按钮。经过一番研究,我能够use Observables instead of EventEmitters to come up with a more elegant solution。 ng-bootstrap.github.io/#/components/modal 怎么样? @mentat,网址已更新valor-software.com/ngx-bootstrap/#/modals 【参考方案1】:

这是一个相当不错的示例,说明如何在 GitHub 上的 Angular2 应用程序中使用 Bootstrap 模式。

它的要点是您可以将引导 html 和 jquery 初始化包装在一个组件中。我创建了一个可重用的 modal 组件,它允许您使用模板变量触发打开。

<button type="button" class="btn btn-default" (click)="modal.open()">Open me!</button>

<modal #modal>
    <modal-header [show-close]="true">
        <h4 class="modal-title">I'm a modal!</h4>
    </modal-header>
    <modal-body>
        Hello World!
    </modal-body>
    <modal-footer [show-default-buttons]="true"></modal-footer>
</modal>

你只需要安装npm包并在你的app模块中注册modal模块:

import  Ng2Bs3ModalModule  from 'ng2-bs3-modal/ng2-bs3-modal';

@NgModule(
    imports: [Ng2Bs3ModalModule]
)
export class MyAppModule 

【讨论】:

无赖 - 依赖 jquery 作为依赖 :( 嗯,是的,bootstrap 依赖于它,我不从事重写库的业务。 这可以在没有 jQuery 的情况下完成。我使用 Sam 的答案和 koscielniak.me/post/2016/03/angular2-confirm-dialog-component 的教程编写了一个服务和相关的模态组件。 如果您没有在项目中使用 bootstrap,请不要忘记添加 bootstrap.css 的链接。 github页面忘记提及这一点。【参考方案2】:

这是一种简单的方法,不依赖于 jquery 或除 Angular 2 之外的任何其他库。 下面的组件 (errorMessage.ts) 可以用作任何其他组件的子视图。它只是一个始终打开或显示的引导模式。它的可见性由 ngIf 语句控制。

errorMessage.ts

import  Component  from '@angular/core';
@Component(
    selector: 'app-error-message',
    templateUrl: './app/common/errorMessage.html',
)
export class ErrorMessage

    private ErrorMsg: string;
    public ErrorMessageIsVisible: boolean;

    showErrorMessage(msg: string)
    
        this.ErrorMsg = msg;
        this.ErrorMessageIsVisible = true;
    

    hideErrorMsg()
    
        this.ErrorMessageIsVisible = false;
    

errorMessage.html

<div *ngIf="ErrorMessageIsVisible" class="modal fade show in danger" id="myModal" role="dialog">
    <div class="modal-dialog">

        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal">&times;</button>
                <h4 class="modal-title">Error</h4>
            </div>
            <div class="modal-body">
                <p>ErrorMsg</p>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-default" (click)="hideErrorMsg()">Close</button>
            </div>
        </div>
    </div>
</div>

这是一个示例父控件(为简洁起见,省略了一些不相关的代码):

父.ts

import  Component, ViewChild  from '@angular/core';
import  NgForm  from '@angular/common';
import Router, RouteSegment, OnActivate, ROUTER_DIRECTIVES  from '@angular/router';
import  OnInit  from '@angular/core';
import  Observable  from 'rxjs/Observable';


@Component(
    selector: 'app-application-detail',
    templateUrl: './app/permissions/applicationDetail.html',
    directives: [ROUTER_DIRECTIVES, ErrorMessage]  // Note ErrorMessage is a directive
)
export class ApplicationDetail implements OnActivate

    @ViewChild(ErrorMessage) errorMsg: ErrorMessage;  // ErrorMessage is a ViewChild



    // yada yada


    onSubmit()
    
        let result = this.permissionsService.SaveApplication(this.Application).subscribe(x =>
        
            x.Error = true;
            x.Message = "This is a dummy error message";

            if (x.Error) 
                this.errorMsg.showErrorMessage(x.Message);
            
            else 
                this.router.navigate(['/applicationsIndex']);
            
        );
    


父.html

<app-error-message></app-error-message>
// your html...

【讨论】:

很好 - 可以解释一下class="modal fade show in danger" @bensiu 我猜没有使用类选择器 - 除非他们有所有这些词的 css 样式选择器,例如“在” 如何获得淡入/淡出效果?【参考方案3】:

这是我对 modal bootstrap angular2 组件的完整实现:​​

我假设在 &lt;body&gt; 标签底部的主 index.html 文件(带有 &lt;html&gt;&lt;body&gt; 标签)中:

  <script src="assets/js/jquery-2.1.1.js"></script>
  <script src="assets/js/bootstrap.min.js"></script>

modal.component.ts:

import  Component, Input, Output, ElementRef, EventEmitter, AfterViewInit  from '@angular/core';

declare var $: any;// this is very importnant (to work this line: this.modalEl.modal('show')) - don't do this (becouse this owerride jQuery which was changed by bootstrap, included in main html-body template): let $ = require('../../../../../node_modules/jquery/dist/jquery.min.js');

@Component(
  selector: 'modal',
  templateUrl: './modal.html',
)
export class Modal implements AfterViewInit 

    @Input() title:string;
    @Input() showClose:boolean = true;
    @Output() onClose: EventEmitter<any> = new EventEmitter();

    modalEl = null;
    id: string = uniqueId('modal_');

    constructor(private _rootNode: ElementRef) 

    open() 
        this.modalEl.modal('show');
    

    close() 
        this.modalEl.modal('hide');
    

    closeInternal()  // close modal when click on times button in up-right corner
        this.onClose.next(null); // emit event
        this.close();
    

    ngAfterViewInit() 
        this.modalEl = $(this._rootNode.nativeElement).find('div.modal');
    

    has(selector) 
        return $(this._rootNode.nativeElement).find(selector).length;
    


let modal_id: number = 0;
export function uniqueId(prefix: string): string 
    return prefix + ++modal_id;

modal.html:

<div class="modal inmodal fade" id="modal_id" tabindex="-1" role="dialog"  aria-hidden="true" #thisModal>
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header" [ngClass]="'hide': !(has('mhead') || title) ">
                <button *ngIf="showClose" type="button" class="close" (click)="closeInternal()"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
                <ng-content select="mhead"></ng-content>
                <h4 *ngIf='title' class="modal-title"> title </h4>
            </div>
            <div class="modal-body">
                <ng-content></ng-content>
            </div>

            <div class="modal-footer" [ngClass]="'hide': !has('mfoot') " >
                <ng-content select="mfoot"></ng-content>
            </div>
        </div>
    </div>
</div>

以及客户端编辑器组件中的使用示例: 客户端编辑组件.ts:

import  Component  from '@angular/core';
import  ClientService  from './client.service';
import  Modal  from '../common';

@Component(
  selector: 'client-edit',
  directives: [ Modal ],
  templateUrl: './client-edit.html',
  providers: [ ClientService ]
)
export class ClientEdit 

    _modal = null;

    constructor(private _ClientService: ClientService) 

    bindModal(modal) this._modal=modal;

    open(client) 
        this._modal.open();
        console.log(client);
    

    close() 
        this._modal.close();
    


客户端编辑.html:

<modal [title]='"Some standard title"' [showClose]='true' (onClose)="close()" #editModal> bindModal(editModal) 
    <mhead>Som non-standart title</mhead>
    Some contents
    <mfoot><button calss='btn' (click)="close()">Close</button></mfoot>
</modal>

当然titleshowClose&lt;mhead&gt;&lt;mfoot&gt; 是可选参数/标签。

【讨论】:

您可以使用 Angular 的 ViewChild 注释来代替 bindModal(modal) this._modal=modal;,例如:@ViewChild('editModal') _modal: Modal;。它在幕后为您处理绑定。【参考方案4】: Angular 2 及更高版本 引导 css(动画被保留) 没有 JQuery 没有 bootstrap.js 支持自定义模态内容(就像接受的答案一样) 最近添加了对相互叠加的多个模式的支持。

`

@Component(
  selector: 'app-component',
  template: `
  <button type="button" (click)="modal.show()">test</button>
  <app-modal #modal>
    <div class="app-modal-header">
      header
    </div>
    <div class="app-modal-body">
      Whatever content you like, form fields, anything
    </div>
    <div class="app-modal-footer">
      <button type="button" class="btn btn-default" (click)="modal.hide()">Close</button>
      <button type="button" class="btn btn-primary">Save changes</button>
    </div>
  </app-modal>
  `
)
export class AppComponent 


@Component(
  selector: 'app-modal',
  template: `
  <div (click)="onContainerClicked($event)" class="modal fade" tabindex="-1" [ngClass]="'in': visibleAnimate"
       [ngStyle]="'display': visible ? 'block' : 'none', 'opacity': visibleAnimate ? 1 : 0">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <ng-content select=".app-modal-header"></ng-content>
        </div>
        <div class="modal-body">
          <ng-content select=".app-modal-body"></ng-content>
        </div>
        <div class="modal-footer">
          <ng-content select=".app-modal-footer"></ng-content>
        </div>
      </div>
    </div>
  </div>
  `
)
export class ModalComponent 

  public visible = false;
  public visibleAnimate = false;

  public show(): void 
    this.visible = true;
    setTimeout(() => this.visibleAnimate = true, 100);
  

  public hide(): void 
    this.visibleAnimate = false;
    setTimeout(() => this.visible = false, 300);
  

  public onContainerClicked(event: MouseEvent): void 
    if ((<HTMLElement>event.target).classList.contains('modal')) 
      this.hide();
    
  

要显示背景,您需要这样的 CSS:

.modal 
  background: rgba(0,0,0,0.6);

该示例现在允许同时使用多个模式。 (参见onContainerClicked() 方法)。

对于 Bootstrap 4 css 用户,您需要进行 1 次小的更改(因为 css 类名称是从 Bootstrap 3 更新的)。这一行: [ngClass]="'in': visibleAnimate" 应改为: [ngClass]="'show': visibleAnimate"

为了演示,这里是 plunkr

【讨论】:

不过有一个问题。因为按钮在这里被包裹在一个额外的元素中,所以引导样式不会将边距应用于按钮(至少在 v4 中)。删除包装 div.modal-footer 并将 .app-modal-footer 更改为 .modal-footer 可以解决此问题。【参考方案5】:

尝试使用 ng-window,它允许开发人员以简单的方式打开和完全控制单页应用程序中的多个窗口,无需 Jquery,无需 Bootstrap。

可用配置

最大化窗口 最小化窗口 自定义尺寸, 自定义位置 窗口是可拖动的 是否屏蔽父窗口 窗口居中与否 将值传递给主窗口 将值从子窗口传递到父窗口 监听关闭父窗口中的子窗口 使用您的自定义监听器监听以调整事件大小 是否以最大尺寸打开 启用和禁用窗口大小调整 启用和禁用最大化 启用和禁用最小化

【讨论】:

-1 这到底有什么用?它没有解决 OP 规定的任何要求。这是我看到你拖钓你的答案的第 4 篇帖子!【参考方案6】:

检查在运行时创建的 ASUI 对话框。不需要隐藏和显示逻辑。简单的服务将在运行时使用 AOT 创建一个组件 ASUI NPM

【讨论】:

嗨 Aravind Sivam,请阅读:***.com/help/promotion【参考方案7】:

我在我的项目中使用ngx-bootstrap。

你可以找到演示here

github是here

使用方法:

    安装ngx-bootstrap

    导入到您的模块

// RECOMMENDED (doesn't work with system.js)
import  ModalModule  from 'ngx-bootstrap/modal';
// or
import  ModalModule  from 'ngx-bootstrap';

@NgModule(
  imports: [ModalModule.forRoot(),...]
)
export class AppModule()
    简单的静态模态
<button type="button" class="btn btn-primary" (click)="staticModal.show()">Static modal</button>
<div class="modal fade" bsModal #staticModal="bs-modal" [config]="backdrop: 'static'"
tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
   <div class="modal-content">
      <div class="modal-header">
         <h4 class="modal-title pull-left">Static modal</h4>
         <button type="button" class="close pull-right" aria-label="Close" (click)="staticModal.hide()">
         <span aria-hidden="true">&times;</span>
         </button>
      </div>
      <div class="modal-body">
         This is static modal, backdrop click will not close it.
         Click <b>&times;</b> to close modal.
      </div>
   </div>
</div>
</div>

【讨论】:

【参考方案8】:

现在可以NPM package

angular-custom-modal


@斯蒂芬保罗continuation...

Angular 2 及更高版本 Bootstrap css(动画被保留) 没有 JQuery 没有 bootstrap.js 支持自定义模态内容 在每个模式之上支持多个模式 其他。 模块化 模式打开时禁用滚动 导航离开时模式被破坏。 惰性内容初始化,退出模态时获取ngOnDestroy(ed)。 模式可见时禁用父级滚动

延迟内容初始化

为什么?

在某些情况下,您可能不希望模态在关闭后保持其状态,而是恢复到初始状态。

原始模式问题

将内容直接传递到视图实际上会在模式获取它之前生成初始化它。即使使用 *ngIf 包装器,模态也无法杀死此类内容。

解决方案

ng-templateng-template 在被命令之前不会渲染。

我的组件.module.ts

...
imports: [
  ...
  ModalModule
]

我的组件.ts

<button (click)="reuseModal.open()">Open</button>
<app-modal #reuseModal>
  <ng-template #header></ng-template>
  <ng-template #body>
    <app-my-body-component>
      <!-- This component will be created only when modal is visible and will be destroyed when it's not. -->
    </app-my-body-content>
    <ng-template #footer></ng-template>
</app-modal>

modal.component.ts

export class ModalComponent ... 
  @ContentChild('header') header: TemplateRef<any>;
  @ContentChild('body') body: TemplateRef<any>;
  @ContentChild('footer') footer: TemplateRef<any>;
 ...

modal.component.html

<div ... *ngIf="visible">
  ...
  <div class="modal-body">
    ng-container *ngTemplateOutlet="body"></ng-container>
  </div>

参考文献

我不得不说,如果没有网络上优秀的官方和社区文档,这是不可能的。这也可能有助于你们中的一些人更好地理解 ng-template*ngTemplateOutlet@ContentChild 的工作原理。

https://angular.io/api/common/NgTemplateOutlethttps://blog.angular-university.io/angular-ng-template-ng-container-ngtemplateoutlet/https://medium.com/claritydesignsystem/ng-content-the-hidden-docs-96a29d70d11bhttps://netbasal.com/understanding-viewchildren-contentchildren-and-querylist-in-angular-896b0c689f6ehttps://netbasal.com/understanding-viewchildren-contentchildren-and-querylist-in-angular-896b0c689f6e

完整的复制粘贴解决方案

modal.component.html

<div
  (click)="onContainerClicked($event)"
  class="modal fade"
  tabindex="-1"
  [ngClass]="'in': visibleAnimate"
  [ngStyle]="'display': visible ? 'block' : 'none', 'opacity': visibleAnimate ? 1 : 0"
  *ngIf="visible">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <ng-container *ngTemplateOutlet="header"></ng-container>
        <button class="close" data-dismiss="modal" type="button" aria-label="Close" (click)="close()">×</button>
      </div>
      <div class="modal-body">
        <ng-container *ngTemplateOutlet="body"></ng-container>
      </div>
      <div class="modal-footer">
        <ng-container *ngTemplateOutlet="footer"></ng-container>
      </div>
    </div>
  </div>
</div>

modal.component.ts

/**
 * @Stephen Paul https://***.com/a/40144809/2013580
 * @zurfyx https://***.com/a/46949848/2013580
 */
import  Component, OnDestroy, ContentChild, TemplateRef  from '@angular/core';

@Component(
  selector: 'app-modal',
  templateUrl: 'modal.component.html',
  styleUrls: ['modal.component.scss'],
)
export class ModalComponent implements OnDestroy 
  @ContentChild('header') header: TemplateRef<any>;
  @ContentChild('body') body: TemplateRef<any>;
  @ContentChild('footer') footer: TemplateRef<any>;

  public visible = false;
  public visibleAnimate = false;

  ngOnDestroy() 
    // Prevent modal from not executing its closing actions if the user navigated away (for example,
    // through a link).
    this.close();
  

  open(): void 
    document.body.style.overflow = 'hidden';

    this.visible = true;
    setTimeout(() => this.visibleAnimate = true, 200);
  

  close(): void 
    document.body.style.overflow = 'auto';

    this.visibleAnimate = false;
    setTimeout(() => this.visible = false, 100);
  

  onContainerClicked(event: MouseEvent): void 
    if ((<HTMLElement>event.target).classList.contains('modal')) 
      this.close();
    
  

modal.module.ts

import  NgModule  from '@angular/core';
import  CommonModule  from '@angular/common';

import  ModalComponent  from './modal.component';

@NgModule(
  imports: [
    CommonModule,
  ],
  exports: [ModalComponent],
  declarations: [ModalComponent],
  providers: [],
)
export class ModalModule  

【讨论】:

【参考方案9】:

Angular 7 + NgBootstrap

从主组件打开模态并将结果传回给它的简单方法。是我想要的。我创建了一个分步教程,其中包括从头开始创建一个新项目、安装 ngbootstrap 和创建 Modal。您可以克隆它或按照指南进行操作。

希望这对 Angular 新手有所帮助!

https://github.com/wkaczurba/modal-demo

详情:

模态简单模板(modal-simple.component.html):

<ng-template #content let-modal>
  <div class="modal-header">
    <h4 class="modal-title" id="modal-basic-title">Are you sure?</h4>
    <button type="button" class="close" aria-label="Close" (click)="modal.dismiss('Cross click')">
      <span aria-hidden="true">&times;</span>
    </button>
  </div>
  <div class="modal-body">
    <p>You have not finished reading my code. Are you sure you want to close?</p>
  </div>
  <div class="modal-footer">
    <button type="button" class="btn btn-outline-dark" (click)="modal.close('yes')">Yes</button>
    <button type="button" class="btn btn-outline-dark" (click)="modal.close('no')">No</button>
  </div>
</ng-template>

modal-simple.component.ts:

import  Component, OnInit, ViewChild, Output, EventEmitter  from '@angular/core';
import  NgbModal  from '@ng-bootstrap/ng-bootstrap';

@Component(
  selector: 'app-modal-simple',
  templateUrl: './modal-simple.component.html',
  styleUrls: ['./modal-simple.component.css']
)
export class ModalSimpleComponent implements OnInit 
  @ViewChild('content') content;
  @Output() result : EventEmitter<string> = new EventEmitter();

  constructor(private modalService : NgbModal)  

  open() 
    this.modalService.open(this.content, ariaLabelledBy: 'modal-simple-title')
      .result.then((result) =>  console.log(result as string); this.result.emit(result) , 
        (reason) =>  console.log(reason as string); this.result.emit(reason) )
  

  ngOnInit() 
  


它的演示(app.component.html) - 处理返回事件的简单方法:

<app-modal-simple #mymodal (result)="onModalClose($event)"></app-modal-simple>
<button (click)="mymodal.open()">Open modal</button>

<p>
Result is  modalCloseResult 
</p>

app.component.ts - onModalClosed 在模式关闭后执行:

@Component(
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
)
export class AppComponent 
  modalCloseResult : string;
  title = 'modal-demo';

  onModalClose(reason : string) 
    this.modalCloseResult = reason;
      

干杯

【讨论】:

以上是关于Angular 2.0 和模态对话框的主要内容,如果未能解决你的问题,请参考以下文章

Angular JS ng-repeat 和模态对话框

如何从 ag-grid 单元格中打开模态对话框单击 Angular 6

angular-ui-bootstrap 模态框可以弹出多个吗

在 Angular 中进行确认对话框的简单方法?

使用引导程序关闭 angular.js 中的所有对话框

Angular 2 模态弹出错误“检查后表达式已更改”