后端學 Angular 2 —— 組件間通信


1. 父組件向子組件傳遞信息

使用@Input

子組件的屬性用 @Input 進行修飾,在父組件的模板中綁定變量

例子:

import { Component, OnInit, Input } from '@angular/core';

@Component({
    selector: 'my-input',
    template: `
    <h1>I am your father!</h1>
    <p>{{content}}</p>
    <input-child [content]="content"></input-child>
    `
})

export class InputComponent implements OnInit {
    content: string = 'May the force be with you';
    ngOnInit() { }
}

@Component({
    selector: 'input-child',
    template: `
    <h1>The skywalker.</h1>
    <p>{{content}}</p>`
})
export class InputChildComponent {
    @Input() content: string = '';
}

效果:

image_1b4i7c7me1vdv194q6vr1l92bls9.png-34.4kB

使用 setter 攔截輸入的屬性

在子組件中做一些修改,添加兩個私有字段:revertContent_content,然后為 _content 添加 setter 和 getter

代碼

@Component({
    selector: 'input-child',
    template: `
    <h1>The skywalker.</h1>
    <p>{{content}}</p>
    <p>{{revertContent}}</p>`
})
export class InputChildComponent {
    revertContent: string;
    _content: string = '';
    @Input() set content(str: string) {
        this._content = str;
        this.revertContent = str.split(' ').reverse().join(' ');
    }

    get content() {
        return this._content;
    }
}

效果:

image_1b4i8b9mo1p8p1bl4pvc19f2btjm.png-38.4kB

使用 ngOnChanged 攔截輸入的屬性

首先要修改一下 import 內容:

import { Component, OnInit, Input, OnChanges, SimpleChange } from '@angular/core';

然后讓子組件實現 OnChanges 接口的 ngOnchanges 方法,修改后的子組件如下:

@Component({
    selector: 'input-child',
    template: `
    <h1>The skywalker.</h1>
    <p>{{content}}</p>
    <p>{{revertContent}}</p>`
})
export class InputChildComponent implements OnChanges {
    revertContent: string = 'default content';
    _content: string = 'default content';

    @Input() set content(str: string) {
        this._content = str;
    }

    get content() {
        return this._content;
    }

    ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
        if (changes['content'] !== undefined) {
            let value = <string>(changes['content'].currentValue);
            this.revertContent = value.split(' ').reverse().join(' ');
            this.revertContent = `revert "${value}" to "${this.revertContent}"`;
        }
    }
}

ngOnChanges 是一個生命周期鈎子,下圖指出了這個方法的調用時機。
此處輸入圖片的描述

效果:

image_1b4j84sk01j0d1fdbuctot41ifc11.png-20.5kB

2. 子組件向父組件傳遞消息

子組件使用 EventEmitter<TEvent>

子組件中聲明一個 EventEmitter<TEvent> 屬性,然后在父組件的模板中添加這個事件的監聽器

代碼:

import { Component, OnInit, Output, EventEmitter } from '@angular/core';

@Component({
    selector: 'event',
    template: `
    <h1>I'm your father.</h1>
    <p>recived damage: {{damage}}</p>
    <event-child (onAttacked)="onAttackedHandler($event)"></event-child>`
})

export class EventComponent implements OnInit {
    damage: number = 0;

    onAttackedHandler(damage: number) {
        this.damage += damage;
    }

    ngOnInit() { }
}

@Component({
    selector: 'event-child',
    template: `
    <h1>The SkyWalker.</h1>
    <button (click)="attack()">Attack!</button><span>Damage: {{damage}} !</span>
    `
})
export class EventChildComponent {
    private baseDamage: number = 100;
    damage: number = 0;
    @Output() onAttacked = new EventEmitter<number>();

    attack() {
        this.damage = Math.random() * this.baseDamage;
        this.onAttacked.emit(this.damage);
    }
}

event.gif-169.3kB

使用本地變量

在父組件模板中使用 #xxx 這樣的本地變量綁定子組件,然后就可以在模板中直接使用子組件的屬性作為數據源

@Component({
    selector: 'event',
    template: `
    <h1>I'm your father.</h1>
    <p>recived damage: {{damage}}</p>
    <p>last damage: {{child.damage}}</p>
    <event-child #child (onAttacked)="onAttackedHandler($event)"></event-child>`
})
export class EventComponent implements OnInit {
    damage: number = 0;

    onAttackedHandler(damage: number) {
        this.damage += damage;
    }

    ngOnInit() { }
}

local.gif-136.4kB

使用 @ViewChild

父組件中使用 @ViewChild 獲取子組件的引用,然后使用方法同上

import { Component, OnInit, Output, EventEmitter, ViewChild } from '@angular/core';

@Component({
    selector: 'event-child',
    template: `
    <h1>The SkyWalker.</h1>
    <button (click)="attack()">Attack!</button><span>Damage: {{damage}} !</span>
    `
})
export class EventChildComponent {
    private baseDamage: number = 100;
    damage: number = 0;
    @Output() onAttacked = new EventEmitter<number>();

    attack() {
        this.damage = Math.random() * this.baseDamage;
        this.onAttacked.emit(this.damage);
    }
}

// 注意,這里我交換了父子組件的位置,現在父組件的定義放在了下面

@Component({
    selector: 'event',
    template: `
    <h1>I'm your father.</h1>
    <p>recived damage: {{damage}}</p>
    <p>last damage: {{child.damage}}</p>
    <event-child  (onAttacked)="onAttackedHandler($event)"></event-child>`
})
export class EventComponent implements OnInit {
    damage: number = 0;

    @ViewChild(EventChildComponent) child: EventChildComponent;

    onAttackedHandler(damage: number) {
        this.damage += damage;
    }

    ngOnInit() { }
}

終極大招——組件間使用 service 通信

首先讓我們在文件的開頭定義一個 AttackService

import { Component, OnInit, Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Rx';


@Injectable()
export class AttackService {
    // 用來產生數據流的數據源
    private damageSource = new Subject<number>();
    // 把數據流轉換成 Observable
    damage$ = this.damageSource.asObservable();

    attack(damage: number) {
        // 把傷害輸入到數據流
        this.damageSource.next(damage);
    }
}

然后在父組件與子組件中訂閱這個數據流:

@Component({
    selector: 'event-child',
    template: `
    <h1>The SkyWalker.</h1>
    <button (click)="attack()">Attack!</button><span>Damage: {{damage}} !</span>
    `
})
export class EventChildComponent {
    private baseDamage: number = 100;
    damage: number = 0;

    constructor(private attackService: AttackService) {
    }

    attack() {
        this.damage = Math.random() * this.baseDamage;
        // 天行者調用 AttackService 產生傷害
        this.attackService.attack(this.damage);
    }
}

// 注意,這里我交換了父子組件的位置,現在父組件的定義放在了下面

@Component({
    selector: 'event',
    providers: [AttackService], // 向父組件注入 AttackService,這樣,父組件與子組件就能共享一個單例的 service
    template: `
    <h1>I'm your father.</h1>
    <p>recived damage: {{damage}}</p>
    <p>last damage: {{lastDamage}}</p>
    <event-child></event-child>`
})
export class EventComponent implements OnInit {
    damage: number = 0;
    lastDamage: number = 0;

    constructor(private attackService: AttackService) {
        // 父組件訂閱來自天行者的傷害
        this.attackService.damage$.subscribe(damage => {
            this.lastDamage = damage;
            this.damage += damage;
        }, error => {
            console.log('error: ' + error);
        });
    }

    ngOnInit() { }
}

效果:

local.gif-136.4kB

如果我們把 service 注冊到根模塊,那么,就可以在整個 app 中共享數據啦~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM