Publicada:

Angular 14 - Tutorial emergente modal (diálogo) con ejemplo

Construido con Angular 14.2.12

Otras versiones disponibles:

Este tutorial muestra cómo implementar una ventana emergente modal ligera personalizada (diálogo) en Angular 14 sin complementos de terceros.

Aplicación de ejemplo angular

La aplicación de ejemplo contiene una página con algo de texto y un par de botones para abrir dos ventanas emergentes modales:

  • Modal #1 - contiene un campo de entrada que le permite editar el texto que se muestra en la página principal; esto demuestra la vinculación de datos directamente desde una propiedad de componente a un elemento en un componente modal secundario.
  • Modal #2 - es una ventana emergente alta para demostrar que el modal se puede desplazar mientras se mantiene la página de abajo bloqueada en su posición.

Código en GitHub

El código del tutorial está disponible en GitHub en https://github.com/cornflourblue/angular-14-modal-popup-example.

Aquí está en acción: (Ver en StackBlitz en https://stackblitz.com/edit/angular-14-modal-popup-example)


Ejecute el ejemplo modal de Angular 14 localmente

  1. Instalar Node.js y NPM desde https://nodejs.org.
  2. Descargue o clone el código fuente del proyecto desde https://github.com/cornflourblue/angular-14-modal-popup-example
  3. Instale todos los paquetes npm necesarios ejecutando npm install o npm i desde la línea de comando en la carpeta raíz del proyecto (donde se encuentra el package.json).
  4. Inicie la aplicación ejecutando npm start desde la línea de comando en la carpeta raíz del proyecto.

NOTA: También puede iniciar la aplicación con el comando Angular CLI ng serve --open. Para hacer esto, primero instale Angular CLI globalmente en su sistema con el comando npm install -g @angular/cli.

Para obtener más información sobre cómo configurar su entorno de desarrollo Angular local, consulte Angular - Setup Development Environment.


Estructura del proyecto Angular 14

La CLI de Angular se usó para generar la estructura del proyecto base con el comando ng new <nombre del proyecto>, la CLI también se usa para construir y servir la aplicación. Para obtener más información sobre la CLI de Angular, consulte https://angular.io/cli.

La aplicación y la estructura del código del tutorial siguen principalmente las recomendaciones de mejores prácticas en la Guía de estilo angular oficial, con algunos de mis propios ajustes aquí y allá.

Convención de nomenclatura de carpetas

El código compartido/común (componentes y servicios) se coloca en carpetas con el prefijo _ de subrayado para diferenciarlos fácilmente de las características y agruparlos en la parte superior de la estructura de carpetas. Este ejemplo no tiene funciones enrutadas (por ejemplo, hogar, productos, etc.), pero si las tuviera, cada una tendría su propia carpeta en el directorio /app.

Archivos de barril

Los archivos index.ts de cada carpeta son archivos de barril que agrupan los módulos exportados de cada carpeta para que puedan importarse usando solo la ruta de la carpeta en lugar de la ruta completa del módulo, y para habilitar importar varios módulos en una sola importación (por ejemplo, import { ComponentOne, ComponentTwo } from '../_components').

Estos son los archivos principales del proyecto que contienen la lógica de la aplicación, no incluí archivos generados por el comando ng new de Angular CLI que dejé sin cambios.

 

Plantilla de componente modal

Ruta: /src/app/_components/modal.component.html

La plantilla del componente modal contiene el HTML externo para cada modal en la página, contiene un par de divs de contenedor para el modal y un elemento Angular <ng-content>.

Proyección de contenido angular

La proyección de contenido le permite insertar (o proyectar) cualquier contenido que desee dentro de un componente secundario utilizando el elemento <ng-content>. El contenido que agrega dentro de un componente modal (por ejemplo, <jw-modal>my content...</jw-modal>) se representa en la ubicación del elemento <ng-content>. Para obtener más información, consulta https://angular.io/guide/content-projection.

<div class="jw-modal">
    <div class="jw-modal-body">
        <ng-content></ng-content>
    </div>
</div>
 

Estilos LESS/CSS de componente modal

Ruta: /src/app/_components/modal.component.less

Esta hoja de estilo es el archivo principal en el tutorial modal, los siguientes estilos CSS son los que convierten un par de divs en la plantilla de componente modal en un modal ventana emergente en su navegador. El resto del código TypeScript (en el servicio modal y componente modal) controla abriendo, cerrando y gestionando modales.

Reglas de estilo modales

jw-modal - el elemento raíz de cada instancia de componente modal está oculto (cerrado) de forma predeterminada. El elemento del componente se define mediante el selector en el componente modal.

.jw-modal - el div del contenedor externo se fija en toda la pantalla por encima de todos los demás elementos con un fondo negro semitransparente y el desplazamiento habilitado en caso de que el contenido sea más largo que la pantalla.

.jw-modal-body - el div del contenedor interno solo tiene un fondo blanco y algo de relleno.

body.jw-modal-open - esta clase se agrega a la etiqueta HTML <body> cuando se abre un modal. El desplazamiento está deshabilitado en el contenido del cuerpo subyacente cuando un modal está abierto arriba.

El prefijo jw-

Prefijé el elemento modal y las clases con jw- para evitar conflictos de nombres con cualquier biblioteca CSS de terceros que pueda estar usando (por ejemplo, Bootstrap).

/* MODAL STYLES
-------------------------------*/
jw-modal {
    /* modals are hidden by default */
    display: none;

    .jw-modal {
        /* modal container fixed across whole screen */
        position: fixed;
        inset: 0;

        /* z-index must be higher than everything else on the page */
        z-index: 10000;
        
        /* semi-transparent black background exposed by padding */
        background-color: rgba(0, 0, 0, .75);
        padding: 40px;

        /* enables scrolling for tall modals */
        overflow: auto;
    }

    .jw-modal-body {
        padding: 20px;
        background: #fff;
    }
}

body.jw-modal-open {
    /* body overflow is hidden to hide main scrollbar when modal window is open */
    overflow: hidden;
}
 

Componente modal

Ruta: /src/app/_components/modal.component.ts

El componente modal controla el comportamiento de una instancia modal, contiene métodos para abrir y cerrar el modal y realiza algunos pasos cuando el componente se inicializa y destruye.

El selector de componente establece el elemento para el componente modal en <jw-modal>. La encapsulación se establece en ViewEncapsulation.None para que los estilos de componentes modales se apliquen globalmente, esto es necesario para deshabilitar el desplazamiento en el cuerpo cuando un modal está abierto porque la regla de estilo (body.jw-modal-open) apunta al elemento raíz <body>.

Identificación única requerida

Cada instancia modal debe tener un atributo id único (por ejemplo, <jw-modal id="modal-1">), se utiliza para identificar qué modal desea abrir al llamar al servicio modal (por ejemplo, modalService.open('modal-1')).

Métodos de componentes modales

ngOnInit() - agrega la instancia del componente al servicio modal para que pueda abrirse desde cualquier lugar. Mueve el elemento HTML modal para que sea un elemento secundario directo del <body> para ponerlo en el contexto de apilamiento raíz de la página, esto asegura que el modal aparecerá sobre todo otros elementos en el eje z usando la regla de estilo z-index. Agrega un detector de eventos para cerrar el modal al hacer clic en segundo plano.

ngOnDestroy() - elimina la instancia del componente del servicio modal. Elimina el elemento HTML modal del cuerpo, esto no es automático porque el elemento se movió en init.

open() - hace que aparezca el modal configurando el estilo display en 'block'. Agrega la clase CSS jw-modal-open a la etiqueta <body>. Establece la propiedad isOpen en true.

close() - hace que el modal desaparezca configurando el estilo display en 'none'. Elimina la clase CSS jw-modal-open de la etiqueta <body>. Establece la propiedad isOpen en false.

import { Component, ViewEncapsulation, ElementRef, Input, OnInit, OnDestroy } from '@angular/core';

import { ModalService } from '../_services';

@Component({
    selector: 'jw-modal',
    templateUrl: 'modal.component.html',
    styleUrls: ['modal.component.less'],
    encapsulation: ViewEncapsulation.None
})
export class ModalComponent implements OnInit, OnDestroy {
    @Input() id?: string;
    isOpen = false;
    private element: any;

    constructor(private modalService: ModalService, private el: ElementRef) {
        this.element = el.nativeElement;
    }

    ngOnInit() {
        // add self (this modal instance) to the modal service so it can be opened from any component
        this.modalService.add(this);

        // move element to bottom of page (just before </body>) so it can be displayed above everything else
        document.body.appendChild(this.element);

        // close modal on background click
        this.element.addEventListener('click', (el: any) => {
            if (el.target.className === 'jw-modal') {
                this.close();
            }
        });
    }

    ngOnDestroy() {
        // remove self from modal service
        this.modalService.remove(this);

        // remove modal element from html
        this.element.remove();
    }

    open() {
        this.element.style.display = 'block';
        document.body.classList.add('jw-modal-open');
        this.isOpen = true;
    }

    close() {
        this.element.style.display = 'none';
        document.body.classList.remove('jw-modal-open');
        this.isOpen = false;
    }
}
 

Servicio emergente modal

Ruta: /src/app/_services/modal.service.ts

El servicio modal es el intermediario que se utiliza para abrir y cerrar ventanas emergentes modales desde cualquier componente (o servicio) en la aplicación Angular.

Administra una colección de instancias modales activas en la matriz modals. Los métodos add() y remove() solo los utilizan las instancias de componentes modales para agregarse o eliminarse a sí mismos en la matriz modals.

Abrir y cerrar modales

open() - el método open toma un parámetro modal id y llama al método open() del modal especificado.

close() - el método close encuentra el modal actualmente abierto (isOpen: true) y llama al método close() de ese modal.

import { Injectable } from '@angular/core';

import { ModalComponent } from '../_components';

@Injectable({ providedIn: 'root' })
export class ModalService {
    private modals: ModalComponent[] = [];

    add(modal: ModalComponent) {
        // ensure component has a unique id attribute
        if (!modal.id || this.modals.find(x => x.id === modal.id)) {
            throw new Error('modal must have a unique id attribute');
        }

        // add modal to array of active modals
        this.modals.push(modal);
    }

    remove(modal: ModalComponent) {
        // remove modal from array of active modals
        this.modals = this.modals.filter(x => x === modal);
    }

    open(id: string) {
        // open modal specified by id
        const modal = this.modals.find(x => x.id === id);

        if (!modal) {
            throw new Error(`modal '${id}' not found`);
        }

        modal.open();
    }

    close() {
        // close the modal that is currently open
        const modal = this.modals.find(x => x.isOpen);
        modal?.close();
    }
}
 

Plantilla de componente de aplicación

Ruta: /src/app/app.component.html

La plantilla del componente de la aplicación contiene algo de texto y un par de botones para abrir dos ventanas emergentes modales:

  • Modal #1 - contiene un campo de entrada vinculado a la propiedad bodyText del componente de la aplicación, te permite editar el texto cerca de la parte superior de la página (<p>{{bodyText}}</p>). Esto demuestra vincular datos directamente desde una propiedad de componente a un elemento en un modal secundario.
  • Modal #2 - es una ventana emergente alta para demostrar que el modal se puede desplazar mientras se mantiene la página de abajo bloqueada en su posición.
<!-- main app container -->
<div>
    <div>
        <h1>Angular 14 - Modal Popup Example</h1>
        <p>{{bodyText}}</p>
        <button (click)="modalService.open('modal-1')">Open Modal 1</button>
        <button (click)="modalService.open('modal-2')">Open Modal 2</button>
    </div>
    
    <jw-modal id="modal-1">
        <h1>A custom modal popup</h1>
        <p>Home page text: <input type="text" [(ngModel)]="bodyText" /></p>
        <button (click)="modalService.close();">Close</button>
    </jw-modal>
    
    <jw-modal id="modal-2">
        <h1>A tall modal popup</h1>
        <p style="padding-bottom: 1500px;">Close with the button below or by clicking the background.</p>
        <button (click)="modalService.close();">Close</button>
    </jw-modal>
</div>
 

Componente de la aplicación

Ruta: /src/app/app.component.ts

El componente de la aplicación contiene una propiedad de cadena bodyText que se utiliza en la plantilla del componente.

El servicio modal lo proporciona la inyección de dependencia angular al declararlo como un parámetro para el constructor, el modificador protected hace el servicio accesible en la plantilla del componente, así como el componente.

import { Component } from '@angular/core';

import { ModalService } from './_services';

@Component({ selector: 'app-root', templateUrl: 'app.component.html' })
export class AppComponent {
    bodyText = 'This text can be updated in modal 1';

    constructor(protected modalService: ModalService) { }
}
 

Módulo de aplicación

Ruta: /src/app/app.module.ts

El módulo de la aplicación define el módulo raíz de la aplicación junto con los metadatos sobre el módulo. Para obtener más información sobre los módulos angulares, consulte https://angular.io/docs/ts/latest/guide/ngmodule.html.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { ModalComponent } from './_components';

@NgModule({
    imports: [
        BrowserModule,
        FormsModule
    ],
    declarations: [
        AppComponent,
        ModalComponent
    ],
    bootstrap: [AppComponent]
})

export class AppModule { }
 

Archivo index.html principal

Ruta: /src/index.html

El archivo index.html principal es la página inicial cargada por el navegador que inicia todo. La CLI de Angular (con Webpack bajo el capó) agrupa todos los archivos javascript compilados y los inyecta en el cuerpo de la página index.html para que el navegador pueda cargar y ejecutar los scripts.

<!DOCTYPE html>
<html>
<head>
    <base href="/" />
    <title>Angular 14 - Modal Popup Tutorial with Example</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
    <app-root></app-root>
</body>
</html>
 

Archivo main.ts

Ruta: /src/main.ts

El archivo main.ts es el punto de entrada utilizado por angular para iniciar y arrancar la aplicación.

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
    enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
    .catch(err => console.error(err));
 

Estilos LESS/CSS globales

Ruta: /src/styles.less

El archivo de estilos globales contiene estilos LESS/CSS que se aplican globalmente en toda la aplicación.

Acabo de agregar algunas cosas para embellecer un poco el ejemplo, ya que no usa Bootstrap ni ningún otro CSS.

/* You can add global styles to this file, and also import other style files */
body {
    font-family: Arial, Helvetica, sans-serif;
    padding: 20px;
}

h1 {
    font-weight: normal;
    margin-top: 0;
}

input[type="text"] {
    display: block;
    width: 100%;
    font-family: Arial, Helvetica, sans-serif;
    font-size: 1em;
    margin-top: 5px;
}

button {
    padding: 7px 10px;
    margin-right: 5px;
}

.credits {
    margin-top: 30px;
    border-top: 1px solid #ddd;
    text-align: center;
}

 


Suscríbete o Sígueme para actualizaciones

Suscríbete a mi canal de YouTube o sígueme en Twitter, Facebook o GitHub para recibir notificaciones cuando publique contenido nuevo.

Aparte de la codificación...

Actualmente estoy intentando viajar por Australia en motocicleta con mi esposa Tina en un par de Royal Enfield Himalayan. Puedes seguir nuestras aventuras en YouTube, Instagram y Facebook.


¿Necesita Ayuda Angular 14?

Buscar fiverr para encontrar ayuda rápidamente de desarrolladores Angular 14 experimentados.