Angular 2.0 y diálogo modal


128

Estoy tratando de encontrar algunos ejemplos sobre cómo hacer un diálogo modal de Confirmación en Angular 2.0. He estado usando el diálogo Bootstrap para Angular 1.0 y no pude encontrar ningún ejemplo en la web para Angular 2.0. También revisé documentos angulares 2.0 sin suerte.

¿Hay alguna manera de usar el cuadro de diálogo Bootstrap con Angular 2.0?



1
Estoy usando este con RC3 y bastante contento con él: valor-software.com/ng2-bootstrap/#/modals
mentat

Gracias a @Sam, tuve un buen comienzo. Sin embargo, noté que el componente de llamada no sabe en qué botón se hace clic. Después de investigar un poco, pude usar Observables en lugar de EventEmitters para encontrar una solución más elegante .
Jon


Respuestas:


199
  • Angular 2 y hasta
  • Bootstrap css (se conserva la animación)
  • NO JQuery
  • NO bootstrap.js
  • Admite contenido modal personalizado (al igual que la respuesta aceptada)
  • Soporte agregado recientemente para múltiples modales uno encima del otro .

``

@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();
    }
  }
}

Para mostrar el fondo , necesitará algo como este CSS:

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

El ejemplo ahora permite múltiples modales al mismo tiempo . (ver el onContainerClicked()método).

Para los usuarios de Bootstrap 4 css , debe realizar 1 cambio menor (porque se actualizó un nombre de clase css desde Bootstrap 3). Esta línea: [ngClass]="{'in': visibleAnimate}"debe cambiarse a: [ngClass]="{'show': visibleAnimate}"

Para demostrar, aquí hay un plunkr


Sin embargo, hay una trampa. Debido a que los botones están envueltos dentro de un elemento adicional aquí, el estilo bootstrap no aplicará márgenes a los botones (al menos en v4). quitando el envoltorio div.modal-footery cambiando el .app-modal-footerpara .modal-footerarreglar esto.
Axel Köhler

55

Aquí hay un ejemplo bastante decente de cómo puede usar el modo Bootstrap dentro de una aplicación Angular2 en GitHub .

La esencia de esto es que puede envolver el arranque html y la inicialización de jquery en un componente. He creado un modalcomponente reutilizable que le permite activar una apertura utilizando una variable de plantilla.

<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>

Solo necesita instalar el paquete npm y registrar el módulo modal en el módulo de su aplicación:

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

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

8
Bummer: confía en jquery como dependencia :(
brando

52
Bueno, sí, bootstrap depende de eso y no estoy en el negocio de reescribir bibliotecas.
Douglas Ludlow

2
Esto se puede hacer sin jQuery. Usé la respuesta de Sam junto con el tutorial en koscielniak.me/post/2016/03/angular2-confirm-dialog-component para escribir un servicio y un componente modal asociado.
BeetleJuice

Si no está utilizando bootstrap en su proyecto, no olvide agregar un enlace a bootstrap.css. La página de Github se olvida de mencionar esto.
Shekhar

46

Este es un enfoque simple que no depende de jquery ni de ninguna otra biblioteca, excepto Angular 2. El componente a continuación (errorMessage.ts) se puede usar como una vista secundaria de cualquier otro componente. Es simplemente un modo de arranque que siempre está abierto o se muestra. Su visibilidad se rige por la declaración 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>

Este es un ejemplo de control principal (se ha omitido un código no relevante por brevedad):

parent.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']);
            }
        });
    }

}

parent.html

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

3
agradable - podría explicarclass="modal fade show in danger"
bensiu

@bensiu Supongo que el selector de clase no se usa, a menos que tengan un selector de estilo CSS para todas esas palabras, por ejemplo, 'en'
Drenai

¿Cómo se consigue el efecto de fundido de entrada / salida con esto?
Big McLargeHuge

10

Ahora disponible como paquete NPM

angular-personalizado-modal


@Stephen Paul continuación ...

  • Angular 2 y más Bootstrap css (se conserva la animación)
  • NO JQuery
  • NO bootstrap.js
  • Admite contenido modal personalizado
  • Soporte para múltiples modales uno encima del otro.
  • Moduralizado
  • Deshabilitar desplazamiento cuando modal está abierto
  • El modal se destruye cuando se aleja.
  • Inicialización de contenido diferido, que se obtiene ngOnDestroy(ed) cuando se sale del modal.
  • Desplazamiento principal deshabilitado cuando modal es visible

Inicialización de contenido diferido

¿Por qué?

En algunos casos, es posible que no desee modal para mantener su estado después de haber sido cerrado, sino más bien restaurado al estado inicial.

Problema modal original

Pasar el contenido directamente a la vista en realidad genera lo inicializa incluso antes de que el modal lo obtenga. El modal no tiene una forma de eliminar dicho contenido incluso si se usa un *ngIfcontenedor.

Solución

ng-template. ng-templateno se procesa hasta que se le ordena hacerlo.

my-component.module.ts

...
imports: [
  ...
  ModalModule
]

my-component.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>

Referencias

Tengo que decir que no hubiera sido posible sin la excelente documentación oficial y comunitaria de la red. Podría ayudar a algunos de ustedes también para comprender mejor cómo ng-template, *ngTemplateOutlety @ContentChildel trabajo.

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

Solución completa de copiar y pegar

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://stackoverflow.com/a/40144809/2013580
 * @zurfyx https://stackoverflow.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 { }

7

Yo uso ngx-bootstrap para mi proyecto.

Puedes encontrar la demo aquí

El github está aquí.

Cómo utilizar:

  1. Instalar ngx-bootstrap

  2. Importar a su módulo

// 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(){}
  1. Modal estático simple
<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>

4

Aquí está mi implementación completa del componente modal2 bootstrap angular2:

Supongo que en su archivo index.html principal (con <html>y <body>etiquetas) en la parte inferior de la <body>etiqueta tiene:

  <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>

Y ejemplo de uso en el componente Editor del cliente: client-edit-component.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();
    }

}

client-edit.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>

Por supuesto title, showClose, <mhead>y <mfoot>ar parámetros / etiquetas opcionales.


2
En lugar de bindModal(modal) {this._modal=modal;}, puede utilizar angular de la ViewChildanotación, así: @ViewChild('editModal') _modal: Modal;. Maneja la encuadernación detrás de escena.
Douglas Ludlow


0

intente usar ng-window, permite al desarrollador abrir y controlar por completo múltiples ventanas en aplicaciones de una sola página de manera simple, sin Jquery, sin Bootstrap.

ingrese la descripción de la imagen aquí

Configuración disponible

  • Ventana de Maxmize
  • Minimizar ventana
  • Tamaño personalizado,
  • Posicion personalizada
  • la ventana es dragable
  • Bloquear ventana principal o no
  • Centrar la ventana o no
  • Pasar valores a la ventana chield
  • Pase valores de la ventana de campo a la ventana principal
  • Escuchando el cierre de la ventana chield en la ventana padre
  • Escuche cambiar el tamaño del evento con su oyente personalizado
  • Abierto con tamaño máximo o no
  • Habilitar y deshabilitar el cambio de tamaño de la ventana
  • Habilitar y deshabilitar la maximización
  • Habilitar y deshabilitar la minimización

-1 ¿Cómo es esto útil? No aborda ninguno de los requisitos especificados por el OP. Esta es la cuarta publicación. ¡Te veo trollear tu respuesta!
Avn

0

Angular 7 + NgBootstrap

Una forma sencilla de abrir modal desde el componente principal y pasar el resultado de nuevo a él. Es lo que quería. Creé un tutorial paso a paso que incluye la creación de un nuevo proyecto desde cero, la instalación de ngbootstrap y la creación de Modal. Puede clonarlo o seguir la guía.

Espero que esto ayude a nuevo en Angular.

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

Detalles:

plantilla modal-simple (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>

El 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() {
  }

}

Demostración de la misma (app.component.html): forma sencilla de tratar el evento de retorno

<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 se ejecuta una vez que modal se cierra:

@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;
  }    
}

Salud

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.