Angular 6 教程:具有新功能的新功能
已发表: 2022-03-11Angular 6 出来了! 最突出的变化在于它的 CLI 以及服务的注入方式。 如果您希望在本教程中编写您的第一个 Angular 6 应用程序(或 Angular/Firebase 应用程序),我们将介绍初始设置的基本步骤并创建一个小型日记应用程序。
Angular 6:基础知识
如果您以前从未使用过 Angular,让我简要介绍一下它以及它是如何工作的。
Angular 是一个 JavaScript 框架,旨在支持为桌面和移动设备构建单页应用程序 (SPA)。
该框架包括一整套指令和模块,可让您轻松实现 Web 应用程序的一些最常见场景,如导航、授权、表单和报告。 它还附带了所有必要的包,以使用 Jasmine 框架添加测试并使用 Karma 或 Protractor 测试运行器运行它们。
Angular 架构基于组件、模板、指令和服务。 它为你的服务提供了一个内置的依赖注入机制,以及双向数据绑定来连接你的视图和你的组件代码。
Angular 使用 TypeScript,一个 JS 的类型化超集,它会让一些事情变得更容易,特别是如果你来自类型化语言背景。
Angular 6:新功能
Angular 6 新特性的简要总结:
- 为框架包(
@angular/core
、@angular/common
、@angular/compiler
等)、CLI、Material 和 CDK 同步主要版本号的策略。 这将有助于使交叉兼容性更加清晰:您可以通过快速浏览版本号来判断关键包是否相互兼容。 - 新的
ng
CLI 命令:-
ng update
智能升级包版本,更新依赖版本并保持同步。 (例如,当运行ng update @angular/core
时,所有框架以及 RxJS 都会被更新。)如果包中包含原理图,它也会运行原理图。 (如果较新的版本包含需要更改代码的重大更改,则原理图将为您更新您的代码。) -
ng add
添加新包(并运行脚本,如果适用)
-
- 服务现在引用将提供它们的模块,而不是像过去那样引用服务的模块。
作为最后一次更改含义的示例,您的代码过去看起来像这样:
@NgModule({ // ... providers: [MyService] })
…随着 Angular 6 的这一特殊变化,它看起来像:
@Injectabe({ providedIn: 'root', })
这些被称为可摇树的提供程序,允许编译器删除未引用的服务,从而生成更小的包。
角 6 CLI
ng
命令行界面是 Angular 的一个非常重要的部分,它允许您在编写应用程序时更快地移动。
使用 CLI,您可以非常轻松地搭建初始应用程序设置、生成新组件、指令等,并在本地环境中构建和运行应用程序。
创建 Angular 6 项目
好了,说够了。 让我们动手并开始编码。
首先,您需要在机器上安装 Node.js 和 npm。
现在,让我们继续安装 CLI:
npm install -g @angular/cli
由于-g
开关,这将全局安装ng
CLI 命令。
一旦我们有了它,我们就可以使用ng new
为我们的应用程序获取初始脚手架:
ng new my-memories --style=scss
这将创建一个my-memories
文件夹,创建所有必要的文件以使您的初始设置准备好开始,并安装所有必要的软件包。 --style=scss
开关是可选的,它将设置编译器将 SCSS 文件编译为 CSS,我们稍后将需要它。
安装完成后,您可以cd my-memories
并运行ng serve
。 这将启动构建过程和在http://localhost:4200
为您的应用程序提供服务的本地 Web 服务器。
幕后发生的事情是 CLI 将所有.ts
(TypeScript 文件)转换为 vanilla JS,从包文件夹node_modules
收集所有必需的依赖项,并将结果输出到一组文件中,这些文件通过本地 Web 服务器提供服务在端口 4200 上运行。
项目文件
如果你不熟悉 Angular 的项目文件夹结构,你需要知道的最重要的事情是所有与应用程序相关的代码都在src
文件夹中。 您通常会按照您的应用架构(例如用户、购物车、产品)在该文件夹中创建所有模块和指令。
最初设定
好的,到目前为止,我们已经完成了应用程序的初始设置。 让我们开始做一些改变。
在开始之前,让我们深入了解一下src
文件夹。 初始页面是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>
在这里,我们看到了一些基本的 HTML 和<app-root>
标签。 这是一个 Angular 组件,Angular 6 在其中插入了我们的组件代码。
我们将找到app/app.component.ts
文件,该文件具有选择器app-root
以匹配index.html
文件中的内容。
该组件是一个修饰的 TypeScript 类,在这种情况下,包含title
属性。 @Component
装饰器告诉 Angular 在类中包含组件行为。 除了选择器之外,它还指定要呈现的 HTML 文件以及要使用的样式表。
import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent { title = 'app'; }
如果我们查看app.component.html
,我们会看到{{title}}
插值绑定。 这是所有魔术绑定发生的地方,Angular 将呈现类 title 属性的值,并在它发生变化时更新它。
<!--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>
让我们继续将课程的title
更新为'My Memories!'
.
... export class AppComponent { title = 'My Memories!'; } ...
我们将看到编译器处理我们的更改并且浏览器刷新以显示我们更新的标题。
这意味着 Angular 6 的ng serve
监视我们的文件更改,并在每次将更改引入任何文件时进行渲染。
为了使编码更加友好并避免每次进行更改时都刷新整个页面,我们可以利用 webpack 热模块替换(HMR),它只是更新被更改的 JS/CSS 块,而不是产生完整的刷新显示您的更改。
配置 HMR
首先,我们需要设置环境。
创建一个包含以下内容的文件src/environments/environment.hmr.ts
:
export const environment = { production: false, hmr: true };
更新src/environments/environment.prod.ts
并将hmr: false
标志添加到环境中:
export const environment = { production: true, hmr: false };
然后更新src/environments/environment.ts
并将hmr: false
标志添加到那里的环境中:
export const environment = { production: false, hmr: false };
接下来在angular.json
文件中,更新这部分:
"projects": { "my-memories": { // ... "architect": { "build": { // ... "configurations": { "hmr":{ "fileReplacements":[ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.hmr.ts" } ] }, // ...
在projects
→ my-memories
→ architect
→ serve
→ configurations
下:
"projects": { "my-memories": { "architect": { // ... "serve": { // ... "configurations": { "hmr": { "browserTarget": "my-memories:build:hmr" }, // ...
现在通过在compilerOptions
下添加以下内容来更新tsconfig.app.json
以包含必要的types
(好吧,类型):
"compilerOptions": { // ... "types": [ "node" ]
接下来,我们将安装@angularclass/hmr
模块作为开发依赖项:
npm install --save-dev @angularclass/hmr
然后通过创建文件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(); }); };
接下来,更新src/main.ts
以使用上述功能:
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)); }
我们在这里做的是让 bootstrap 调用一个匿名函数,然后询问environment.hmr
标志是否为真。 如果是,我们从hmr.ts
中调用之前定义的函数来启用热模块替换; 否则,我们像以前一样引导它。
现在,当我们运行ng serve --hmr --configuration=hmr
时,我们将调用hmr
配置,当我们对文件进行更改时,我们将获得更新而无需完全刷新。 第一个--hmr
用于 webpack,--configuration --configuration=hmr
用于 Angular 使用hmr
环境。
渐进式 Web 应用程序 (PWA)
为了添加 Angular 6 PWA 支持并启用应用程序的离线加载,我们可以使用新的 CLI 命令之一ng add
:
ng add @angular/[email protected]
请注意,我正在添加版本,因为我在编写本教程时的最新版本引发了错误。 (您可以尝试不使用它并使用简单的ng add @angular/pwa
检查它是否适合您。)
好的,所以在我们运行命令之后,我们会看到我们的项目发生了很多变化。 最重要的变化是它增加了:
- 对
angular.json
资产文件中manifest.json
的引用,以便它包含在构建输出中,以及"serviceWorker": true
在生产构建中 - 具有初始设置的
ngsw-config.json
文件,用于缓存应用程序运行所需的所有文件 index.html
文件中的manifest.json
元标记manifest.json
文件本身,包含应用程序的基本配置- 应用程序模块
ServiceWorkerModule.register('/ngsw-worker.js', { enabled: environment.production })
中的 service worker 负载(注意 service worker 只会在生产环境中启用)
所以,这意味着当用户第一次访问 URL 时,文件将被下载。 之后,如果用户尝试在没有网络服务的情况下访问 URL,应用程序仍然可以通过拉取这些缓存文件来工作。
添加 Material Angular 6 UI 库
到目前为止,我们已经完成了初始设置,并且准备开始构建我们的应用程序。 要利用已经构建的组件,我们可以使用 Angular 6 版本的 Material。
为了在我们的应用程序上安装material
包,我们将再次使用ng add
:
ng add @angular/material
运行之后,我们会看到添加了一些新的包和一些基本的样式配置:
-
index.html
包括 Roboto 字体和 Material 图标 BrowserAnimationsModule
被添加到我们的AppModule
-
angular.json
已经为我们包含了靛蓝粉色主题
您需要重新启动ng serve
以获取主题,或者您可以选择另一个预建主题。
基本布局
为了获得初始的 sidenav 布局,我们将使用 Material 附带的示意图。 但是如果你想使用不同的布局也没关系。
(简而言之,原理图可让您将转换应用于项目:您可以根据需要创建、修改或删除文件。在这种情况下,它为我们的应用程序搭建了一个侧导航布局。)
ng generate @angular/material:material-nav --name=my-nav
这将创建一个 sidenav 组件,并准备好启动最低设置。 那不是很棒吗?
它还在我们的app.module.ts
中包含了所有必要的模块。
由于我们使用的是 SCSS,我们需要将my-nav.component.css
文件重命名为my-nav.component.scss
,并在my-nav.component.ts
中更新相应的引用styleUrls
以使用新名称。
现在要使用新组件,让我们转到app.component.html
并删除所有初始代码,只留下:
<app-my-nav></app-my-nav>
当我们返回浏览器时,我们将看到以下内容:
让我们更新链接以仅包含我们想要的两个选项。
首先,让我们创建两个新组件:
ng gc AddMemory ng generate @angular/material:material-table --name=view-memories
(第二个是用于创建表格的材料示意图。)
接下来,在my-nav
上,我们将更新以设置链接并包含<router-outlet>
以显示我们的内容组件:
<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>
此外,在app.component.html
中,我们需要将其更新为仅具有主<router-outlet>
(即删除<my-nav>
):

<router-outlet></router-outlet>
接下来,在AppModule
中,我们将包含路由:
import { RouterModule, Routes } from '@angular/router'; // ... imports: [ // ... RouterModule.forRoot([ { path: '', component: MyNavComponent, children: [ { path: 'add-memory', component: AddMemoryComponent }, { path: 'view-memories', component: ViewMemoriesComponent } ] }, ]), ]
请注意,我们将MyNavComponent
设置为父级,并将我们创建的两个组件设置为子级。 这是因为我们在MyNavComponent
中包含了<router-outlet>
,并且每当我们点击这两个路由之一时,我们将渲染放置<router-outlet>
的子组件。
在此之后,当我们提供应用程序时,我们应该看到:
构建应用程序(回忆日记)
好的,现在让我们创建表单来将新的记忆保存到我们的日记中。
我们需要将一些材料模块和表单模块导入到我们的app.module.ts
:
import { FormsModule } from '@angular/forms'; import { MatCardModule, MatFormFieldModule, MatInputModule, MatDatepickerModule, MatNativeDateModule } from '@angular/material'; // ... Imports:[ // ... MatCardModule, MatFormFieldModule, MatInputModule, MatDatepickerModule, FormsModule, MatNativeDateModule, // ... ]
然后在add-memory.component.html
中,我们将添加表单:
<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>
在这里,我们使用mat-card
并添加两个字段,一个date
和一个textarea
。
请注意,我们正在使用[(ngModel)]
。 这个 Angular 指令将类中的memory.date
表达式和memory
属性相互绑定,我们稍后会看到。 ( [(ngModel)]
是语法糖——执行从类到视图以及从视图到类的双向数据绑定的快捷方式。这意味着当您在视图中输入文本时, memory.date
将反映这些更改在类实例中,如果您在类实例中对memory.date
进行更改,视图将反映更改。)
在add-memory.component.ts
中,代码将如下所示:
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); } }
在这里,我们正在初始化通过ngModel
绑定的memory
属性。 当AddMemoryComponent
组件被实例化时, memory
将是一个空对象。 然后当ngModel
指令运行时,它将能够将输入值分配给memory.date
和memory.text
。 如果我们不这样做,我们会收到Cannot set property 'date/text' of undefined
的错误。
同时, add-memory.component.scss
需要有:
.memory-card { min-width: 150px; max-width: 400px; width: 100%; margin: auto; } .mat-form-field { width: 100%; }
因为我们有<pre> {{ memory | json }} </pre>
<pre> {{ memory | json }} </pre>
我们可以在视图中看到memory
的当前状态。 如果我们转到浏览器,到目前为止的结果如下:
在视图中,我们通过(ngSubmit)="onSubmit()"
将表单绑定到类中的onSubmit
函数。
onSubmit() { console.log(this.memory); }
所以当你点击“救救我!” 按钮,您将获得发送到控制台日志的用户输入的内部表示:
Angular 6 教程:与 Firebase 连接
我们接下来要做的是将我们的项目连接到 Firebase 以保存我们的记忆。
首先,我们将转到 Firebase 控制台并在那里创建一个项目。
其次,我们将安装firebase
和angularfire2
包:
npm install firebase angularfire2 --save
然后在这三个文件中的每一个中:
-
/src/environments/environment.ts
-
/src/environments/environment.hmr.ts
-
/src/environments/environment.prod.ts
…我们将添加我们的 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>' } };
您可以通过单击项目概述页面上的“将 Firebase 添加到您的网络应用程序”来获取上述文件所需的配置详细信息。
之后,我们将在app.module.ts
中包含 Firebase 模块:
import { AngularFireModule } from 'angularfire2'; import { AngularFireDatabaseModule } from 'angularfire2/database'; import { environment } from '../environments/environment'; // ... Imports:[ // ... AngularFireModule.initializeApp(environment.firebase), AngularFireDatabaseModule, // ... ]
在add-memory.component.ts
中,我们在构造函数中注入数据库并将表单中的值保存到数据库中。 当 Firebase 的推送承诺成功时,我们在控制台中记录成功并重置模型:
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') }) }
您需要允许对数据库规则进行公共访问,以便匿名用户可以对其进行读取和写入。 请注意,通过此设置,任何用户都可以读取/更改/删除您的应用数据。 确保在投入生产之前相应地设置规则。
此外,要获取环境更改,您需要重新启动ng serve
进程。
现在,当我们返回浏览器并单击保存按钮时,我们将看到内存已添加到数据库中:
让我们看一下如何检索我们的记忆并将它们显示在 Material 表中。
当我们使用ng generate @angular/material:material-table --name=view-memories', we automatically got a file
view-memories/view-memories-datasource.ts`。 此文件包含虚假数据,因此我们需要对其进行更改以开始从 Firebase 中提取数据。
在view-memories-datasource.ts
中,我们将删除EXAMPLE_DATA
并设置一个空数组:
export class ViewMemoriesDataSource extends DataSource<ViewMemoriesItem> { data: ViewMemoriesItem[] = []; // ...
在getSortedData
我们将更新字段名称:
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; } }); }
在view-memories.component.html
中,我们将列名更新为来自内存模型的date
和text
。 请注意,由于我们以毫秒格式保存日期,因此我们使用日期管道将值转换为更人性化的日期格式显示。 最后,我们从分页器中删除[length]="dataSource.data.length"
,因为我们将从 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>
将view-memories.component.css
更改为view-memories.component.scss
并设置表格样式:
table{ width: 100%; }
在view-memories.component.ts
中,我们将更改styleUrls
以反映上述重命名为./view-memories.component.scss
。 我们还将displayedColumns
数组更新为['date', 'text']
并设置表数据源以从 Firebase 获取数据。
这里发生的事情是我们订阅了内存列表,当我们收到数据时,我们实例化ViewMemoriesDataSource
并使用来自 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 返回一个 ReactiveX 风格的 Observable 数组。
请注意,我们将this.db.list<ViewMemoriesItem>('memories')
从'memories'
路径中提取的值)转换为ViewMemoriesItem
。 这由angularfire2
库负责。
我们还将unsubscribe
调用包含在 Angular 组件生命周期的onDestroy
挂钩中。
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(); } }
部署到 Firebase 托管
现在,为了让我们的应用上线,让我们将其部署到 Firebase 托管。 为此,我们将安装 Firebase CLI,它使firebase
命令可用:
npm install -g firebase-tools
现在我们可以使用 Firebase CLI 登录:
firebase login
这将提示您选择您的 Google 帐户。
接下来,我们将初始化项目并配置 Firebase 托管:
firebase init
我们将只选择托管选项。
接下来,当我们被要求提供路径时,我们会将其设置为dist/my-memories
。 当我们被问到是否将其配置为单页应用程序时(即将所有 URL 重写为/index.html
),我们会回答“是”。
最后,我们会点击,“文件 dist/my-memories/index.html 已经存在。 覆盖?” 在这里,我们会说“不”。
这将使用提供的配置创建firebase.json
配置文件.firebaserc
和 firebase.json。
最后一步是运行:
ng build --prod firebase deploy
有了这个,我们将把应用程序发布到 Firebase,它提供了一个 URL 供我们导航,例如 https://my-memories-b4c52.firebaseapp.com/view-memories,您可以在其中查看我自己发布的演示。
哇,你已经完成了教程! 我希望你喜欢它。 你也可以在 GitHub 上查看它的完整代码。
一步一步来
Angular 是一个非常强大的构建 Web 应用程序的框架。 它已经存在了很长时间,并且已经证明了自己适用于小型、简单的应用程序和大型、复杂的应用程序——Angular 6 在这里也不例外。
展望未来,Angular 计划不断改进并遵循新的 Web 范例,例如 Web 组件(Angular Elements)。 如果您对构建混合应用程序感兴趣,可以查看使用 Angular 作为其底层框架的 Ionic。
本教程涵盖了开始使用 Angular、Material 和 Firebase 的非常基本的步骤。 但是您应该考虑到,对于现实世界的应用程序,您需要添加验证,并且为了使您的应用程序更易于维护和扩展,您可能希望遵循最佳实践,例如使用服务、可重用组件等. 这将是另一篇文章的主题——希望这篇文章足以激起你对 Angular 开发的兴趣!