Tutorial de Angular 6: Nuevas funciones con nueva potencia

Publicado: 2022-03-11

¡Angular 6 está disponible! Los cambios más destacados están en su CLI y en cómo se inyectan los servicios. Si está buscando escribir su primera aplicación Angular 6, o aplicación Angular/Firebase, en este tutorial, repasaremos los pasos básicos de la configuración inicial y crearemos una pequeña aplicación de diario.

Angular 6: Los fundamentos

Si nunca has usado Angular antes, déjame darte una breve descripción y cómo funciona.

Angular es un marco de JavaScript diseñado para admitir la creación de aplicaciones de una sola página (SPA) tanto para escritorio como para dispositivos móviles.

El marco incluye un conjunto completo de directivas y módulos que le permiten implementar fácilmente algunos de los escenarios más comunes para una aplicación web, como navegación, autorización, formularios e informes. También viene con todos los paquetes necesarios para agregar pruebas usando el marco Jasmine y ejecutarlas usando los corredores de prueba Karma o Protractor.

La arquitectura angular se basa en componentes, plantillas, directivas y servicios. Proporciona un mecanismo de inyección de dependencia integrado para sus servicios, así como un enlace de datos bidireccional para conectar sus vistas con el código de su componente.

Angular usa TypeScript, un superconjunto escrito de JS, y hará que algunas cosas sean más fáciles, especialmente si proviene de un entorno de lenguaje escrito.

Angular 6: nuevas características

Un breve resumen de las nuevas características en Angular 6:

  • Una política de sincronización de los números de versión principales para los paquetes de framework ( @angular/core , @angular/common , @angular/compiler , etc.), CLI, Material y CDK. Esto ayudará a que la compatibilidad cruzada sea más clara en el futuro: con un vistazo rápido al número de versión, puede saber si los paquetes clave son compatibles entre sí.
  • Nuevos comandos ng CLI:
    • ng update para actualizar las versiones de los paquetes de forma inteligente, actualizando las versiones de las dependencias y manteniéndolas sincronizadas. (Por ejemplo, cuando se ejecuta ng update @angular/core todos los marcos se actualizarán, así como RxJS). También ejecutará esquemas si el paquete los incluye. (Si una versión más reciente incluye cambios importantes que requieren cambios en el código, el esquema actualizará su código por usted).
    • ng add para agregar nuevos paquetes (y ejecutar scripts, si corresponde)
  • Los servicios ahora hacen referencia a los módulos que los proporcionarán, en lugar de que los módulos hagan referencia a los servicios, como solían ser.

Como ejemplo de lo que significa este último cambio, donde solía verse su código:

 @NgModule({ // ... providers: [MyService] })

…con este cambio particular en Angular 6, se verá así:

 @Injectabe({ providedIn: 'root', })

Estos se denominan proveedores que se pueden sacudir en el árbol y permiten que el compilador elimine los servicios no referenciados, lo que da como resultado paquetes de menor tamaño.

Angular 6 CLI

La interfaz de línea de comandos ng es una pieza muy importante de Angular y le permite moverse más rápido al codificar su aplicación.

Con la CLI, puede montar la configuración inicial de su aplicación muy fácilmente, generar nuevos componentes, directivas, etc., y compilar y ejecutar su aplicación en su entorno local.

Creando un Proyecto Angular 6

Bien, suficiente charla. Ensuciémonos las manos y comencemos a programar.

Para comenzar, necesitará Node.js y npm instalados en su máquina.

Ahora, sigamos adelante e instalemos la CLI:

 npm install -g @angular/cli

Esto instalará el comando ng CLI globalmente, debido al interruptor -g .

Una vez que tengamos eso, podemos obtener el andamio inicial para nuestra aplicación con ng new :

 ng new my-memories --style=scss

Esto creará una carpeta my-memories , creará todos los archivos necesarios para que su configuración inicial esté lista para comenzar e instalará todos los paquetes necesarios. El --style=scss es opcional y configurará el compilador para compilar archivos SCSS a CSS, que necesitaremos más adelante.

Una vez que se completa la instalación, puede usar un cd my-memories y ejecutar ng serve . Esto iniciará el proceso de compilación y un servidor web local que sirva su aplicación en http://localhost:4200 .

Una aplicación Angular 6 inmediatamente después del scaffolding

Lo que sucede detrás de escena es que la CLI transpila todos los .ts (archivos TypeScript) a Vanilla JS, recopila todas las dependencias requeridas de la carpeta de paquetes node_modules y genera el resultado en un conjunto de archivos que se sirven a través de un servidor web local. que se ejecuta en el puerto 4200.

Archivos de proyecto

Si no está familiarizado con la estructura de carpetas del proyecto de Angular, lo más importante que debe saber es que todo el código relacionado con la aplicación va dentro de la carpeta src . Por lo general, creará todos sus módulos y directivas en esa carpeta siguiendo la arquitectura de su aplicación (por ejemplo, usuario, carrito, producto).

La estructura de carpetas del proyecto Angular 6

Configuración inicial

Bien, hasta ahora tenemos la configuración inicial de nuestra aplicación. Vamos a empezar a hacer algunos cambios.

Antes de comenzar, profundicemos un poco en la carpeta src . La página inicial es index.html :

 <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>MyMemories</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> </head> <body> <app-root></app-root> </body> </html>

Aquí vemos algo de HTML básico y la etiqueta <app-root> . Este es un componente Angular, y donde Angular 6 inserta nuestro código de componente.

Encontraremos el archivo app/app.component.ts , que tiene el selector app-root para que coincida con lo que hay en el archivo index.html .

El componente es una clase de TypeScript decorada y, en este caso, contiene la propiedad de title . El decorador @Component le dice a Angular que incluya el comportamiento del componente en la clase. Además del selector, especifica qué archivo HTML representar y qué hojas de estilo usar.

 import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent { title = 'app'; }

Si observamos app.component.html , veremos el enlace de interpolación {{title}} . Aquí es donde ocurre todo el enlace mágico, y Angular representará el valor de la propiedad del título de la clase y lo actualizará cada vez que cambie.

 <!--The content below is only a placeholder and can be replaced.--> <div> <h1> Welcome to {{ title }}! </h1> <img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg=="> </div> <h2>Here are some links to help you start: </h2> <ul> <li> <h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2> </li> <li> <h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2> </li> <li> <h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2> </li> </ul>

Avancemos y actualicemos el title de la clase a 'My Memories!' .

 ... export class AppComponent { title = 'My Memories!'; } ...

Veremos que el compilador procesa nuestro cambio y el navegador se actualiza para mostrar nuestro título actualizado.

Esto significa que el ng serve de Angular 6 observa los cambios de nuestro archivo y los renderiza cada vez que se introduce un cambio en cualquier archivo.

Para hacer que la codificación sea más amigable y evitar la actualización de la página completa cada vez que hacemos cambios, podemos aprovechar el Reemplazo del módulo en caliente (HMR) del paquete web, que solo actualiza la parte de JS/CSS que se cambió en lugar de producir una actualización completa para mostrar sus cambios.

Configuración de HMR

En primer lugar, tenemos que configurar el entorno.

Cree un archivo src/environments/environment.hmr.ts con el siguiente contenido:

 export const environment = { production: false, hmr: true };

Actualice src/environments/environment.prod.ts y agregue hmr: false flag al entorno:

 export const environment = { production: true, hmr: false };

Luego, actualice src/environments/environment.ts y agregue hmr: false flag al entorno allí también:

 export const environment = { production: false, hmr: false };

A continuación, en el archivo angular.json , actualice esta parte:

 "projects": { "my-memories": { // ... "architect": { "build": { // ... "configurations": { "hmr":{ "fileReplacements":[ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.hmr.ts" } ] }, // ...

Y en projectsmy-memoriesarchitectserveconfigurations :

 "projects": { "my-memories": { "architect": { // ... "serve": { // ... "configurations": { "hmr": { "browserTarget": "my-memories:build:hmr" }, // ...

Ahora actualice tsconfig.app.json para incluir los types necesarios (bueno, tipo) agregando esto en compilerOptions :

 "compilerOptions": { // ... "types": [ "node" ]

A continuación, instalaremos el @angularclass/hmr como una dependencia de desarrollo:

 npm install --save-dev @angularclass/hmr

Luego configúrelo creando un archivo src/hmr.ts :

 import { NgModuleRef, ApplicationRef } from '@angular/core'; import { createNewHosts } from '@angularclass/hmr'; export const hmrBootstrap = (module: any, bootstrap: () => Promise<NgModuleRef<any>>) => { let ngModule: NgModuleRef<any>; module.hot.accept(); bootstrap().then(mod => ngModule = mod); module.hot.dispose(() => { const appRef: ApplicationRef = ngModule.injector.get(ApplicationRef); const elements = appRef.components.map(c => c.location.nativeElement); const makeVisible = createNewHosts(elements); ngModule.destroy(); makeVisible(); }); };

A continuación, actualice src/main.ts para usar la función anterior:

 import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; import { hmrBootstrap } from './hmr'; if (environment.production) { enableProdMode(); } const bootstrap = () => platformBrowserDynamic().bootstrapModule(AppModule); if (environment.hmr) { if (module[ 'hot' ]) { hmrBootstrap(module, bootstrap); } else { console.error('HMR is not enabled for webpack-dev-server!'); console.log('Are you using the --hmr flag for ng serve?'); } } else { bootstrap().catch(err => console.log(err)); }

Lo que estamos haciendo aquí es hacer que la llamada de arranque sea una función anónima y luego preguntar si el indicador de environment.hmr es verdadero. Si es así, llamamos a la función previamente definida desde hmr.ts que permitió el reemplazo del módulo en caliente; de lo contrario, lo arrancamos como solíamos hacerlo.

Ahora, cuando ng serve --hmr --configuration=hmr , invocaremos la configuración de hmr , y cuando hagamos cambios en los archivos, obtendremos actualizaciones sin una actualización completa. El primero --hmr es para webpack, y --configuration=hmr es para que Angular use el entorno hmr .

Aplicación web progresiva (PWA)

Para agregar compatibilidad con Angular 6 PWA y habilitar la carga sin conexión para la aplicación, podemos utilizar uno de los nuevos comandos CLI, ng add :

 ng add @angular/[email protected]

Tenga en cuenta que estoy agregando la versión, ya que la última versión cuando estaba escribiendo este tutorial arrojaba un error. (Puede probar sin él y verificar si funciona para usted usando simplemente ng add @angular/pwa ).

Bien, después de que hayamos ejecutado el comando, veremos muchos cambios en nuestro proyecto. Los cambios más importantes son que agregó:

  • Una referencia a manifest.json en el archivo de activos angular.json , para que se incluya en el resultado de la compilación, así como "serviceWorker": true en las compilaciones de producción
  • El archivo ngsw-config.json con la configuración inicial para almacenar en caché todos los archivos necesarios para que la aplicación se ejecute
  • Una metaetiqueta manifest.json en el archivo index.html
  • El propio archivo manifest.json , con una configuración básica para la aplicación
  • El trabajador del servicio se carga en el módulo de la aplicación ServiceWorkerModule.register('/ngsw-worker.js', { enabled: environment.production }) (tenga en cuenta que el trabajador del servicio solo estará habilitado en entornos de producción)

Entonces, esto ahora significa que cuando el usuario acceda por primera vez a la URL, los archivos se descargarán. Después de eso, si el usuario intenta acceder a la URL sin servicio de red, la aplicación seguirá funcionando extrayendo esos archivos almacenados en caché.

Agregar la biblioteca de interfaz de usuario de Material Angular 6

Hasta ahora tenemos la configuración inicial y estamos listos para comenzar a construir nuestra aplicación. Para hacer uso de componentes ya construidos, podemos usar la versión Angular 6 de Material.

Para instalar el paquete de material en nuestra aplicación, volveremos a utilizar ng add :

 ng add @angular/material

Después de ejecutar eso, veremos algunos paquetes nuevos agregados y una configuración de estilo básica:

  • index.html incluye la fuente Roboto y los íconos de Material
  • BrowserAnimationsModule se agrega a nuestro AppModule
  • angular.json tiene el tema rosa índigo ya incluido para nosotros

Indicando su elección de un tema Angular 6 preconstruido

Deberá reiniciar ng serve para retomar el tema, o puede elegir otro tema preconstruido.

Relacionado: Cree aplicaciones web ultramodernas con material angular

Diseño básico

Para tener el diseño inicial de sidenav, usaremos los esquemas que vienen con Material. Pero está bien si desea utilizar un diseño diferente.

(En pocas palabras, los esquemas le permiten aplicar transformaciones a un proyecto: puede crear, modificar o eliminar archivos según sea necesario. En este caso, crea un diseño de navegación lateral para nuestra aplicación).

 ng generate @angular/material:material-nav --name=my-nav

Esto creará un componente sidenav con la configuración mínima lista para comenzar. ¿No es genial?

También ha incluido todos los módulos necesarios en nuestro app.module.ts .

Un componente Angular 6 "my-nav" recién creado

Dado que usamos SCSS, debemos cambiar el nombre del archivo my- my-nav.component.css a my-nav.component.scss y, en my-nav.component.ts actualizar las styleUrls de estilo de referencia correspondientes para usar el nuevo nombre.

Ahora, para hacer uso del nuevo componente, vayamos a app.component.html y eliminemos todo el código inicial, dejando solo:

 <app-my-nav></app-my-nav>

Cuando volvamos al navegador, esto es lo que veremos:

Un diseño de cuatro paneles con Menú en la esquina superior izquierda, my-memories al lado y tres enlaces numerados debajo de Menú; el cuarto panel está vacío

Actualicemos los enlaces para tener solo las dos opciones que queremos.

Primero, vamos a crear dos nuevos componentes:

 ng gc AddMemory ng generate @angular/material:material-table --name=view-memories

(El segundo es un esquema de material utilizado para crear una tabla).

A continuación, en my-nav actualizaremos para configurar los enlaces e incluir el <router-outlet> para mostrar nuestros componentes de contenido:

 <mat-sidenav-container class="sidenav-container"> <mat-sidenav #drawer class="sidenav" fixedInViewport="true" [attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'" [mode]="(isHandset$ | async) ? 'over' : 'side'" [opened]="!(isHandset$ | async)"> <mat-toolbar color="primary">Menu</mat-toolbar> <mat-nav-list> <a mat-list-item [routerLink]="['/add-memory']">Add Memory</a> <a mat-list-item [routerLink]="['/view-memories']">View My Memories</a> </mat-nav-list> </mat-sidenav> <mat-sidenav-content> <mat-toolbar color="primary"> <button type="button" aria-label="Toggle sidenav" mat-icon-button (click)="drawer.toggle()" *ngIf="isHandset$ | async"> <mat-icon aria-label="Side nav toggle icon">menu</mat-icon> </button> <span>my-memories</span> </mat-toolbar> <router-outlet></router-outlet> </mat-sidenav-content> </mat-sidenav-container>

Además, en app.component.html necesitamos actualizarlo para que solo tenga el <router-outlet> principal (es decir, eliminar <my-nav> ):

 <router-outlet></router-outlet>

A continuación, en el AppModule incluiremos las rutas:

 import { RouterModule, Routes } from '@angular/router'; // ... imports: [ // ... RouterModule.forRoot([ { path: '', component: MyNavComponent, children: [ { path: 'add-memory', component: AddMemoryComponent }, { path: 'view-memories', component: ViewMemoriesComponent } ] }, ]), ]

Tenga en cuenta que estamos configurando MyNavComponent como principal y los dos componentes que creamos como secundarios. Esto se debe a que incluimos el <router-outlet> en MyNavComponent , y cada vez que llegamos a una de esas dos rutas, representaremos el componente secundario donde se colocó el <router-outlet> .

Luego de esto, cuando sirvamos la app deberíamos ver:

Los enlaces de la izquierda se han reemplazado con Agregar memoria y Ver mis recuerdos, con este último seleccionado. El panel vacío ahora tiene una tabla con números de identificación y nombres.

Cree la aplicación (un diario de recuerdos)

Bien, ahora vamos a crear el formulario para guardar nuevos recuerdos en nuestro diario.

Tendremos que importar algunos módulos de material y el módulo de formularios a nuestro app.module.ts :

 import { FormsModule } from '@angular/forms'; import { MatCardModule, MatFormFieldModule, MatInputModule, MatDatepickerModule, MatNativeDateModule } from '@angular/material'; // ... Imports:[ // ... MatCardModule, MatFormFieldModule, MatInputModule, MatDatepickerModule, FormsModule, MatNativeDateModule, // ... ]

Y luego en add-memory.component.html , agregaremos el formulario:

 <form #form="ngForm" (ngSubmit)="onSubmit()"> <mat-card class="memory-card"> <mat-card-title> Add a memory </mat-card-title> <mat-card-content> <mat-form-field> <input disabled matInput placeholder="Select the date..." [(ngModel)]="memory.date" name="date" [matDatepicker]="date"> <mat-datepicker-toggle matSuffix [for]="date"></mat-datepicker-toggle> <mat-datepicker disabled="false" #date></mat-datepicker> </mat-form-field> <mat-form-field> <textarea placeholder="Enter your memory..." rows="3" maxlength="300" matInput [(ngModel)]="memory.text" name="memory"></textarea> </mat-form-field> </mat-card-content> <mat-card-actions> <button mat-button type="submit">Save me!</button> </mat-card-actions> </mat-card> </form> <pre> {{ memory | json }} </pre>

Aquí estamos usando una mat-card y agregando dos campos, una date y un área de textarea .

Tenga en cuenta que estamos usando [(ngModel)] . Esta directiva de Angular unirá la expresión memory.date y la propiedad de memory en la clase entre sí, como veremos más adelante. ( [(ngModel)] es azúcar sintáctico: un atajo para realizar un enlace de datos bidireccional de la clase a la vista y de la vista a la clase. Esto significa que cuando ingresa texto en la vista, memory.date reflejará esos cambios en la instancia de clase, y si realiza cambios en memory.date en la instancia de clase, la vista reflejará los cambios).

En add-memory.component.ts , el código se verá así:

 import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-add-memory', templateUrl: './add-memory.component.html', styleUrls: ['./add-memory.component.scss'] }) export class AddMemoryComponent implements OnInit { memory: any = {}; constructor() { } ngOnInit() { } onSubmit() { console.log(this.memory); } }

Aquí, estamos inicializando la propiedad de memory vinculada a través ngModel . Cuando se crea una instancia del componente AddMemoryComponent , memory será un objeto vacío. Luego, cuando se ejecute la directiva ngModel , podrá asignar el valor de entrada a memory.date y memory.text . Si no hiciéramos esto, obtendríamos un error de Cannot set property 'date/text' of undefined .

Mientras tanto, add-memory.component.scss debe tener:

 .memory-card { min-width: 150px; max-width: 400px; width: 100%; margin: auto; } .mat-form-field { width: 100%; }

Ya que tenemos <pre> {{ memory | json }} </pre> <pre> {{ memory | json }} </pre> podemos ver el estado actual de la memory en la vista. Si vamos al navegador, aquí está el resultado hasta ahora:

El prototipo de la aplicación de diario "my-memories", que muestra la representación interna de la entrada del usuario (fecha y texto).

En la vista, vinculamos el formulario mediante (ngSubmit)="onSubmit()" a la función onSubmit en la clase.

 onSubmit() { console.log(this.memory); }

Entonces, cuando haces clic en "¡Sálvame!" botón, obtendrá la representación interna de la entrada del usuario enviada al registro de la consola:

La representación interna de la entrada del usuario en el registro de la consola.

Tutorial de Angular 6: Conexión con Firebase

Lo que haremos a continuación es conectar nuestro proyecto a Firebase para guardar nuestros recuerdos.

Primero, iremos a la consola Firebase y crearemos un proyecto allí.

Agregar un proyecto de Firebase.

En segundo lugar, instalaremos los paquetes firebase y angularfire2 :

 npm install firebase angularfire2 --save

Y luego en cada uno de estos tres archivos:

  1. /src/environments/environment.ts
  2. /src/environments/environment.hmr.ts
  3. /src/environments/environment.prod.ts

… agregaremos nuestra configuración de Firebase:

 export const environment = { // ... firebase: { apiKey: '<your-key>', authDomain: '<your-project-authdomain>', databaseURL: '<your-database-URL>', projectId: '<your-project-id>', storageBucket: '<your-storage-bucket>', messagingSenderId: '<your-messaging-sender-id>' } };

Puede obtener los detalles de configuración necesarios para los archivos anteriores haciendo clic en "Agregar Firebase a su aplicación web" en la página de descripción general del proyecto.

Después de eso, incluiremos los módulos de Firebase en nuestro app.module.ts :

 import { AngularFireModule } from 'angularfire2'; import { AngularFireDatabaseModule } from 'angularfire2/database'; import { environment } from '../environments/environment'; // ... Imports:[ // ... AngularFireModule.initializeApp(environment.firebase), AngularFireDatabaseModule, // ... ]

Y en add-memory.component.ts , inyectamos la base de datos en el constructor y guardamos los valores del formulario en la base de datos. Cuando la promesa de inserción de Firebase es exitosa, registramos el éxito en la consola y reiniciamos el modelo:

 import { AngularFireDatabase } from 'angularfire2/database'; // ... constructor(private db: AngularFireDatabase) { } // ... onSubmit() { this.memory.date = new Date(this.memory.date).valueOf(); this.db.list('memories').push(this.memory) .then(_ => { this.memory = {} console.log('success') }) } 

Permitir el acceso de lectura y escritura a su base de datos de Firebase.

Deberá permitir el acceso público a las reglas de la base de datos, para que los usuarios anónimos puedan leer y escribir en ella. Tenga en cuenta que con esta configuración, cualquier usuario podrá leer/cambiar/eliminar los datos de su aplicación. Asegúrese de configurar sus reglas en consecuencia antes de pasar a producción.

Además, para recoger los cambios en el entorno, deberá reiniciar el proceso de ng serve .

Ahora, cuando volvamos al navegador y hagamos clic en el botón Guardar, veremos que la memoria se agregó a la base de datos:

Nuestra memoria de prueba se agregó a la base de datos Firebase de nuestra aplicación de diario.

Echemos un vistazo a cómo podemos recuperar nuestros recuerdos y mostrarlos en la tabla Material.

Cuando creamos la tabla usando ng generate @angular/material:material-table --name=view-memories', we automatically got a file view-memories/view-memories-datasource.ts`. Este archivo contiene datos falsos, por lo que tendremos que cambiarlo para comenzar a extraerlo de Firebase.

En view-memories-datasource.ts , eliminaremos EXAMPLE_DATA y estableceremos una matriz vacía:

 export class ViewMemoriesDataSource extends DataSource<ViewMemoriesItem> { data: ViewMemoriesItem[] = []; // ...

Y en getSortedData actualizaremos los nombres de los campos:

 private getSortedData(data: ViewMemoriesItem[]) { if (!this.sort.active || this.sort.direction === '') { return data; } return data.sort((a, b) => { const isAsc = this.sort.direction === 'asc'; switch (this.sort.active) { case 'text': return compare(a.name, b.name, isAsc); case 'date': return compare(+a.id, +b.id, isAsc); default: return 0; } }); }

En view-memories.component.html actualizaremos los nombres de las columnas a la date y el text de nuestro modelo de memoria. Tenga en cuenta que, dado que guardamos la fecha en formato de milisegundos, aquí estamos usando una tubería de fecha para transformar el valor para mostrar en un formato de fecha más amigable para los humanos. Por último, estamos eliminando [length]="dataSource.data.length" del paginador, ya que cargaremos los datos de forma asíncrona desde Firebase:

 <div class="mat-elevation-z8"> <table mat-table #table [dataSource]="dataSource" matSort aria-label="Elements"> <!-- Id Column --> <ng-container matColumnDef="date"> <th mat-header-cell *matHeaderCellDef mat-sort-header>Date</th> <td mat-cell *matCellDef="let row">{{row.date | date:'short'}}</td> </ng-container> <!-- Name Column --> <ng-container matColumnDef="text"> <th mat-header-cell *matHeaderCellDef mat-sort-header>Text</th> <td mat-cell *matCellDef="let row">{{row.text}}</td> </ng-container> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> </table> <mat-paginator #paginator [pageIndex]="0" [pageSize]="50" [pageSizeOptions]="[25, 50, 100, 250]"> </mat-paginator> </div>

Cambie view-memories.component.css a view-memories.component.scss y configure el estilo de la tabla:

 table{ width: 100%; }

En view-memories.component.ts , cambiaremos styleUrls para reflejar el cambio de nombre anterior a ./view-memories.component.scss . También actualizaremos la matriz de columnas displayedColumns para que sea ['date', 'text'] y configuraremos la fuente de datos de la tabla para obtener datos de Firebase.

Lo que sucede aquí es que nos suscribimos a la lista de memorias y, cuando recibimos los datos, creamos una instancia de ViewMemoriesDataSource y configuramos su propiedad de datos con los datos de Firebase.

 this.subscription = this.db.list<ViewMemoriesItem>('memories').valueChanges().subscribe(d=>{ console.log('data streaming'); this.dataSource = new ViewMemoriesDataSource(this.paginator, this.sort); this.dataSource.data = d; });

Firebase devuelve una matriz Observable de estilo ReactiveX.

Tenga en cuenta que estamos this.db.list<ViewMemoriesItem>('memories') , los valores extraídos de la ruta de 'memories' , a ViewMemoriesItem . De esto se ocupa la biblioteca angularfire2 .

También incluimos la llamada de cancelación de unsubscribe dentro del gancho onDestroy del ciclo de vida del componente Angular.

 import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core'; import { MatPaginator, MatSort } from '@angular/material'; import { ViewMemoriesDataSource, ViewMemoriesItem } from './view-memories-datasource'; import { AngularFireDatabase } from 'angularfire2/database'; import { Subscription } from 'rxjs'; import { map, first } from 'rxjs/operators'; @Component({ selector: 'app-view-memories', templateUrl: './view-memories.component.html', styleUrls: ['./view-memories.component.scss'] }) export class ViewMemoriesComponent implements OnInit, OnDestroy{ @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; dataSource: ViewMemoriesDataSource; /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ displayedColumns = ['date', 'text']; subscription: Subscription; constructor(private db: AngularFireDatabase) { } ngOnInit() { this.subscription = this.db.list<ViewMemoriesItem>('memories').valueChanges().subscribe(d=>{ console.log('data streaming'); this.dataSource = new ViewMemoriesDataSource(this.paginator, this.sort); this.dataSource.data = d; }); } ngOnDestroy(): void { this.subscription.unsubscribe(); } }
Relacionado: Creación de aplicaciones móviles multiplataforma en tiempo real: ejemplos con Ionic Framework y Firebase

Implementación en Firebase Hosting

Ahora, para que nuestra aplicación esté activa, implementémosla en Firebase Hosting. Para eso, instalaremos Firebase CLI, que hace que el comando firebase esté disponible:

 npm install -g firebase-tools

Ahora podemos usar Firebase CLI para iniciar sesión:

 firebase login

Esto le pedirá que seleccione su cuenta de Google.

A continuación, inicializaremos el proyecto y configuraremos Firebase Hosting:

 firebase init

Simplemente seleccionaremos la opción Hosting.

A continuación, cuando se nos solicite la ruta, la configuraremos en dist/my-memories . Cuando se nos pregunte si configurarlo como una aplicación de una sola página (es decir, reescribir todas las URL en /index.html ), responderemos "sí".

Finalmente, presionaremos, “El archivo dist/my-memories/index.html ya existe. ¿Sobrescribir?" Aquí diremos “no”.

Esto creará los archivos de configuración de Firebase .firebaserc y firebase.json con la configuración proporcionada.

El último paso es ejecutar:

 ng build --prod firebase deploy

Y con eso, habremos publicado la aplicación en Firebase, que proporciona una URL para que naveguemos, como https://my-memories-b4c52.firebaseapp.com/view-memories, donde puede ver mi propia publicación. manifestación.


¡Vaya, has pasado por el tutorial! Espero que lo hayan disfrutado. También puede consultar el código completo en GitHub.

Un paso a la vez

Angular es un marco muy poderoso para crear aplicaciones web. Ha existido durante mucho tiempo y ha demostrado su valía tanto para aplicaciones pequeñas y simples como para aplicaciones grandes y complejas: Angular 6 no es una excepción aquí.

En el futuro, Angular planea seguir mejorando y siguiendo nuevos paradigmas web, como los componentes web (Angular Elements). Si está interesado en crear aplicaciones híbridas, puede consultar Ionic, que utiliza Angular como marco subyacente.

Este tutorial cubrió pasos muy básicos para comenzar a usar Angular, Material y Firebase. Pero debe tener en cuenta que para las aplicaciones del mundo real, deberá agregar la validación y, para que su aplicación sea más fácil de mantener y escalar, probablemente desee seguir las mejores prácticas, como el uso de servicios, componentes reutilizables, etc. Ese tendrá que ser el tema de otro artículo, ¡con suerte, este fue suficiente para despertar su apetito por el desarrollo angular!

Relacionado: Todas las ventajas, sin complicaciones: un tutorial de Angular 9