Angular 在父子组件、指令之间共享数据

2022-06-29 10:38 更新

在父子指令及组件之间共享数据

Angular 中的一个常见模式就是在父组件和一个或多个子组件之间共享数据。可以用 ​@Input()​ 和 ​@Output()​ 来实现这个模式。

本章包含代码片段的可工作实例参阅现场演练 / 下载范例

考虑以下层次结构:

<parent-component>
  <child-component></child-component>
</parent-component>

<parent-component>​ 充当了 ​<child-component>​ 的上下文。

@Input()​ 和 ​@Output()​ 为子组件提供了一种与其父组件通信的方法。 ​@Input()​ 允许父组件更新子组件中的数据。相反,​@Output()​ 允许子组件向父组件发送数据。

把数据发送到子组件

子组件或指令中的 ​@Input()​ 装饰器表示该属性可以从其父组件中获取值。

input

要使用 ​@Input()​,就必须对父组件和子组件进行配置。

配置子组件

要使用 ​@Input()​ 装饰器,首先要导入 ​Input​,然后用 ​@Input()​ 装饰该属性,如下例所示。

import { Component, Input } from '@angular/core'; // First, import Input
export class ItemDetailComponent {
  @Input() item = ''; // decorate the property with @Input()
}

在这个例子中, ​@Input()​ 会修饰属性 ​item​,它的类型为 ​string​,但 ​@Input()​ 属性可以是任意类型,比如 ​number​、​string​、​boolean ​或 ​object​。​item ​的值来自父组件。

接下来,在子组件模板中添加以下内容:

<p>
  Today's item: {{item}}
</p>

配置父组件

下一步是在父组件的模板中绑定该属性。在这个例子中,父组件模板是 ​app.component.html​ 。

  1. 使用子组件的 selector (​<app-item-detail>​) 作为父组件模板中的指令。
  2. 使用属性绑定把子组件的 ​item ​属性绑定到父组件的 ​currentItem ​属性上。
  3. <app-item-detail [item]="currentItem"></app-item-detail>
  4. 在父组件类中,为 ​currentItem ​指定一个值:
  5. export class AppComponent {
      currentItem = 'Television';
    }

通过 ​@Input()​,Angular 把 ​currentItem ​的值传给子组件,以便 ​item ​渲染为 ​Television ​。

下图展示了这种结构:

input-diagram-target-source

方括号 ​[]​ 中的目标就是子组件中用 ​@Input()​ 装饰的那个属性。绑定源(等号的右边部分)则是父组件传给内嵌组件的数据。

监视 @Input() 的变更

要想监视 ​@Input()​ 属性的变化,可以用 Angular 的生命周期钩子​OnChanges ​。

把数据发送到父组件

子组件或指令中的 ​@Output()​ 装饰器允许数据从子组件传给父组件。

output

@Output()​ 在子组件中标记了一个属性,作为数据从子组件传递到父组件的途径。

子组件使用 ​@Output()​ 属性来引发事件,以通知父组件这一变化。为了引发事件, ​@Output()​ 必须是 ​EventEmitter ​类型,它是 ​@angular/core​ 中用来发出自定义事件的类。

下面的例子给出了如何在组件中设置 ​@Output()​,来把数据从 HTML 的 ​<input>​ 推送到父组件的数组中。

要使用 ​@Output()​ ,就必须配置父组件和子组件。

配置子组件

下面的例子中有一个 ​<input>​ ,用户可以输入一个值,然后点击一个引发事件 ​<button>​ 然后, ​EventEmitter ​数据中继到父组件。

  1. 在子组件类中导入 ​Output ​和 ​EventEmitter
  2. import { Output, EventEmitter } from '@angular/core';
  3. 在组件类中,用 ​@Output()​ 装饰一个属性。下面的例子中 ​newItemEvent ​这个 ​@Output()​ 的类型为 ​EventEmitter ​,这意味着它是一个事件。
  4. @Output() newItemEvent = new EventEmitter<string>();

    上述声明中的差异点如下:

    • @Output()​ - 一个装饰器函数,它把该属性标记为数据从子组件进入父组件的一种途径
    • newItemEvent ​- 这个 ​@Output()​ 的名字
    • EventEmitter<string>​ - 这个 ​@Output()​ 的类型
    • new EventEmitter<string>()​ - 使用 Angular 来创建一个新的事件发射器,它发出的数据是 ​string ​类型的。
  5. 在同一个组件类中创建一个 ​addNewItem()​ 方法:
  6. export class ItemOutputComponent {
    
      @Output() newItemEvent = new EventEmitter<string>();
    
      addNewItem(value: string) {
        this.newItemEvent.emit(value);
      }
    }

    addNewItem()​ 函数使用 ​newItemEvent ​这个 ​@Output()​ 来引发一个事件,该事件带有用户输入到 ​<input>​ 中的值。

配置子组件的模板

子组件的模板有两个控件。第一个是带有模板引用变量 ​#newItem​ 的 ​<input>​,用户可在其中输入条目名称。 ​#newItem​ 变量的 ​value ​属性存储了用户输入到 ​<input>​ 中的值。

<label for="item-input">Add an item:</label>
<input type="text" id="item-input" #newItem>
<button (click)="addNewItem(newItem.value)">Add to parent's list</button>

第二个元素是带有 ​click ​事件绑定 的 ​<button>​ 元素。

(click) ​事件绑定到了子组件类中 ​addNewItem()​ 方法。​addNewItem()​ 方法接受一个 ​#newItem.value​ 属性的值作为参数。

配置父组件

此范例中的 ​AppComponent ​有一个 ​items ​列表,以及一个向数组中添加更多条目的方法。

export class AppComponent {
  items = ['item1', 'item2', 'item3', 'item4'];

  addItem(newItem: string) {
    this.items.push(newItem);
  }
}

addItem()​ 方法接受一个字符串形式的参数,然后把该字符串添加到 ​items ​数组中。

配置父组件的模板

  1. 在父模板中,把父组件的方法绑定到子组件的事件上。
  2. 把子组件选择器(​<app-item-output>​)放在父组件的模板 ​app.component.html​ 中。
  3. <app-item-output (newItemEvent)="addItem($event)"></app-item-output>

    事件绑定 ​(newItemEvent)='addItem($event)' ​会把子组件中的 ​newItemEvent ​事件连接到父组件的 ​addItem()​ 方法。

    $event​ 中包含用户在子组件模板上的 ​<input>​ 中键入的数据。

    要了解 ​@Output()​ 的工作方式,你可以把以下内容添加到父组件的模板中:

    <ul>
      <li *ngFor="let item of items">{{item}}</li>
    </ul>

    *ngFor​ 会迭代 ​items ​数组中的条目。当你在子组件的 ​<input>​ 中输入一个值并单击该按钮时,子组件就会发出该事件,而父组件的 ​addItem()​ 方法会把这个值追加到其 ​items ​数组中,并且列表中会渲染出这个新条目。

同时使用 @Input() 和 @Output()

可以在同一个子组件上使用 ​@Input()​ 和 ​@Output()​,范例如下:

<app-input-output
  [item]="currentItem"
  (deleteRequest)="crossOffItem($event)">
</app-input-output>

目标 ​item ​是子组件类中的一个 ​@Input()​ 属性,它会从父组件的 ​currentItem ​属性中获取它的值。当你单击“删除”时,子组件就会引发一个事件 ​deleteRequest ​,它会作为父组件中 ​crossOffItem()​ 方法的参数。

下图展示了子组件 ​<app-input-output>​ 中 ​@Input()​ 和 ​@Output()​ 的各个部分。

input-output-diagram

这里的子选择器是 ​<app-input-output>​,它所带的 ​item ​和 ​deleteRequest ​是子组件类中的 ​@Input()​ 和 ​@Output()​ 属性。而 ​currentItem ​属性和 ​crossOffItem()​ 方法都位于父组件类中。


以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号