第 3 天 「速速前吕布奉先!」|NgModule、HttpClientModule、新增元件

前情提要

在昨天我们建立了 Angular 专案、使用 JSON-server 来制作 mock db,并且建立了英雄资料。今天这篇文章我们将完成下列事项:

  • 使用 HttpClientModule 来取得英雄资料。
  • 新增 HeroListComponent 元件。

005

(现在,我们要试着召唤英灵...)

我知道!再怎麽说,我也是个读书人呢。

(哦?你知道什麽?)

要召唤英灵对吧,那就要念这个咒文——「速速前吕布奉先!」。

(呃...)

我知道问题出在哪了。速速前只能召唤物体,所以,我要召唤方天画戟,这样拿着方天画戟的吕布就会一起传过来——「速速前方天画戟!」。

(神啊,我是不是做错了什麽。)

糟糕,难道是因为我没有梦见妈祖吗......

(你最好什麽都不要念,好好看接下来的文章。)

引入 HttpClientModule

现在,我们在 mock db 里面存有英雄资料,透过 JSON-server 的协助,可以在 Angular 中来模拟使用 http 来呼叫 api 的行为。在 Angular 中,我们可以引入 HttpClientModule,并使用这个模组所拥有的提供商(provider)HttpClient, 来执行 http 请求。

在引入 HttpClientModule 模组之前,要先简单说明「模组」(module)概念。「模组」概念解决的问题是,将应用程序更有组织地进行管理,而透过 import、export,就可以很好地达到复用程序码的目的,在 JavaScript ES6 版本中,正式提出了模组系统 ,也就是「JS 模组」。

而在 Angular 中,模组化包含了两个部分 :

  • JS 模组:JavasScript ES6 提供了模组,这让我们可以很轻松地 import 、 export 程序码。
  • Angular 模组:带有 @NgModule 装饰器标记的类别,在装饰器里面会放置元资料的物件,你也需要在这个物件中说明「宣告」(declarations)、「汇入」(imports)、「汇出」(exports)...分别有哪些类别。

我们可以先用「佣兵团」概念来思考 @NgModule 装饰器记载的元资料:

  • 「宣告」(declarations):登记在这里的类别,都是本佣兵团的佣兵。
  • 「汇入」(imports):这里是有签订支援协定的佣兵团(module),你可以使用这些佣兵团有提供支援服务的佣兵。
  • 「汇出」(exports):本佣兵团的成员里,有提供支援服务的佣兵会登记在这里。

「佣兵团」概念或许可以说明在 NgModule 使用其他 NgModule 提供的支援服务,但这个概念也存在一个风险:那就是「支援协定并不是双向的,而是单方面寻求支援」。意思是,你不能在 A 模组汇入 B 模组的情形下,同时在 B 模组汇入 A 模组,这会造成循环汇入的错误。对这个风险有印象,在日後遇到时,你就会知道发生了什麽事情,应该从何着手处理。

现在让我们看看专案中必定存在的根模组 AppModule(app.module.ts):

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

上方的 import {xxx} from 'path'; 即为汇入 JS 模组的语法。而 @NgModule 里面的各种阵列,即为此模组的相关内容。先看「宣告」(declarations) 阵列,这里是放置宣告隶属这个 module 档案中的类别,现在这里拥有 AppComponent 元件,如下:

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'angular-tour-of-heroes';
}

可以看到,AppComponent 是一个带有 @Component (元件)装饰器的类别。你可以这麽想,带有 @Component(元件) 、 @Directive(指令) 、@Pipe(管道) 装饰器的类别,就是通过认证的佣兵。不同的装饰器,用来说明佣兵负责的不同功用。

并且,在这个世界中不存在个体户佣兵,所有想要从事佣兵工作的人,都必须加入佣兵团,也只能加入一个佣兵团。具体来说,AppComponent 只能登记在一个 NgModule 的「宣告」(declarations)中,无法再宣吿在其他 NgModule 下;如果没有宣告在任何 NgModule 中,就没有人可以使用 AppComponent。

此外,关於带有元件/指令/管道不同装饰器的类别,我们慢慢都会使用到。

现在,在根模组 AppModule 「汇入」(imports)阵列中加入 HttpClientModule【与其他佣兵团签订支援协议】,让我们可以使用 HttpClientModule 汇出(exports)的内容【使用其他佣兵团有提供支援服务的佣兵】。

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Angular 就是一个 JavsScript 框架,连 NgModule 装饰器,都是作为 JS 模组汇入到需要使用的地方的。

更详尽的说明,请参考官方文件

新增 HeroListComponent 元件

为了更好的演示,让我们用 Angular CLI 来建立一个 hero-list 元件。进入 src/app 目录,这里有个小技巧,在编辑器中,我们可以针对想要建立元件的资料夹点击滑鼠右键,就可以发现「在终端机中打开」(Open in Integrated Terminal),点击它,编辑器就会为我们打开终端机,并预设在此路径中。

https://ithelp.ithome.com.tw/upload/images/20210917/20128395SfEdkCQhDs.png

接着就可以执行下列指令:

ng g c hero-list  // g for generate, c for component

Anglaur CLI 就会建立数个档案,可能包含「.html」、「.ts」、「.css」、「.spec.ts」,并同时更新了 app.module.ts,让我们查看更新的档案:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';

import { AppComponent } from './app.component';
import { HeroListComponent } from './hero-list/hero-list.component';

@NgModule({
  declarations: [
    AppComponent,
    HeroListComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

可以发现,Angualr CLI 帮我们在这个模组中宣告了 HeroListComponent 类别,因此,我们就可以在这个模组中使用它——以这里来说,我们就可以在 AppComponent 中使用 HeroListComponent。

使用 HttpClient 取得英雄资料

因为在 app.module.ts 中汇入了 HttpClientModule 模组,我们就可以在宣告在 app.module.ts 的 HeroListComponent 里,使用 HttpClientModule 汇出的内容,包括 HttpClient。

打开 hero-list.component.ts 档案,在建构式(constructor)中注入 HttpClient:

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-hero-list',
  templateUrl: './hero-list.component.html',
  styleUrls: ['./hero-list.component.css']
})
export class HeroListComponent implements OnInit {

  constructor(
    private http: HttpClient
  ) { }

  ngOnInit(): void {
  }

}

然後我们使用它来取得英雄资料,在 ngOnInit 里撰写相关程序码:

  constructor(
    private http: HttpClient
  ) { }
  
  ngOnInit(): void {
    this.http.get('api/heros').subscribe((heroList) => {
      console.log('HeroList', heroList);
    });
  }

如果现在用浏览器打开此 App,console 是不会印出 HeroList 的,因为我们根本还没在 app 中使用 HeroListComponent 元件。

要怎麽使用元件呢?在 hero-list.component.ts@Component 装饰器里的元资料物件有个属性 selector: 'app-hedo-list',这个就是我们可以写在 *.html 档案中、代表使用此元件的语法,类似 HTML 的 tag。

让我们在根元件 app.component.html 中使用 HeroListComponent 元件:

<app-hero-list></app-hero-list>

如此我们就可以在 app 中看到如下画面,代表我们确实取得了英雄资料:

https://ithelp.ithome.com.tw/upload/images/20210918/20128395lBCxupQyzU.png

小提醒:当你新增/删除档案後,必须重启专案(ng serve)才会执行改变後的程序码。

参考资料

  • 《哈利波特——火盃的考验》,我只想说,写这篇的体验是「火焰的靠背」。
  • NgModule

<<:  Day 4 - [Zenbo开发系列] 01-後盖打开方式、侦错USB孔位置

>>:  【Day 03】又是 Print Spooler 搞的鬼 - CVE-2021-1675 PrintNightmare

[Day 30] 微探讨 Angular 的 Event binding 与 Property binding

铁人赛最後一天! 今天要跟各位分享的呢,是昨天与前天 Banana in a box 系列的延伸,关...

【Day09-填空】漏漏缺缺欠欠填填删删补补——面对缺失值的处理方式

昨天我们讲了在numpy中NaN要如何判断相等 那今天就来稍微谈一下会出现缺失值的那些情况要怎麽处理...

django新手村13-----路由规则

urls.py str有可以用int path('personal_info<str:name...

Day 12 ( 中级 ) 猫咪跑步 ( 超长背景 )

猫咪跑步 ( 超长背景 ) 教学原文参考:猫咪跑步 ( 超长背景 ) 这篇文章会介绍,如何在 Scr...

[Day6] Git版本控制 - 基本操作篇 (MacOS)

搞定 Mac 的软件和终端机,接下来要学习一步步把其他工具的使用环境建立起来,并学习如何去运用。 1...