Cómo crear e implementar una aplicación de material angular

Publicado: 2022-03-10
Resumen rápido ↬ Un tutorial para crear una aplicación web Angular 8 y una aplicación generadora de códigos QR completamente basada en Angular mientras está alojada en Netlify.

Angular es una de las opciones populares al crear nuevas aplicaciones web. Además, las especificaciones de "Diseño de materiales" se han convertido en la opción preferida para crear una experiencia mínima y atractiva en la actualidad. Por lo tanto, cualquier nuevo proyecto "Angular" utiliza principalmente la "Biblioteca de diseño de materiales angulares" para usar los componentes que siguen las especificaciones de diseño de materiales. Desde animaciones fluidas hasta comentarios de interacción adecuados, todo esto ya está disponible como parte de la biblioteca oficial de diseño de materiales para angular.

Una vez desarrollada la aplicación web, el siguiente paso es implementarla. Ahí es donde "Netlify" entra en escena. Con su interfaz muy fácil de usar, implementación automática, división de tráfico para pruebas A/B y varias otras características, Netlify es sin duda una gran herramienta.

El artículo será un recorrido por la creación de una aplicación web Angular 8 utilizando la biblioteca oficial de Angular Material Design. Crearemos una aplicación web generadora de códigos QR completamente basada en Angular mientras esté alojada en Netlify.

Los archivos para este tutorial se pueden encontrar en GitHub y aquí se implementa una versión de demostración.

Empezando

  1. Instalar Angular 8,
  2. Crear una cuenta de GitHub,
  3. Instala Git en tu computadora,
  4. Crea una cuenta Netlify.

Nota : usaré VSCode y Microsoft Windows como el IDE y el sistema operativo preferidos, aunque los pasos serían similares para cualquier otro IDE en cualquier otro sistema operativo.

Después de completar los requisitos previos anteriores, ¡comencemos!

¡Más después del salto! Continúe leyendo a continuación ↓

Simulacros y planificación

Antes de comenzar a crear el proyecto, sería beneficioso planificar con anticipación: ¿Qué tipo de interfaz de usuario queremos en nuestra aplicación? ¿Habrá piezas reutilizables? ¿Cómo interactuará la aplicación con los servicios externos?

Primero, verifique las simulaciones de la interfaz de usuario.

Página de inicio (vista previa grande)
Creación de una página QR (vista previa grande)
Página de historial (vista previa grande)

Estas son las tres páginas diferentes que estarán contenidas en la aplicación. La página de inicio será el punto de partida de nuestra aplicación. La creación de una página QR debe tratar con la creación de un nuevo código QR. La página Historial mostrará todos los códigos QR guardados.

Las maquetas no solo brindan una idea de la apariencia de la aplicación, sino que también segregan la responsabilidad de cada página.

Una observación (de los simulacros) es que parece que la barra de navegación superior es común en todas las páginas. Por lo tanto, la barra de navegación puede crearse como un componente reutilizable y reutilizarse.

Ahora que tenemos una buena idea de cómo se verá la aplicación y qué se puede reutilizar, comencemos.

Crear un nuevo proyecto angular

Inicie VSCode, luego abra una ventana de terminal en VSCode para generar un nuevo proyecto Angular.

Terminal en VSCode (vista previa grande)

El terminal se abrirá con una ruta predeterminada como se muestra en el aviso. Puede cambiar a un directorio preferido antes de continuar; en el caso de Windows, usaré el comando cd .

Navegación a la ruta preferida (vista previa grande)

En el futuro, angular-cli tiene un comando para generar nuevos proyectos ng new <project-name> . Simplemente use cualquier nombre de proyecto elegante que desee y presione enter, por ejemplo, ng new qr .

Esto activará la magia angular-cli; proporcionará algunas opciones para configurar algunos aspectos del proyecto, por ejemplo, agregar enrutamiento angular. Luego, en función de las opciones seleccionadas, generará el esqueleto completo del proyecto que se puede ejecutar sin ninguna modificación.

Para este tutorial, ingrese para el enrutamiento y seleccione CSS para el estilo. Esto generará un nuevo proyecto Angular:

Creando un nuevo proyecto Angular (Vista previa grande)

Ahora tenemos un proyecto Angular completamente funcional. Para asegurarnos de que todo funciona correctamente, podemos ejecutar el proyecto ingresando este comando en la terminal: ng serve . Uh oh, pero espera, esto resulta en un error. ¿Lo que podría haber ocurrido?

error de servicio ng (vista previa grande)

No te preocupes. Cada vez que crea un nuevo proyecto usando angular-cli, genera el esqueleto completo dentro de una carpeta con el nombre del proyecto especificado en el comando ng new qr . Aquí, tendremos que cambiar el directorio de trabajo actual al que acabamos de crear. En Windows, use el comando cd qr para cambiar de directorio.

Ahora, intente ejecutar el proyecto nuevamente con la ayuda de ng serve :

Proyecto en ejecución (vista previa grande)

Abra un navegador web, vaya a la URL https://localhost:4200 para ver el proyecto en ejecución. El comando ng serve ejecuta la aplicación en el puerto 4200 de forma predeterminada.

SUGERENCIA : Para ejecutarlo en un puerto diferente, usamos el comando ng serve --port <any-port> por ejemplo, ng serve --port 3000 .

Esto garantiza que nuestro proyecto Angular básico esté en funcionamiento. Vamonos.

Necesitamos agregar la carpeta del proyecto a VSCode. Vaya al menú "Archivo" y seleccione "Abrir carpeta" y seleccione la carpeta del proyecto. La carpeta del proyecto ahora se mostrará en la vista del Explorador a la izquierda.

Agregar biblioteca de materiales angulares

Para instalar la biblioteca de materiales de Angular, use el siguiente comando en la ventana del terminal: ng add @angular/material . Esto (nuevamente) hará algunas preguntas, como qué tema desea, si desea animaciones predeterminadas, si se requiere soporte táctil, entre otras. Simplemente seleccionaremos el tema Indigo/Pink predeterminado, Yes para agregar la biblioteca HammerJS y las animaciones del navegador.

Adición de material angular (vista previa grande)

El comando anterior también configura todo el proyecto para habilitar el soporte para los componentes de material.

  1. Agrega dependencias del proyecto a package.json ,
  2. Agrega la fuente Roboto al archivo index.html ,
  3. Agrega la fuente del icono de Material Design a su index.html ,
  4. También agrega algunos estilos CSS globales a:
    • Eliminar márgenes del cuerpo,
    • Establecer height: 100% en el HTML y el cuerpo,
    • Configure Roboto como la fuente de aplicación predeterminada.

Solo para asegurarse de que todo está bien, puede ejecutar el proyecto nuevamente en este punto, aunque no notará nada nuevo.

Agregar página de inicio

El esqueleto de nuestro proyecto ya está listo. Comencemos agregando la página de inicio.

(Vista previa grande)

Queremos mantener nuestra página de inicio simple, como la imagen de arriba. Esta página de inicio utiliza algunos componentes de material angular. Vamos a diseccionar.

  1. La barra superior es un elemento de nav HTML simple que contiene un botón de estilo de material, mat-button , con una imagen y un texto como elemento secundario. El color de la barra es el mismo que el color primario que se seleccionó al agregar la biblioteca de materiales de Angular;
  2. Una imagen centrada;
  3. Otro, mat-button , con solo un texto como elemento secundario. Este botón permitirá a los usuarios navegar a la página de historial;
  4. Una insignia de conteo, matBadge , adjunta al botón anterior, que muestra la cantidad de códigos QR guardados por el usuario;
  5. Un botón de acción flotante, mat-fab , en la esquina inferior derecha con el color de acento del tema seleccionado.

Divagando un poco, agreguemos primero otros componentes y servicios necesarios.

Agregar encabezado

Como se planeó anteriormente, la barra de navegación debe reutilizarse, creémosla como un componente angular separado. Abra la terminal en VSCode y escriba ng gc header (abreviatura de ng generar encabezado de componente) y presione Entrar. Esto creará una nueva carpeta llamada "encabezado" que contendrá cuatro archivos:

  • header.component.css : se utiliza para proporcionar estilo a este componente;
  • header.component.html : para agregar elementos HTML;
  • header.component.spec.ts : para escribir casos de prueba;
  • header.component.ts : para agregar la lógica basada en TypeScript.
Componente de encabezado (vista previa grande)

Para hacer que el encabezado se vea como estaba en los simulacros, agregue el siguiente código HTML en header.component.html :

 <nav class="navbar" [class.mat-elevation-z8]=true> <div> <button *ngIf="showBackButton" aria-hidden=false mat-icon-button routerLink="/"> <mat-icon> <i class="material-icons md-32">arrow_back</i> </mat-icon> </button> <span>{{currentTitle}}</span> </div> <button *ngIf="!showBackButton" aria-hidden=false mat-button class="button"> <img src="../../assets/qr-icon-white.png"> <span>QR Generator</span> </button> <button *ngIf="showHistoryNav" aria-hidden=false mat-button class="button" routerLink="/history"> <span>History</span> </button> </nav>

SUGERENCIA : para agregar elevación para cualquier componente de material, use [class.mat-elevation-z8]=true , el valor de elevación se puede cambiar cambiando el valor z , en este caso es z8 . Por ejemplo, para cambiar la elevación a 16, use [class.mat-elevation-z16]=true .

En el fragmento HTML anterior, se utilizan dos elementos de material angular: mat-icon y mat-button/mat-icon-button . Su uso es muy simple; primero, necesitamos agregar esos dos como módulos en nuestro app.module.ts como se muestra a continuación:

Importación de módulos para mat-icon y mat-button (vista previa grande)

Esto nos permitirá usar estos dos elementos de material angular en cualquier lugar de cualquier componente.

Para agregar botones de material, se utiliza el siguiente fragmento de HTML:

 <button mat-button> Material Button </button>

Hay diferentes tipos de elementos de botón de material disponibles en la biblioteca de materiales de Angular, como mat-raised-button , mat-flat-button , mat-fab y otros; simplemente reemplace el mat-button en el fragmento de código anterior con cualquier otro tipo.

Tipos de botones de material (vista previa grande)

El otro elemento es mat-icon que se utiliza para mostrar iconos disponibles en la biblioteca de iconos de materiales. Cuando se agregó la biblioteca de materiales de Angular al principio, también se agregó una referencia a la biblioteca de íconos de materiales, lo que nos permitió usar íconos de la amplia gama de íconos.

El uso es tan simple como:

 <mat-icon> <i class="material-icons md-32">arrow_back</i> </mat-icon>

La etiqueta <i> anidada se puede usar para cambiar el tamaño del ícono (aquí es md-32 ), lo que hará que el tamaño del ícono sea de 32 px en altura y ancho. Este valor puede ser md-24 , md-48 , etc. El valor de la etiqueta anidada <i> es el nombre del icono. (El nombre se puede encontrar aquí para cualquier otro ícono).

Accesibilidad

Siempre que se utilicen iconos o imágenes, es imperativo que proporcionen información suficiente para fines de accesibilidad o para un usuario de lector de pantalla. ARIA (Accessible Rich Internet Applications) define una forma de hacer que el contenido web y las aplicaciones web sean más accesibles para las personas con discapacidad.

Un punto a tener en cuenta es que los elementos HTML que tienen su semántica nativa (por ejemplo, nav ) no necesitan atributos ARIA; el lector de pantalla ya sabría que nav es un elemento de navegación y lo leería como tal.

Las especificaciones de ARIA se dividen en tres categorías: roles, estados y propiedades. Digamos que se usa un div para crear una barra de progreso en el código HTML. No tiene ninguna semántica nativa; El rol de ARIA puede describir este widget como una barra de progreso, la propiedad de ARIA puede denotar su característica, como que se puede arrastrar. El estado de ARIA describirá su estado actual, como el valor actual de la barra de progreso. Vea el fragmento a continuación:

 <div role="progressbar" aria-valuenow="75" aria-valuemin="0" aria-valuemax="100"> </div>

Del mismo modo, se utiliza un atributo de aria muy utilizado: aria-hidden=true/false . El valor verdadero hace que ese elemento sea invisible para los lectores de pantalla.

Dado que la mayoría de los elementos de la interfaz de usuario que se usan en esta aplicación tienen un significado semántico nativo, los únicos atributos de ARIA que se usan son para especificar los estados de visibilidad de ARIA. Para obtener información detallada, consulte esto.

El header.component.html contiene cierta lógica para ocultar y mostrar el botón Atrás según la página actual. Además, el botón Inicio también contiene una imagen/logotipo que debe agregarse a la carpeta /assets . Descarga la imagen desde aquí y guárdala en la carpeta /assets .

Para el estilo de la barra de navegación, agregue el siguiente css en header.component.css :

 .navbar { position: fixed; top: 0; left: 0; right: 0; z-index: 2; background: #3f51b5; display: flex; flex-wrap: wrap; align-items: center; padding: 12px 16px; } .button { color: white; margin: 0px 10px; }

Como queremos mantener el componente de encabezado reutilizable en otros componentes, para decidir qué se debe mostrar, los necesitaremos como parámetros de otros componentes. Esto requiere el uso del decorador @Input() que se vinculará a las variables que usamos en header.component.html .

Agregue estas líneas en el archivo header.component.ts :

 // Add these three lines above the constructor entry. @Input() showBackButton: boolean; @Input() currentTitle: string; @Input() showHistoryNav: boolean; constructor() { }

Los tres enlaces anteriores se pasarán como un parámetro de otros componentes que utilizará el componente de encabezado. Su uso será más claro una vez que avancemos.

Continuando, necesitamos crear una página de inicio que pueda ser representada por un componente Angular. Entonces, comencemos creando otro componente; escriba ng gc home en la terminal para generar automáticamente el componente de inicio. Como antes, se creará una nueva carpeta llamada "inicio" que contiene cuatro archivos diferentes. Antes de proceder a modificar esos archivos, agreguemos información de enrutamiento al módulo de enrutamiento angular.

Adición de enrutamiento

Angular proporciona una forma de asignar URL a un componente específico. Cada vez que ocurre alguna navegación, el marco Angular monitorea la URL y se basa en la información presente en el archivo app-routing.module.ts ; inicializa el componente mapeado. De esta manera, los diferentes componentes no necesitan asumir la responsabilidad de inicializar otros componentes. En nuestro caso, la aplicación cuenta con tres páginas navegables haciendo clic en diferentes botones. Logramos esto aprovechando el soporte de enrutamiento proporcionado por el marco Angular.

El componente de inicio debe ser el punto de partida de la aplicación. Agreguemos esta información al archivo app-routing.module.ts .

Componente de inicio de enrutamiento (vista previa grande)

La propiedad de path se establece como una cadena vacía; esto nos permite asignar la URL de la aplicación al componente de la página de inicio, algo así como google.com , que muestra la página de inicio de Google.

SUGERENCIA : el valor de la ruta nunca comienza con " / ", sino que usa una cadena vacía, aunque la ruta puede ser como search/coffee .

Volviendo al componente de la página de inicio, reemplace el contenido de home.component.html con esto:

 <app-header [showBackButton]="false" [currentTitle]=""></app-header> <app-profile></app-profile> <!-- FAB Fixed --> <button mat-fab class="fab-bottom-right" routerLink="/create"> <mat-icon> <i class="material-icons md-48">add</i> </mat-icon> </button>

Hay tres partes en el componente de inicio:

  1. El componente de encabezado reutilizable <app-header> ,
  2. Componente de perfil <app-profile> ,
  3. El botón de acción flotante en la parte inferior derecha.

El fragmento HTML anterior muestra cómo se utiliza el componente de encabezado reutilizable en otros componentes; simplemente usamos el selector de componentes y pasamos los parámetros requeridos.

El componente de perfil se crea para usarse como el cuerpo de la página de inicio; lo crearemos pronto.

El botón de acción flotante con el ícono + es una especie de botón de material angular de tipo mat-fab en la parte inferior derecha de la pantalla. Tiene la directiva de atributo routerLink que utiliza la información de ruta proporcionada en app-routing.module.ts para la navegación. En este caso, el botón tiene el valor de la ruta como /create , que se asignará para crear el componente.

Para hacer que el botón Crear flote en la parte inferior derecha, agregue el siguiente código CSS en home.component.css :

 .fab-bottom-right { position: fixed; left: auto; bottom: 5%; right: 10%; }

Dado que se supone que el componente de perfil administra el cuerpo de la página de inicio, dejaremos home.component.ts intacto.

Agregar componente de perfil

Abra la terminal, escriba ng gc profile y presione enter para generar el componente de perfil. Como se planeó anteriormente, este componente manejará el cuerpo principal de la página de inicio. Abra profile.component.html y reemplace su contenido con esto:

 <div class="center profile-child"> <img class="avatar" src="../../assets/avatar.png"> <div class="profile-actions"> <button mat-raised-button matBadge="{{historyCount}}" matBadgeOverlap="true" matBadgeSize="medium" matBadgeColor="accent" color="primary" routerLink="/history"> <span>History</span> </button> </div> </div>

El fragmento HTML anterior muestra cómo utilizar el elemento matBadge de la biblioteca de materiales. Para poder usarlo aquí, debemos seguir el procedimiento habitual de agregar MatBadgeModule al archivo app.module.ts . Las insignias son pequeños descriptores de estado pictóricos para elementos de la interfaz de usuario, como botones, íconos o textos. En este caso, se usa con un botón para mostrar el recuento de QR guardados por el usuario. La insignia de la biblioteca de materiales angular tiene otras propiedades, como establecer la posición de la insignia con matBadgePosition , matBadgeSize para especificar el tamaño y matBadgeColor para establecer el color de la insignia.

Se debe agregar un activo de imagen más a la carpeta de activos: Descargar. Guarde lo mismo en la carpeta /assets del proyecto.

Abra profile.component.css y agregue esto:

 .center { top: 50%; left: 50%; position: absolute; transform: translate(-50%, -50%); } .profile-child { display: flex; flex-direction: column; align-items: center; } .profile-actions { padding-top: 20px; } .avatar { border-radius: 50%; width: 180px; height: 180px; }

El CSS anterior logrará la interfaz de usuario según lo planeado.

Continuando, necesitamos algún tipo de lógica para actualizar el valor del conteo de historial como se reflejará en el matBadge usado anteriormente. Abra profile.component.ts y agregue el siguiente fragmento de forma adecuada:

 export class ProfileComponent implements OnInit { historyCount = 0; constructor(private storageUtilService: StorageutilService) { } ngOnInit() { this.updateHistoryCount(); } updateHistoryCount() { this.historyCount = this.storageUtilService.getHistoryCount(); } }

Hemos agregado StorageutilService pero no hemos creado dicho servicio hasta ahora. Ignorando el error, hemos completado nuestro componente de perfil que también finaliza nuestro componente de página de inicio. Revisaremos este componente de perfil después de crear nuestro servicio de utilidad de almacenamiento. Bien, entonces hagámoslo.

Almacenamiento local

HTML5 proporciona una función de almacenamiento web que se puede utilizar para almacenar datos localmente. Esto proporciona mucho más almacenamiento en comparación con las cookies: al menos 5 MB frente a 4 KB. Hay dos tipos de almacenamiento web con diferente alcance y duración: local y de sesión . El primero puede almacenar datos de forma permanente mientras que el segundo es temporal y para una sola sesión. La decisión de seleccionar el tipo se puede basar en el caso de uso, en nuestro escenario queremos guardar en todas las sesiones, por lo que optaremos por el almacenamiento local .

Cada dato se almacena en un par clave/valor. Usaremos el texto para el que se genera el QR como clave y la imagen QR codificada como una cadena base64 como valor. Cree una carpeta de entidad, dentro de la carpeta cree un nuevo archivo qr-object.ts y agregue el fragmento de código como se muestra:

Modelo de entidad QR (vista previa grande)

El contenido de la clase:

 export class QR { text: string; imageBase64: string; constructor(text: string, imageBase64: string) { this.imageBase64 = imageBase64; this.text = text; } }

Siempre que el usuario guarde el QR generado, crearemos un objeto de la clase anterior y guardaremos ese objeto utilizando el servicio de utilidad de almacenamiento.

Cree una nueva carpeta de servicios, crearemos muchos servicios, es mejor agruparlos.

Carpeta de servicios (vista previa grande)

Cambie el directorio de trabajo actual a services, cd services , para crear un nuevo servicio usando ng gs <any name> . Esta es una forma abreviada de ng generate service <any name> , escriba ng gs storageutil y presione enter

Esto creará dos archivos:

  • almacenamientoutil.servicio.ts
  • storageutil.servicio.spec.ts

El último es para escribir pruebas unitarias. Abra storageutil.service.ts y agregue esto:

 private historyCount: number; constructor() { } saveHistory(key : string, item :string) { localStorage.setItem(key, item) this.historyCount = this.historyCount + 1; } readHistory(key : string) : string { return localStorage.getItem(key) } readAllHistory() : Array<QR> { const qrList = new Array<QR>(); for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); const value = localStorage.getItem(key); if (key && value) { const qr = new QR(key, value); qrList.push(qr); } } this.historyCount = qrList.length; return qrList; } getHistoryCount(): number { if (this.historyCount) { return this.historyCount; } this.readAllHistory(); return this.historyCount; } deleteHistory(key : string) { localStorage.removeItem(key) this.historyCount = this.historyCount - 1; }

Importe la clase qr-object para corregir cualquier error. Para usar la función de almacenamiento local, no es necesario importar nada nuevo, solo use la palabra clave localStorage para guardar u obtener valor basado en una clave.

Ahora abra el archivo profile.component.ts nuevamente e importe la clase StorageutilService para finalizar correctamente el componente del perfil.

Ejecutando el proyecto, podemos ver que la página de inicio está lista según lo planeado.

Agregar Crear página QR

Ya tenemos nuestra página de inicio lista, aunque el botón crear/añadir no hace nada. No se preocupe, la lógica real ya estaba escrita. Usamos una directiva routerLink para cambiar la ruta base de la URL a /create pero no se agregó ninguna asignación al archivo app-routing.module.ts .

Vamos a crear un componente que se ocupará de la creación de nuevos códigos QR, escriba ng gc create-qr y presione enter para generar un nuevo componente.

Abra el archivo app-routing.module.ts y agregue la siguiente entrada a la matriz de routes :

 { path: 'create', component: CreateQrComponent },

Esto CreateQRComponent con la URL /create .

Abra create-qr.components.html y reemplace el contenido con esto:

 <app-header [showBackButton]="showBackButton" [currentTitle]="title" [showHistoryNav]="showHistoryNav"></app-header> <mat-card class="qrCard" [class.mat-elevation-z12]=true> <div class="qrContent"> <!--Close button section--> <div class="closeBtn"> <button mat-icon-button color="accent" routerLink="/" matTooltip="Close"> <mat-icon> <i class="material-icons md-48">close</i> </mat-icon> </button> </div> <!--QR code image section--> <div class="qrImgDiv"> <img *ngIf="!showProgressSpinner" src={{qrCodeImage}} width="200px" height="200px"> <mat-spinner *ngIf="showProgressSpinner"></mat-spinner> <div class="actionButtons" *ngIf="!showProgressSpinner"> <button mat-icon-button color="accent" matTooltip="Share this QR"> <mat-icon> <i class="material-icons md-48">share</i> </mat-icon> </button> <button mat-icon-button color="accent" (click)="saveQR()" matTooltip="Save this QR"> <mat-icon> <i class="material-icons md-48">save</i> </mat-icon> </button> </div> </div> <!--Textarea to write any text or link--> <div class="qrTextAreaDiv"> <mat-form-field> <textarea matInput [(ngModel)]="qrText" cdkTextareaAutosize cdkAutosizeMinRows="4" cdkAutosizeMaxRows="4" placeholder="Enter a website link or any text..."></textarea> </mat-form-field> </div> <!--Create Button--> <div class="createBtnDiv"> <button class="createBtn" mat-raised-button color="accent" matTooltip="Create new QR code" matTooltipPosition="above" (click)="createQrCode()">Create</button> </div> </div> </mat-card>

El fragmento anterior utiliza muchos de los elementos de la biblioteca de materiales de Angular. Como estaba previsto, tiene una referencia de componente de encabezado en la que se pasan los parámetros necesarios. El siguiente es el cuerpo principal de la página de creación; consiste en una tarjeta de material Angular o mat-card centrada y elevada hasta 12px como se usa [class.mat-elevation-z12]=true .

La tarjeta de material es solo otro tipo de contenedor que se puede usar como cualquier otra etiqueta div . Aunque la biblioteca de materiales proporciona algunas propiedades para diseñar información bien definida en una mat-card , como la ubicación de la imagen, el título, el subtítulo, la descripción y la acción, como se puede ver a continuación.

Ejemplo de tarjeta (vista previa grande)

En el fragmento HTML anterior, hemos utilizado mat-card como cualquier otro contenedor. Otro elemento de la biblioteca de materiales utilizado es matTooltip ; es solo otra información sobre herramientas fácil de usar, que se muestra cuando el usuario pasa el cursor sobre un elemento o lo presiona prolongadamente. Simplemente use el fragmento a continuación para mostrar información sobre herramientas:

 matTooltip="Any text you want to show"

Se puede usar con botones de iconos o cualquier otro elemento de la interfaz de usuario para transmitir información adicional. En el contexto de la aplicación, muestra información sobre el botón de icono de cierre. Para cambiar la ubicación de la información sobre herramientas, se utiliza matTooltipPosition :

 matTooltip="Any text you want to show" matTooltipPosition="above"

Además de matTooltip , mat-spinner se usa para mostrar el progreso de la carga. Cuando el usuario hace clic en el botón "Crear", se realiza una llamada de red. Aquí es cuando se muestra la rueda de progreso. Cuando la llamada de la red regresa con el resultado, simplemente ocultamos la rueda giratoria. Se puede usar simplemente así:

 <mat-spinner *ngIf="showProgressSpinner"></mat-spinner>

showProgressSpinner es una variable booleana que se utiliza para mostrar/ocultar la rueda de progreso. La biblioteca también proporciona algunos otros parámetros como [color]='accent' para cambiar el color, [mode]='indeterminate' para cambiar el tipo de indicador de progreso. Un indicador de progreso indeterminado no mostrará el progreso de la tarea, mientras que uno determinado puede tener diferentes valores para reflejar el progreso de la tarea. Aquí, se usa una rueda indeterminada ya que no sabemos cuánto tiempo tomará la llamada de red.

La biblioteca de materiales proporciona una variante de textarea que se ajusta a la guía de materiales, pero solo se puede usar como un descendiente de mat-form-field . El uso del área de texto material es tan simple como el HTML predeterminado, como se muestra a continuación:

 <mat-form-field> <textarea matInput placeholder="Hint text"></textarea> </mat-form-field>

matInput es una directiva que permite que la etiqueta de input nativa funcione con mat-form-field . La propiedad de placeholder de posición permite agregar cualquier texto de sugerencia para el usuario.

SUGERENCIA : use la propiedad de área de texto cdkTextareaAutosize para que se redimensione automáticamente. Use cdkAutosizeMinRows y cdkAutosizeMaxRows para configurar filas y columnas y los tres juntos para hacer que el área de texto cambie de tamaño automáticamente hasta que alcance el límite máximo de filas y columnas establecido.

Para usar todos estos elementos de la biblioteca de materiales, debemos agregarlos en el archivo app.module.ts .

Creación de importaciones de módulos QR (vista previa grande)

Hay una imagen de marcador de posición que se está utilizando en el HTML. Descárguelo y guárdelo en la carpeta /assets .

El HTML anterior también requiere estilo CSS, así que abra el archivo create-qr.component.ts y agregue lo siguiente:

 .qrCard { display: flex; flex-direction: column; align-items: center; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 20%; height: 65%; padding: 50px 20px; } .qrContent { display: flex; flex-direction: column; align-items: center; width: 100%; } .qrTextAreaDiv { width: 100%; display: flex; flex-direction: row; justify-content: center; padding: 0px 0px; position: absolute; bottom: 10%; } .createBtn { left: 50%; transform: translate(-50%, 0px); width: 80%; } .createBtnDiv { position: absolute; bottom: 5%; width: 100%; } .closeBtn { display: flex; flex-direction: row-reverse; align-items: flex-end; width: 100%; margin-bottom: 20px; } .closeBtnFont { font-size: 32px; color: rgba(0,0,0,0.75); } .qrImgDiv { top: 20%; position: absolute; display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; } .actionButtons { display: flex; flex-direction: row; padding-top: 20px; }

Conectemos la interfaz de usuario con lógica. Abra el archivo create-qr.component.ts y agregue el siguiente código, dejando las líneas que ya están presentes:

 export class CreateQrComponent implements OnInit { qrCodeImage = '../../../assets/download.png'; showProgressSpinner = false; qrText: string; currentQR; showBackButton = true; title = 'Generate New QR Code'; showHistoryNav = true; constructor(private snackBar: MatSnackBar, private restutil: RestutilService, private storageService: StorageutilService) { } ngOnInit() { } createQrCode() { //Check if any value is given for the qr code text if (!!this.qrText) { //Make the http call to load qr code this.loadQRCodeImage(this.qrText); } else { //Show snackbar this.showSnackbar('Enter some text first') } } public loadQRCodeImage(text: string) { // Show progress spinner as the request is being made this.showProgressSpinner = true; // Trigger the API call this.restutil.getQRCode(text).subscribe(image =>{ // Received the result - as an image blob - require parsing this.createImageBlob(image); }, error => { console.log('Cannot fetch QR code from the url', error) // Hide the spinner - show a proper error message this.showProgressSpinner = false; }); } private createImageBlob(image: Blob) { // Create a file reader to read the image blob const reader = new FileReader(); // Add event listener for "load" - invoked once the blob reading is complete reader.addEventListener('load', () => { this.qrCodeImage = reader.result.toString(); //Hide the progress spinner this.showProgressSpinner = false; this.currentQR = reader.result.toString(); }, false); // Read image blob if it is not null or undefined if (image) { reader.readAsDataURL(image); } } saveQR() { if (!!this.qrText) { this.storageService.saveHistory(this.qrText, this.currentQR); this.showSnackbar('QR saved') } else { //Show snackbar this.showSnackbar('Enter some text first') } } showSnackbar(msg: string) { //Show snackbar this.snackBar.open(msg, '', { duration: 2000, }); } }

Para proporcionar información contextual a los usuarios, también usamos MatSnackBar de la biblioteca de diseño de materiales. Esto aparece como una ventana emergente debajo de la pantalla y permanece durante unos segundos antes de desaparecer. Este no es un elemento, sino un servicio que se puede invocar desde el código de TypeScript.

El fragmento anterior con el nombre de método showSnackbar muestra cómo abrir una barra de refrigerios, pero antes de poder usarla, debemos agregar la entrada MatSnackBar en el archivo app.module.ts tal como lo hicimos con otros elementos de la biblioteca de materiales.

SUGERENCIA : en las versiones recientes de la biblioteca de materiales de Angular, no existe una forma sencilla de cambiar el estilo de la barra de refrigerios. En cambio, uno tiene que hacer dos adiciones al código.

Primero, use el CSS a continuación para modificar los colores de fondo y de primer plano:

 ::ng-deep snack-bar-container.snackbarColor { background-color: rgba(63, 81, 181, 1); } ::ng-deep .snackbarColor .mat-simple-snackbar { color: white; }

En segundo lugar, use una propiedad llamada panelClass para establecer el estilo en la clase CSS anterior:

 this.snackBar.open(msg, '', { duration: 2000, panelClass: ['snackbarColor'] });

Las dos combinaciones anteriores permitirán personalizar el estilo del componente snackbar de la biblioteca de diseño de materiales.

Esto completa los pasos sobre cómo crear una página QR, pero aún falta una pieza. Al verificar el archivo create-qr.component.ts , mostrará un error con respecto a la pieza que falta. La pieza que falta en este rompecabezas es RestutilService que es responsable de obtener la imagen del código QR de la API de terceros.

En la terminal, cambie el directorio actual a servicios escribiendo ng gs restutil y presionando Enter. Esto creará los archivos RestUtilService. Abra el archivo restutil.service.ts y agregue este fragmento:

 private edgeSize = '300'; private BASE_URL = 'https://api.qrserver.com/v1/create-qr-code/?data={data}!&size={edge}x{edge}'; constructor(private httpClient: HttpClient) { } public getQRCode(text: string): Observable { // Create the url with the provided data and other options let url = this.BASE_URL; url = url.replace("{data}", text).replace(/{edge}/g, this.edgeSize); // Make the http api call to the url return this.httpClient.get(url, { responseType: 'blob' }); } private edgeSize = '300'; private BASE_URL = 'https://api.qrserver.com/v1/create-qr-code/?data={data}!&size={edge}x{edge}'; constructor(private httpClient: HttpClient) { } public getQRCode(text: string): Observable { // Create the url with the provided data and other options let url = this.BASE_URL; url = url.replace("{data}", text).replace(/{edge}/g, this.edgeSize); // Make the http api call to the url return this.httpClient.get(url, { responseType: 'blob' }); }

El servicio anterior obtiene la imagen QR de la API de terceros y dado que la respuesta no es de tipo JSON, sino una imagen, especificamos el tipo de responseType como 'blob' en el fragmento anterior.

Angular proporciona la clase HttpClient para comunicarse con cualquier servidor compatible con HTTP. Proporciona muchas funciones, como filtrar la solicitud antes de que se active, recuperar la respuesta, permitir el procesamiento de la respuesta a través de devoluciones de llamada y otras. Para usar lo mismo, agregue una entrada para HttpClientModule en el archivo app.module.ts .

Finalmente, importe este servicio al archivo create-qr.component.ts para completar la creación del código QR.

¡Pero espera! Hay un problema con la lógica QR de creación anterior. Si el usuario usa el mismo texto para generar el QR una y otra vez, resultará en una llamada de red. Una forma de solucionar esto es almacenar en caché la solicitud, y así servir la respuesta desde la memoria caché si el texto de la solicitud es el mismo.

Solicitud de almacenamiento en caché

Angular proporciona una forma simplificada de realizar llamadas HTTP, HttpClient, junto con HttpInterceptors para inspeccionar y transformar las solicitudes o respuestas HTTP hacia y desde los servidores. Se puede usar para la autenticación o el almacenamiento en caché y muchas de esas cosas, se pueden agregar y encadenar múltiples interceptores para su posterior procesamiento. En este caso, estamos interceptando solicitudes y sirviendo la respuesta desde el caché si el texto QR es el mismo.

Cree una carpeta de interceptor, luego cree un archivo cache-interceptor.ts :

Interceptor de caché (vista previa grande)

Agregue el siguiente fragmento de código al archivo:

 import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpResponse, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { tap } from 'rxjs/operators'; import { of, Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class RequestCachingService implements HttpInterceptor { private cacheMap = new Map<string, HttpResponse<any>>(); constructor() { } intercept(req: HttpRequest , next: HttpHandler): Observable<HttpEvent<any>> { const cachedResponse = this.cacheMap.get(req.urlWithParams); if (cachedResponse) { return of(cachedResponse); } return next.handle(req).pipe(tap(event => { if (event instanceof HttpResponse) { this.cacheMap.set(req.urlWithParams, event); } })) } } import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpResponse, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { tap } from 'rxjs/operators'; import { of, Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class RequestCachingService implements HttpInterceptor { private cacheMap = new Map<string, HttpResponse<any>>(); constructor() { } intercept(req: HttpRequest , next: HttpHandler): Observable<HttpEvent<any>> { const cachedResponse = this.cacheMap.get(req.urlWithParams); if (cachedResponse) { return of(cachedResponse); } return next.handle(req).pipe(tap(event => { if (event instanceof HttpResponse) { this.cacheMap.set(req.urlWithParams, event); } })) } }

En el fragmento de código anterior, tenemos un mapa en el que la clave es la URL de solicitud y la respuesta es el valor. Verificamos si la URL actual está presente en el mapa; if it is, then return the response (the rest is handled automatically). If the URL is not in the map, we add it.

We are not done yet. An entry to the app.module.ts is required for its proper functioning. Add the below snippet:

 import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { CacheInterceptor } from './interceptor/cache-interceptor'; providers: [ { provide: HTTP_INTERCEPTORS, useClass: CacheInterceptor, multi: true } ],

This adds the caching feature to our application. Let's move on to the third page, the History page.

Adding The History Page

All the saved QR codes will be visible here. To create another component, open terminal type ng gc history and press Enter.

Open history.component.css and add the below code:

 .main-content { padding: 5% 10%; } .truncate { width: 90%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .center-img { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); display: flex; flex-direction: column; align-items: center; }

Open history.component.html and replace the content with this:

 <app-header [showBackButton]="showBackButton" [currentTitle]="title" [showHistoryNav]="showHistoryNav"></app-header> <div class="main-content"> <mat-grid-list cols="4" rowHeight="500px" *ngIf="historyList.length > 0"> <mat-grid-tile *ngFor="let qr of historyList"> <mat-card> <img mat-card-image src="{{qr.imageBase64}}"> <mat-card-content> <div class="truncate"> {{qr.text}} </div> </mat-card-content> <mat-card-actions> <button mat-button (click)="share(qr.text)">SHARE</button> <button mat-button color="accent" (click)="delete(qr.text)">DELETE</button> </mat-card-actions> </mat-card> </mat-grid-tile> </mat-grid-list> <div class="center-img" *ngIf="historyList.length == 0"> <img src="../../assets/no-see.png" width="256" height="256"> <span>Nothing to see here</span> </div> </div>

As usual, we have the header component at the top. Then, the rest of the body is a grid list that will show all the saved QR codes as individual mat-card . For the grid view, we are using mat-grid-list from the Angular material library. As per the drill, before we can use it, we have to first add it to the app.module.ts file.

Mat grid list actúa como un contenedor con múltiples elementos secundarios denominados mat-grid-tile . En el fragmento HTML anterior, cada mosaico se crea utilizando mat-card utilizando algunas de sus propiedades para la ubicación genérica de otros elementos de la interfaz de usuario. Podemos proporcionar el number of columns y rowHeight , que se usa para calcular el ancho automáticamente. En el fragmento anterior, proporcionamos tanto el número de columnas como el valor de rowHeight de fila.

Estamos usando una imagen de marcador de posición cuando el historial está vacío, descárguelo y agréguelo a la carpeta de activos.

Para implementar la lógica para completar toda esta información, abra el archivo history.component.ts y agregue el siguiente fragmento de código a la clase HistoryComponent :

 showBackButton = true; title = 'History'; showHistoryNav = false; historyList; constructor(private storageService: StorageutilService, private snackbar: MatSnackBar ) { } ngOnInit() { this.populateHistory(); } private populateHistory() { this.historyList = this.storageService.readAllHistory(); } delete(text: string) { this.storageService.deleteHistory(text); this.populateHistory(); } share(text: string) { this.snackbar.open(text, '', {duration: 2000,}) }

La lógica anterior solo obtiene todos los QR guardados y llena la página con ellos. Los usuarios pueden eliminar el QR guardado, lo que eliminará la entrada del almacenamiento local.

Así que esto acaba con nuestro componente de historia... ¿o no? Todavía necesitamos agregar el mapeo de rutas para este componente. Abra app-routing.module.ts y agregue también una asignación para la página de historial:

 { path: 'history', component: HistoryComponent },

Toda la matriz de rutas debería verse así ahora:

 const routes: Routes = [ { path: '', component: HomeComponent }, { path: 'create', component: CreateQrComponent }, { path: 'history', component: HistoryComponent }, ];

Ahora es un buen momento para ejecutar la aplicación para verificar el flujo completo, así que abre la terminal y escribe ng serve y presiona Enter. Luego, vaya a localhost:4200 para verificar el funcionamiento de la aplicación.

Agregar a GitHub

Antes de continuar con el paso de implementación, sería bueno agregar el proyecto a un repositorio de GitHub.

  1. Abra GitHub.
  2. Crear un nuevo repositorio.
  3. Nuevo repositorio de GitHub (vista previa grande)
  4. En VS Code, use la terminal y siga el primer conjunto de comandos mencionados en la guía de inicio rápido para enviar todos los archivos del proyecto.
  5. Agregar un proyecto en GitHub (vista previa grande)

Simplemente actualice la página para verificar si todos los archivos están visibles. A partir de este momento, cualquier cambio de git (como confirmación, extracción/empuje) se reflejará en este repositorio recién creado.

Netlify y despliegue

Nuestra aplicación se ejecuta en nuestra máquina local, pero para permitir que otros accedan a ella, debemos implementarla en una plataforma en la nube y registrarla con un nombre de dominio. Aquí es donde entra en juego Netlify. Proporciona servicios de implementación continua, integración con GitHub y muchas más funciones de las que beneficiarse. En este momento, queremos habilitar el acceso global a nuestra aplicación. Empecemos.

  1. Regístrate en Netlify.
  2. Desde el tablero, haz clic en el botón Nuevo sitio desde Git .
  3. Netlify nuevo sitio (vista previa grande)
  4. Haga clic en GitHub en la siguiente pantalla.
  5. Netlify seleccione proveedor de git (vista previa grande)
  6. Autoriza a Netlify para poder acceder a tus repositorios de GitHub.
  7. Autorización de Netlify GitHub (vista previa grande)
  8. Busque y seleccione el repositorio qr recién creado.
  9. Selección del repositorio de Netlify GitHub (vista previa grande)
  10. Netlify, en el siguiente paso, nos permite elegir la rama del repositorio de GitHub para las implementaciones. Normalmente, se usa la rama master , pero también se puede tener una rama release separada que contenga solo funciones estables y relacionadas con el lanzamiento.
  11. Compilación e implementación de Netlify (vista previa grande)

Como se trata de una aplicación web Angular, agregue ng build --prod como comando de compilación. Los directorios publicados serán dist/qr como se menciona en el archivo angular.json .

Ruta de construcción angular (vista previa grande)

Ahora haga clic en el botón Deploy site que activará la compilación de un proyecto con el comando ng build --prod y enviará el archivo a dist/qr .

Dado que proporcionamos la información de la ruta a Netlify, automáticamente recogerá los archivos correctos para dar servicio a la aplicación web. Netlify agrega un dominio aleatorio a nuestra aplicación por defecto.

Sitio de Netlify implementado (vista previa grande)

Ahora puede hacer clic en el enlace proporcionado en la página anterior para acceder a la aplicación desde cualquier lugar. Finalmente, la aplicación ha sido desplegada.

Dominio personalizado

En la imagen de arriba, se muestra la URL de nuestra aplicación mientras que el subdominio se genera aleatoriamente. Cambiemos eso.

Haga clic en el botón Domain settings , luego en la sección Dominios personalizados, haga clic en el menú de 3 puntos y seleccione Edit site name .

Dominio personalizado (vista previa grande)

Esto abrirá una ventana emergente en la que se puede ingresar un nuevo nombre de sitio; este nombre debe ser único en todo el dominio de Netlify. Ingrese cualquier nombre de sitio que esté disponible y haga clic en Guardar .

Nombre del sitio (vista previa grande)

Ahora el enlace a nuestra aplicación se actualizará con el nuevo nombre del sitio.

Prueba dividida

Otra característica interesante que ofrece Netlify es la prueba dividida. Permite la división del tráfico para que diferentes conjuntos de usuarios interactúen con diferentes implementaciones de aplicaciones. Podemos agregar nuevas funciones a una rama diferente y dividir el tráfico en la implementación de esta rama, analizar el tráfico y luego fusionar la rama de funciones con la rama de implementación principal. Vamos a configurarlo.

El requisito previo para habilitar las pruebas divididas es un repositorio de GitHub con al menos dos sucursales. Dirígete al repositorio de aplicaciones en GitHub que se creó anteriormente y crea una nueva rama a .

Crear nueva rama (Vista previa grande)

El repositorio ahora tendrá una rama master y a rama. Netlify debe configurarse para realizar implementaciones en sucursales, así que abra el panel de control de Netlify y haga clic en Settings . En el lado izquierdo, haga clic en Build & Deploy , luego Continuous Deployment , luego en el lado derecho en la sección Deploy contexts , haga clic en Edit settings .

Despliegues de sucursales (versión preliminar grande)

En la subsección Branch deploys , seleccione la opción "Permitirme agregar sucursales individuales", ingrese los nombres de las sucursales y guárdelo.

La implementación de brances es otra característica útil proporcionada por Netlify; podemos seleccionar qué ramas del repositorio de GitHub implementar, y también podemos habilitar vistas previas para cada solicitud de extracción a la rama master antes de fusionar. Esta es una característica interesante que permite a los desarrolladores probar sus cambios en vivo antes de agregar sus cambios de código a la rama de implementación principal.

Ahora, haga clic en la opción de la pestaña Split Testing en la parte superior de la página. Las configuraciones de prueba dividida se presentarán aquí.

Prueba dividida (vista previa grande)

Podemos seleccionar la rama (que no sea la rama de producción), en este caso a . También podemos jugar con la configuración de dividir el tráfico. Según el porcentaje de tráfico que se ha asignado a cada sucursal, Netlify redirigirá a algunos usuarios a la aplicación implementada mediante a sucursal y otros a la sucursal master . Después de configurar, haga clic en el botón Start test para habilitar la división del tráfico.

SUGERENCIA : es posible que Netlify no reconozca que el repositorio de GitHub conectado tiene más de una rama y puede dar este error:

Error de prueba dividida (vista previa grande)

Para resolver esto, simplemente vuelva a conectarse al repositorio desde las opciones de Build & Deploy .

Netlify también ofrece muchas otras funciones. Acabamos de revisar algunas de sus funciones útiles para demostrar la facilidad de configurar diferentes aspectos de Netlify.

Esto nos lleva al final de nuestro viaje. Hemos creado con éxito un diseño de material angular basado en una aplicación web y lo implementamos en Netlify.

Conclusión

Angular es un marco excelente y popular para el desarrollo de aplicaciones web. Con la biblioteca oficial de diseño de materiales de Angular, es mucho más fácil crear aplicaciones que se adhieran a las especificaciones de diseño de materiales para una interacción muy natural con los usuarios. Además, la aplicación desarrollada con un gran marco debe usar una gran plataforma para su implementación, y Netlify es precisamente eso. Con una evolución constante, un gran soporte y una plétora de características, seguramente es una gran plataforma para llevar aplicaciones web o sitios estáticos a las masas. Con suerte, este artículo proporcionará ayuda para comenzar con un nuevo proyecto de Angular desde solo un pensamiento hasta la implementación.

Otras lecturas

  • arquitectura angular
  • Más componentes de material angular
  • Más sobre las características de Netlify