Angular 6 Tutorial: Neue Features mit neuer Power

Angular 6 ist da! Die wichtigsten Änderungen betreffen die CLI und die Art und Weise, wie Dienste injiziert werden. Wenn Sie Ihre allererste Angular 6-App – oder Angular/Firebase-App – schreiben wollen, gehen wir in diesem Tutorial die grundlegenden Schritte der Ersteinrichtung durch und erstellen eine kleine Tagebuch-App.

Angular 6: Die Grundlagen

Wenn Sie Angular noch nie benutzt haben, möchte ich Ihnen eine kurze Beschreibung geben, was es ist und wie es funktioniert.

Angular ist ein JavaScript-Framework, das für die Erstellung von Single-Page-Applikationen (SPAs) sowohl für den Desktop als auch für mobile Endgeräte entwickelt wurde.

Das Framework umfasst eine ganze Reihe von Direktiven und Modulen, mit denen Sie einige der gängigsten Szenarien für eine Webanwendung, wie Navigation, Autorisierung, Formulare und Berichte, einfach implementieren können. Es enthält auch alle notwendigen Pakete, um Tests mit dem Jasmine-Framework hinzuzufügen und sie mit den Testläufern Karma oder Protractor auszuführen.

Die Architektur von Angular basiert auf Komponenten, Vorlagen, Direktiven und Services. Es bietet einen eingebauten Dependency-Injection-Mechanismus für Ihre Dienste sowie Zwei-Wege-Datenbindung, um Ihre Ansichten mit Ihrem Komponentencode zu verbinden.

Angular verwendet TypeScript, eine typisierte Obermenge von JS, und wird einige Dinge einfacher machen, besonders wenn Sie von einem typisierten Sprachhintergrund kommen.

Angular 6: Neue Funktionen

Eine kurze Zusammenfassung der neuen Funktionen in Angular 6:

  • Eine Richtlinie zur Synchronisierung der Hauptversionsnummern für die Framework-Pakete (@angular/core, @angular/common, @angular/compiler, etc.), CLI, Material und CDK. Dies wird dazu beitragen, dass die Kompatibilität in Zukunft deutlicher wird: Man kann mit einem Blick auf die Versionsnummer erkennen, ob Schlüsselpakete miteinander kompatibel sind.
  • Neue ng CLI-Befehle:
    • ng update um Paketversionen intelligent zu aktualisieren, wobei die Versionen der Abhängigkeiten aktualisiert und synchronisiert werden. (Wenn z.B. ng update @angular/core ausgeführt wird, werden alle Frameworks sowie RxJS aktualisiert.) Es werden auch Schaltpläne ausgeführt, wenn das Paket sie enthält. (Wenn eine neuere Version bahnbrechende Änderungen enthält, die Änderungen im Code erfordern, wird das Schema Ihren Code für Sie aktualisieren.)
    • ng add um neue Pakete hinzuzufügen (und Skripte auszuführen, falls zutreffend)
  • Dienste verweisen jetzt auf die Module, die sie bereitstellen, anstatt dass Module auf Dienste verweisen, wie es früher der Fall war.

Ein Beispiel dafür, was diese letzte Änderung bedeutet: Wo Ihr Code früher wie folgt aussah:

@NgModule({ // ... providers: })

… wird er mit dieser speziellen Änderung in Angular 6 wie folgt aussehen:

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

Diese werden als „tree-shakeable providers“ bezeichnet und ermöglichen es dem Compiler, nicht referenzierte Dienste zu entfernen, was zu kleineren Paketen führt.

Angular 6 CLI

Die ngBefehlszeilenschnittstelle ist ein sehr wichtiger Bestandteil von Angular und ermöglicht es Ihnen, bei der Programmierung Ihrer App schneller voranzukommen.

Mit der CLI können Sie Ihr anfängliches App-Setup sehr einfach einrichten, neue Komponenten, Direktiven usw. generieren und Ihre App in Ihrer lokalen Umgebung bauen und ausführen.

Erstellen eines Angular 6 Projekts

Okay, genug geredet. Machen wir uns die Hände schmutzig und fangen an zu programmieren.

Zu Beginn müssen Node.js und npm auf deinem Rechner installiert sein.

Lassen Sie uns nun fortfahren und das CLI installieren:

npm install -g @angular/cli

Dadurch wird der ng CLI-Befehl global installiert, dank des -g-Schalters.

Sobald wir das haben, können wir mit ng new das anfängliche Gerüst für unsere Anwendung erstellen:

ng new my-memories --style=scss

Damit wird ein my-memories-Ordner erstellt, alle notwendigen Dateien erstellt, um das anfängliche Setup startklar zu machen, und alle notwendigen Pakete installiert. Der --style=scss-Schalter ist optional und wird den Compiler so einrichten, dass er SCSS-Dateien in CSS kompiliert, was wir später brauchen werden.

Nachdem die Installation abgeschlossen ist, können Sie cd my-memories und ng serve ausführen. Dadurch wird der Build-Prozess und ein lokaler Webserver gestartet, der Ihre Anwendung unter http://localhost:4200 bedient.

Eine Angular 6 App direkt nach dem Scaffolding

Was hinter den Kulissen passiert, ist, dass das CLI alle .ts (TypeScript-Dateien) in Vanilla JS transpiliert, alle erforderlichen Abhängigkeiten aus dem Packages-Ordner node_modules sammelt und das Ergebnis in einem Satz von Dateien ausgibt, die über einen lokalen Webserver, der auf Port 4200 läuft, bereitgestellt werden.

Projektdateien

Wenn Sie mit der Projektordnerstruktur von Angular nicht vertraut sind, ist das Wichtigste, was Sie wissen müssen, dass sich der gesamte Code für die App im Ordner src befindet. Normalerweise werden Sie alle Ihre Module und Direktiven in diesem Ordner erstellen und dabei Ihrer App-Architektur folgen (z.B. Benutzer, Warenkorb, Produkt.)

Die Angular 6 Projektordnerstruktur

Initiales Setup

Okay, soweit haben wir das initiale Setup für unsere App. Fangen wir an, ein paar Änderungen vorzunehmen.

Bevor wir anfangen, wollen wir ein wenig im srcOrdner stöbern. Die Ausgangsseite ist 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>

Hier sehen wir etwas grundlegendes HTML und den <app-root> Tag. Dies ist eine Angular-Komponente, und hier fügt Angular 6 unseren Komponentencode ein.

Wir finden die app/app.component.ts-Datei, die den Selektor app-root hat, um mit dem übereinzustimmen, was in der index.html-Datei steht.

Die Komponente ist eine dekorierte TypeScript-Klasse und enthält in diesem Fall die title-Eigenschaft. Der @Component-Dekorator weist Angular an, das Verhalten der Komponente in die Klasse aufzunehmen. Zusätzlich zum Selektor wird angegeben, welche HTML-Datei gerendert und welche Stylesheets verwendet werden sollen.

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

Wenn wir uns app.component.html ansehen, sehen wir die {{title}} Interpolationsbindung. Hier passiert die ganze magische Bindung, und Angular rendert den Wert der Klasseneigenschaft title und aktualisiert ihn jedes Mal, wenn er sich ändert.

<!--The content below is only a placeholder and can be replaced.--><div style="text-align:center"> <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>

Lassen Sie uns fortfahren und die title von der Klasse auf 'My Memories!' aktualisieren.

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

Wir werden sehen, wie der Compiler unsere Änderung verarbeitet und der Browser aktualisiert wird, um unseren aktualisierten Titel anzuzeigen.

Das bedeutet, dass Angular 6’s ng serve auf unsere Datei-Änderungen achtet und jedes Mal rendert, wenn eine Änderung in eine beliebige Datei eingeführt wird.

Um die Kodierung freundlicher zu gestalten und einen vollständigen Seitenrefresh bei jeder Änderung zu vermeiden, können wir die Vorteile von webpack Hot Module Replacement (HMR) nutzen, das nur den Teil von JS/CSS aktualisiert, der geändert wurde, anstatt einen vollständigen Refresh zu erzeugen, um die Änderungen anzuzeigen.

HMR konfigurieren

Zuerst müssen wir die Umgebung einrichten.

Erstellen Sie eine Datei src/environments/environment.hmr.ts mit folgendem Inhalt:

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

Aktualisieren Sie src/environments/environment.prod.ts und fügen Sie das Flag hmr: false zur Umgebung hinzu:

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

Aktualisieren Sie dann src/environments/environment.ts und fügen Sie auch dort das Flag hmr: false zur Umgebung hinzu:

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

Als nächstes aktualisieren Sie in der Datei angular.json diesen Teil:

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

Und unter projectsmy-memoriesarchitectserveconfigurations:

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

Nun aktualisieren Sie tsconfig.app.json, um das notwendige types (nun, Typ) einzuschließen, indem Sie dies unter compilerOptions hinzufügen:

 "compilerOptions": { // ... "types": 

Als nächstes installieren wir das Modul @angularclass/hmr als Entwicklungsabhängigkeit:

npm install --save-dev @angularclass/hmr

Dann konfigurieren wir es, indem wir eine Datei src/hmr.ts erstellen:

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

Als Nächstes aktualisieren Sie src/main.ts, um die obige Funktion zu verwenden:

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) { 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));}

Was wir hier tun, ist, bootstrap eine anonyme Funktion aufrufen zu lassen, und dann zu fragen, ob das environment.hmr-Flag wahr ist. Wenn dies der Fall ist, rufen wir die zuvor definierte Funktion aus hmr.ts auf, die die Ersetzung von Modulen im laufenden Betrieb ermöglichte; andernfalls booten wir sie wie bisher.

Wenn wir nun ng serve --hmr --configuration=hmr ausführen, rufen wir die hmr-Konfiguration auf, und wenn wir Änderungen an Dateien vornehmen, erhalten wir Aktualisierungen ohne eine vollständige Aktualisierung. Das erste --hmr ist für Webpack, und --configuration=hmr ist für Angular, um die hmr-Umgebung zu verwenden.

Progressive Web App (PWA)

Um Angular 6 PWA-Unterstützung hinzuzufügen und das Offline-Laden für die App zu aktivieren, können wir einen der neuen CLI-Befehle verwenden: ng add:

ng add @angular/

Beachte, dass ich die Version hinzufüge, da die letzte Version, als ich dieses Tutorial schrieb, einen Fehler auslöste. (Sie können es auch ohne versuchen und prüfen, ob es bei Ihnen funktioniert, indem Sie einfach ng add @angular/pwa verwenden.)

Okay, nachdem wir den Befehl ausgeführt haben, werden wir eine Menge Änderungen an unserem Projekt sehen. Die wichtigsten Änderungen sind, dass es hinzugefügt wurde:

  • Ein Verweis auf manifest.json in der angular.jsonAssets-Datei, so dass es in der Build-Ausgabe enthalten ist, sowie "serviceWorker": true in den Produktions-Builds
  • Die ngsw-config.jsonDatei mit der anfänglichen Einrichtung zum Zwischenspeichern aller Dateien, die für die Ausführung der App erforderlich sind
  • Ein manifest.json-Meta-Tag in der index.htmlDatei
  • Die manifest.json-Datei selbst, mit einer Grundkonfiguration für die App
  • Der Service Worker im App-Modul ServiceWorkerModule.register('/ngsw-worker.js', { enabled: environment.production }) (beachten Sie, dass der Service Worker nur in Produktionsumgebungen aktiviert wird)

Das bedeutet also, dass die Dateien heruntergeladen werden, wenn der Benutzer das erste Mal auf die URL zugreift. Wenn der Benutzer danach versucht, auf die URL ohne Netzwerkdienst zuzugreifen, funktioniert die App immer noch, indem sie diese zwischengespeicherten Dateien abruft.

Hinzufügen der Material Angular 6 UI-Bibliothek

So weit haben wir die anfängliche Einrichtung, und wir sind bereit, mit der Erstellung unserer App zu beginnen. Um die bereits erstellten Komponenten zu nutzen, können wir die Angular 6 Version von Material verwenden.

Um das material-Paket auf unserer App zu installieren, verwenden wir wieder ng add:

ng add @angular/material

Nachdem wir das ausgeführt haben, sehen wir einige neue Pakete hinzugefügt und einige grundlegende Stilkonfigurationen:

  • index.html enthält die Roboto-Schriftart und Material-Symbole
  • BrowserAnimationsModule wird zu unserem AppModule
  • angular.json hinzugefügt, das indigo-rosa Thema bereits für uns enthalten

Die Wahl eines vorgefertigten Angular 6-Themas

Sie müssen ng serve neu starten, um das Thema zu übernehmen, oder Sie können ein anderes vorgefertigtes Theme wählen.

Verwandt: Hochmoderne Web-Apps mit Angular Material erstellen

Grundlayout

Für das anfängliche Sidenav-Layout verwenden wir die Schemata, die mit Material geliefert werden. Es ist jedoch kein Problem, wenn Sie ein anderes Layout verwenden möchten.

(Kurz gesagt: Mit Schemata können Sie Transformationen auf ein Projekt anwenden: Sie können Dateien nach Bedarf erstellen, ändern oder löschen. In diesem Fall wird ein Sidenav-Layout für unsere App erstellt.)

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

Damit wird eine Sidenav-Komponente mit dem minimalen Setup erstellt, die sofort einsatzbereit ist. Ist das nicht toll?

Es hat auch alle notwendigen Module in unserem app.module.ts enthalten.

Eine neu erstellte "my-nav" Angular 6 Komponente

Da wir SCSS verwenden, müssen wir die Datei my-nav.component.css in my-nav.component.scss umbenennen und in my-nav.component.ts die entsprechende Referenz styleUrls aktualisieren, um den neuen Namen zu verwenden.

Um nun die neue Komponente zu nutzen, gehen wir zu app.component.html und entfernen den gesamten ursprünglichen Code, so dass nur noch übrig bleibt:

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

Wenn wir zum Browser zurückkehren, sehen wir folgendes:

Ein Layout mit vier Feldern, mit Menü in der oberen linken Ecke, meinen Erinnerungen daneben und drei nummerierten Links unter Menü; das vierte Feld ist leer

Lassen Sie uns die Links aktualisieren, damit sie nur die zwei gewünschten Optionen enthalten.

Zunächst erstellen wir zwei neue Komponenten:

ng g c AddMemoryng generate @angular/material:material-table --name=view-memories

(Die zweite ist ein Materialschema, das zum Erstellen einer Tabelle verwendet wird.)

Als nächstes aktualisieren wir my-nav, um die Links einzurichten und die <router-outlet> einzuschließen, um unsere Inhaltskomponenten anzuzeigen:

<mat-sidenav-container class="sidenav-container"> <mat-sidenav #drawer class="sidenav" fixedInViewport="true" ="(isHandset$ | async) ? 'dialog' : 'navigation'" ="(isHandset$ | async) ? 'over' : 'side'" ="!(isHandset$ | async)"> <mat-toolbar color="primary">Menu</mat-toolbar> <mat-nav-list> <a mat-list-item ="">Add Memory</a> <a mat-list-item ="">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>

Auch in app.component.html müssen wir es aktualisieren, um nur die Haupt-<router-outlet> zu haben (d.h., Entfernen Sie <my-nav>):

<router-outlet></router-outlet>

Als Nächstes fügen wir in AppModule die Routen ein:

import { RouterModule, Routes } from '@angular/router';// ...imports: }, ]),]

Beachten Sie, dass wir MyNavComponent als übergeordnete Komponente und die beiden von uns erstellten Komponenten als untergeordnete Komponenten festlegen. Das liegt daran, dass wir <router-outlet> in MyNavComponent eingefügt haben, und jedes Mal, wenn wir auf eine dieser beiden Routen zugreifen, wird die untergeordnete Komponente gerendert, in der <router-outlet> platziert wurde.

Wenn wir nun die App ausliefern, sollten wir Folgendes sehen:

Die Links auf der linken Seite wurden durch

Build the App (A Memories Diary)

Okay, jetzt erstellen wir das Formular zum Speichern neuer Erinnerungen in unserem Tagebuch.

Wir müssen einige Materialmodule und das Formularmodul in unser app.module.ts importieren:

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

Und dann fügen wir in add-memory.component.html das Formular hinzu:

<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..." ="memory.date" name="date" ="date"> <mat-datepicker-toggle matSuffix ="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 ="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>

Hier verwenden wir ein mat-card und fügen zwei Felder hinzu, ein date und ein textarea.

Beachte, dass wir verwenden. Diese Angular-Direktive wird den memory.date-Ausdruck und die memory-Eigenschaft in der Klasse aneinander binden, wie wir später sehen werden. ( ist ein syntaktischer Zucker – eine Abkürzung, um eine bidirektionale Datenbindung von der Klasse zur Ansicht und von der Ansicht zur Klasse durchzuführen. Das bedeutet, wenn Sie Text in der Ansicht eingeben, wird memory.date diese Änderungen in der Klasseninstanz widerspiegeln, und wenn Sie Änderungen an memory.date in der Klasseninstanz vornehmen, wird die Ansicht die Änderungen widerspiegeln.)

In der add-memory.component.ts sieht der Code wie folgt aus:

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

Hier initialisieren wir die über ngModel gebundene Eigenschaft memory. Wenn die Komponente AddMemoryComponent instanziiert wird, wird memory ein leeres Objekt sein. Wenn dann die Richtlinie ngModel ausgeführt wird, kann sie den Eingabewert memory.date und memory.text zuweisen. Wenn wir das nicht tun würden, bekämen wir einen Fehler von Cannot set property 'date/text' of undefined.

In der Zwischenzeit muss add-memory.component.scss haben:

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

Da wir <pre> {{ memory | json }} </pre> haben, können wir den aktuellen Zustand von memory in der Ansicht sehen. Wenn wir in den Browser gehen, sehen wir das bisherige Ergebnis:

Der Prototyp der Tagebuch-App "my-memories", der die interne Darstellung der Benutzereingabe (Datum und Text) zeigt:

In der Ansicht haben wir das Formular über (ngSubmit)="onSubmit()" an die onSubmit-Funktion in der Klasse gebunden.

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

Wenn Sie also auf die Schaltfläche „Save me!“ klicken, wird die interne Darstellung der Benutzereingabe an das Konsolenprotokoll gesendet:

Die interne Darstellung der Benutzereingabe im Konsolenprotokoll.

Angular 6 Tutorial: Verbinden mit Firebase

Als Nächstes verbinden wir unser Projekt mit Firebase, um unsere Erinnerungen zu speichern.

Zuerst gehen wir zur Firebase-Konsole und erstellen dort ein Projekt.

Ein Firebase-Projekt hinzufügen.

Zweitens installieren wir die firebase und angularfire2 Pakete:

npm install firebase angularfire2 --save

Und dann fügen wir in jeder dieser drei Dateien:

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

…unsere Firebase-Konfiguration hinzu:

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

Die erforderlichen Konfigurationsdetails für die oben genannten Dateien erhalten Sie, wenn Sie auf der Projektübersichtsseite auf „Add Firebase to your web app“ klicken.

Danach binden wir die Firebase-Module in unser app.module.ts ein:

import { AngularFireModule } from 'angularfire2';import { AngularFireDatabaseModule } from 'angularfire2/database';import { environment } from '../environments/environment';// ...Imports:

Und in add-memory.component.ts injizieren wir die Datenbank in den Konstruktor und speichern die Werte aus dem Formular in der Datenbank. Wenn das Push-Versprechen von Firebase erfolgreich ist, protokollieren wir den Erfolg in der Konsole und setzen das Modell zurück:

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') }) }

Lese- und Schreibzugriff auf Ihre Firebase-Datenbank zulassen.

Sie müssen den öffentlichen Zugriff auf die Datenbankregeln zulassen, damit anonyme Benutzer darin lesen und schreiben können. Bitte beachten Sie, dass bei dieser Einstellung jeder Benutzer in der Lage wäre, Ihre Anwendungsdaten zu lesen, zu ändern oder zu löschen. Stellen Sie sicher, dass Sie Ihre Regeln entsprechend einrichten, bevor Sie in die Produktion gehen.

Um die Umgebungsänderungen zu übernehmen, müssen Sie außerdem den ng serveProzess neu starten.

Wenn wir jetzt zum Browser zurückkehren und auf die Schaltfläche „Speichern“ klicken, sehen wir, dass der Speicher zur Datenbank hinzugefügt wurde:

Unser Testspeicher wurde der Firebase-Datenbank unserer Tagebuch-App hinzugefügt.

Lassen Sie uns einen Blick darauf werfen, wie wir unsere Erinnerungen abrufen und in der Materialtabelle anzeigen können.

Zurück haben wir die Tabelle mit ng generate @angular/material:material-table --name=view-memories', we automatically got a file view-memories/view-memories-datasource.ts` erstellt. Diese Datei enthält gefälschte Daten, also müssen wir sie ändern, um mit dem Abruf von Firebase zu beginnen.

In view-memories-datasource.ts entfernen wir das EXAMPLE_DATA und setzen ein leeres Array:

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

Und in getSortedData aktualisieren wir die Feldnamen:

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

In view-memories.component.html aktualisieren wir die Namen der Spalten auf date und text aus unserem Speichermodell. Da wir das Datum im Millisekundenformat gespeichert haben, verwenden wir hier eine Datums-Pipe, um den Wert für die Anzeige in ein menschenfreundlicheres Datumsformat umzuwandeln. Schließlich entfernen wir das ="dataSource.data.length" aus dem Paginator, da wir die Daten asynchron aus Firebase laden:

<div class="mat-elevation-z8"> <table mat-table #table ="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 ="0" ="50" =""> </mat-paginator></div>

Ändern Sie das view-memories.component.css in view-memories.component.scss und legen Sie den Tabellenstil fest:

table{ width: 100%;}

In view-memories.component.ts ändern wir das styleUrls, um die obige Umbenennung in ./view-memories.component.scss zu reflektieren. Wir aktualisieren auch das Array displayedColumns zu und richten die Tabellendatenquelle ein, um Daten von Firebase zu erhalten.

Was hier passiert, ist, dass wir die Erinnerungsliste abonnieren, und wenn wir die Daten erhalten, instanziieren wir die ViewMemoriesDataSource und setzen ihre Dateneigenschaft mit den Daten von 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 gibt ein Observable-Array im ReactiveX-Stil zurück.

Beachten Sie, dass wir this.db.list<ViewMemoriesItem>('memories')die Werte, die aus dem 'memories'-Pfad gezogen werden, in ViewMemoriesItem umwandeln. Dafür sorgt die angularfire2-Bibliothek.

Wir haben den unsubscribe-Aufruf auch in den onDestroy-Hook des Angular-Komponenten-Lebenszyklus integriert.

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

Deploying to Firebase Hosting

Um unsere App live zu schalten, müssen wir sie nun auf Firebase Hosting deployen. Dazu installieren wir die Firebase CLI, die den Befehl firebase zur Verfügung stellt:

npm install -g firebase-tools

Jetzt können wir die Firebase CLI verwenden, um uns anzumelden:

firebase login

Dann werden Sie aufgefordert, Ihr Google-Konto auszuwählen.

Als Nächstes initialisieren wir das Projekt und konfigurieren das Firebase-Hosting:

firebase init

Wir wählen einfach die Option „Hosting“ aus.

Als Nächstes, wenn wir nach dem Pfad gefragt werden, setzen wir ihn auf dist/my-memories. Wenn wir gefragt werden, ob wir es als Single-Page-App konfigurieren wollen (d.h. alle URLs auf /index.html umschreiben), antworten wir mit „Ja“.

Schließlich wählen wir „Datei dist/my-memories/index.html existiert bereits. Overwrite?“ Hier sagen wir „Nein“

Damit werden die Firebase-Konfigurationsdateien .firebaserc und firebase.json mit der angegebenen Konfiguration erstellt.

Der letzte Schritt ist die Ausführung:

ng build --prodfirebase deploy

Und damit haben wir die App auf Firebase veröffentlicht, die uns eine URL zur Verfügung stellt, zu der wir navigieren können, wie https://my-memories-b4c52.firebaseapp.com/view-memories, wo Sie meine eigene veröffentlichte Demo sehen können.

Wow, Sie haben das Tutorial durchlaufen! Ich hoffe, es hat dir gefallen. Du kannst dir auch den vollständigen Code auf GitHub ansehen.

Ein Schritt nach dem anderen

Angular ist ein sehr leistungsfähiges Framework zum Erstellen von Webanwendungen. Es ist schon lange auf dem Markt und hat sich sowohl für kleine, einfache Apps als auch für große, komplexe Anwendungen bewährt – Angular 6 bildet hier keine Ausnahme.

In Zukunft plant Angular, sich weiter zu verbessern und neuen Webparadigmen wie Webkomponenten (Angular Elements) zu folgen. Wenn Sie daran interessiert sind, hybride Apps zu erstellen, können Sie sich Ionic ansehen, das Angular als zugrunde liegendes Framework verwendet.

Dieses Tutorial behandelt sehr grundlegende Schritte, um mit Angular, Material und Firebase zu beginnen. Sie sollten jedoch bedenken, dass Sie für reale Anwendungen eine Validierung hinzufügen müssen, und um Ihre Anwendung einfacher zu warten und zu skalieren, sollten Sie wahrscheinlich Best Practices wie die Verwendung von Services, wiederverwendbaren Komponenten usw. befolgen. Das wird Thema eines anderen Artikels sein – hoffentlich hat dieser Artikel Ihren Appetit auf die Angular-Entwicklung geweckt!

Verwandte Themen: All Perks, No Hassle: Ein Angular 9 Tutorial

Leave a Reply