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 ng
Befehlszeilenschnittstelle 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.
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.)
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 src
Ordner 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 projects
→ my-memories
→ architect
→ serve
→ configurations
:
"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 derangular.json
Assets-Datei, so dass es in der Build-Ausgabe enthalten ist, sowie"serviceWorker": true
in den Produktions-Builds - Die
ngsw-config.json
Datei 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 derindex.html
Datei - 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 unseremAppModule
-
angular.json
hinzugefügt, das indigo-rosa Thema bereits für uns enthalten
Sie müssen ng serve
neu starten, um das Thema zu übernehmen, oder Sie können ein anderes vorgefertigtes Theme wählen.
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.
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:
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:
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:
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:
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.
Zweitens installieren wir die firebase
und angularfire2
Pakete:
npm install firebase angularfire2 --save
Und dann fügen wir in jeder dieser drei Dateien:
/src/environments/environment.ts
/src/environments/environment.hmr.ts
/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') }) }
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 serve
Prozess 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:
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!
Leave a Reply