参照Angular公式文件以理解Angular:①
首先
我对React有了一定的理解,但因为工作需要理解Angular的代码,所以决定学习一下。Angular是JavaScript三大框架之一,希望能够在学习的过程中感受到与React的区别。我会参考官方文档来进行学习。
基本事项 (jī shì
首先,要理解Angular的基本概念。目前我們只通過閱讀Angular的官方文件,來了解Angular是什麼,如果有任何誤解,請務必糾正。
-
- コンポーネント
-
- アプリの構成要素。@Component() デコレータをつけた TypeScript クラス、HMTL テンプレート、スタイルが含まれる。カプセル化でき、直感的なアプリ構造にできる。
-
- テンプレート
-
- コンポーネントに必要となる HTML テンプレート。コンポーネントから動的な値を受け取り、状態変化に合わせて自動更新できる。値だけでなく、HTML や CSS、イベントもコンポーネントから渡すことができる。ディレクティブという機能もあるらしいが、これは現状正確には理解できていない。フラグによって表示を変えられる ngIf など、多数のディレクティブがあらかじめ用意されている。
-
- 依存性の注入(DI; Dependency Injection)
- コンポーネントで使用したいサービスを注入できる。インスタンス化を明示的に行わなくてもサービスを扱えるため、簡単かつ柔軟にサービスをコンポーネント内で使用できる。
与 React 相比,组件的概述是相同的(尽管有函数组件和类组件的区别),但是在 React 中不存在指令或依赖注入这样的术语,所以对此的理解还很肤浅。(可能只是因为不知道而已,React 中也可能有。)
不仅仅通过阅读文档来理解,我们还将通过实际操作来深入理解。
Angular 教程
随着开始一个基本的Angular应用程序,我们将遵循一个修正和添加的方式,根据示例代码进行开发。我们还会进行与React的比较。
创建商品清单
1. *ngFor 指令
在下面的代码中,我们使用 *ngFor 指令来显示商品列表。
<h2>Products</h2>
<div *ngFor="let product of products">
<h3>
{{ product.name }}
</h3>
</div>
产品是由products.ts文件中定义的数组,在product-list.component.ts中被导入并传递到product-list.component.html模板中。在编写的过程中,*ngFor可能类似于在React中使用的map函数。(以下是React代码示例,未经过执行确认)
{products.map((product, index) => {
return (
<div key={index}>
<h3>{product.name}</h3>
</div>
)
})}
作为一个以 React 为基础的人,对于像
这样用””括起来的写法有点不太习惯。
2. 属性绑定
进一步在下面添加一个a标签,并使用属性绑定[]语法传递商品名作为title属性的值。据说可以将属性封装在[]中传递。确实,如果去除了[],将光标放在上面也看不到文字。
<h2>Products</h2>
<div *ngFor="let product of products">
<h3>
<a [title]="product.name + ' details'">
{{ product.name }}
</a>
</h3>
</div>
条件判断指令 *ngIf
紧接着使用 *ngIf 指令。只有当 product 包含 description 时,才会显示 p 标签。
<h2>Products</h2>
<div *ngFor="let product of products">
<h3>
<a [title]="product.name + ' details'">
{{ product.name }}
</a>
</h3>
<p *ngIf="product.description">
Description: {{ product.description }}
</p>
</div>
这可能类似于 React 中的 && 和三元运算符。(以下是 React 代码示例,尚未验证运行)
{product.description && <p>Description: {product.description}</p>}
4. 事件绑定
最后,向按钮中添加 click 事件并将其绑定到 product-list.component.ts 的 share() 方法上。在事件绑定中,需要在事件周围使用 ( )。很容易混淆事件绑定的 ( ) 和属性绑定的 [ ]。
<h2>Products</h2>
<div *ngFor="let product of products">
<h3>
<a [title]="product.name + ' details'">
{{ product.name }}
</a>
</h3>
<p *ngIf="product.description">
Description: {{ product.description }}
</p>
<button (click)="share()">
Share
</button>
</div>
目前,我覺得使用React的JSX(TSX)更容易。Angular明確地將HTML文件和TS文件分開,因此在小規模開發中,來回切換文件可能會感到繁瑣,但隨著規模的擴大,每個文件的角色變得明確,可讀性提高且修改也更容易。
将数据传递给子组件
1. 创建子组件
在这里,我们创建了一个作为子组件的 ProductAlertsComponent,并从父组件 ProductListComponent 中接收数据。在组件创建时,代码如下所示。(由于本文章中不需要 constructor 和 ngOnInit,因此以下部分将被省略。)
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-product-alerts',
templateUrl: './product-alerts.component.html',
styleUrls: ['./product-alerts.component.css']
})
export class ProductAlertsComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
@Component() 装饰器用于标识定义的类为组件,并指定组件的选择器、模板、样式等相关元数据。
输入装饰器
为了从父组件接收商品数据,首先需要从 @angular/core 导入 Input ,然后在名为 product 的属性上使用 @Input() 装饰器进行定义。在这里,使用 ! 表示 product 是非空的。
import { Component, OnInit } from '@angular/core';
import { Input } from '@angular/core';
import { Product } from '../products';
@Component({
selector: 'app-product-alerts',
templateUrl: './product-alerts.component.html',
styleUrls: ['./product-alerts.component.css']
})
export class ProductAlertsComponent {
@Input() product!: Product;
}
通过上述方法,可以从父组件ProductListComponent接收属性的值。为了利用从父组件接收到的数据,我们可以在product-alerts.component.html中创建一个当价格超过$700时才会显示的Notify Me按钮。
<p *ngIf="product && product.price > 700">
<button>Notify Me</button>
</p>
3. 在模块中声明
只需一种选项:
在 product-list.component.html 中添加选择器 ,就可以应用了。然而,在预览中出现了以下错误。

虽然在官方文件中没有写明,但似乎需要在 app.module.ts 文件中导入和在 declarations 中声明才能应用新创建的组件。经过补充后,成功应用并显示了售价超过700美元的Phone XL的“通知我”按钮。

将数据传递给父组件
1. @输出装饰器
为了使子组件的“Notify Me”按钮起作用,子组件需要通知父组件被点击的事件,并且父组件需要对该事件作出响应。为此,在子组件ProductAlertsComponent的product-alerts.component.ts文件中,我们从@angular/core中导入Output和EventEmitter,并使用@Output()装饰器和EventEmitter()的实例来定义名为notify的属性。通过这个设定,当notify属性的值被改变时,我们可以触发事件。
import { Component, OnInit } from '@angular/core';
import { Input } from '@angular/core';
import { Output, EventEmitter } from '@angular/core';
import { Product } from '../products';
@Component({
selector: 'app-product-alerts',
templateUrl: './product-alerts.component.html',
styleUrls: ['./product-alerts.component.css']
})
export class ProductAlertsComponent {
@Input() product!: Product;
@Output() notify = new EventEmitter();
}
2. 在子组件中触发事件
通过使用事件绑定修复代码,以便在按下“Notify Me”按钮时触发事件。
<p *ngIf="product && product.price > 700">
<button (click)="notify.emit()">Notify Me</button>
</p>
EventEmitter 的实例 notify 通过 emit() 方法发出事件。这个方法可以触发事件并将数据传递给父组件。(在这个例子中,emit() 方法没有参数,只是触发了事件而没有传递数据。在本文的最后,作为附加内容,也尝试了传递数据的情况。)
3. 在父组件中对事件进行响应。
当子组件ProductAlertsComponent触发事件时,在product-list.component.ts文件中将父组件ProductListComopnent的操作定义为onNotify()方法。
export class ProductListComponent {
products = products;
share() {
window.alert('The product has been shared!');
}
onNotify() {
window.alert('You will be notified when the product goes on sale');
}
}
在product-list.component.html中,最后修改如下,从子组件接收数据。把product传递给子组件,并接收来自子组件的notify。
<h2>Products</h2>
<div *ngFor="let product of products">
<h3>
<a [title]="product.name + ' details'">
{{ product.name }}
</a>
</h3>
<p *ngIf="product.description">
Description: {{ product.description }}
</p>
<button (click)="share()">
Share
</button>
<app-product-alerts [product]="product" (notify)="onNotify()">
</app-product-alerts>
</div>
4. (附赠品)EventEmitter()
虽然官方文件到这里就结束了,但不仅仅可以通过EventEmitter()触发事件,还可以同时传输数据。为了将产品名称作为通知内容更改并通知,我对Notify Me按钮做了以下三个修正。
- notify.emit() にて product.name を転送
<p *ngIf="product && product.price > 700">
<button (click)="notify.emit(product.name)">Notify Me</button>
</p>
- onNitify() で name を引数にとり、アラートに表示
...
onNotify(name: string) {
window.alert(
'Your will be notified when ' + name + ' product goes on sale'
);
}
}
- onNotify() メソッドに対して $event を渡す
...
<app-product-alerts [product]="product" (notify)="onNotify($event)">
</app-product-alerts>
</div>
更改後的结果如下。点击Phone XL的”通知我”按钮,确认能收到商品名称的通知。

最后
我按照官方文档学习了Angular。与React相比,发现了许多不同之处,感到很有趣,同时也对其他功能很感兴趣,所以我打算继续学下去。在React中,我使用Hooks在所有函数组件中进行编写,所以我也想借此机会进一步深入理解关于类的概念(当然还有TypeScript)。