Angular2組件間交互

? ? ? ?組件間交互簡單來說就是讓兩個或多個組件之間共享信息徊哑。接下來我們就對Angular2組件間的交互做一個簡單的解釋。當然做好的文檔還是官方文檔:https://www.angular.cn/guide/component-interaction

一议经、通過@Input把父組件的屬性綁定到子組件

? ? ? ?@Input注解是屬性綁定,通常在父組件需要向子組件傳遞數(shù)據(jù)的時候使用谴返。關(guān)于@Input你可以簡單的理解為子組件創(chuàng)建的時候需要傳遞參數(shù)(當然子組件的創(chuàng)建指的是在父組件對應的html里面申明)煞肾。

有@Input那肯定就會對應的有一個@Output,@Output是用于子組件向父組件傳遞數(shù)據(jù)的時候觸發(fā)事件嗓袱。關(guān)于@Output我們會在下面講到籍救。

? ? ? ?@Input的使用簡單的很,首先在子組件定義的時候我們先明確哪些屬性是需要父組件傳遞過來的渠抹,給加上@Input注解就完事了蝙昙。然后父組件通過模板語法把屬性綁定到子組件上去就完事了。

? ? ? ?我們用一個非常簡單的實例看下@Input的使用梧却,父組件需要把Hero對象傳遞到子組件里面去奇颠。

子組件hero屬性加上@Input()注解。

import {Component, Input} from '@angular/core';
import {Hero} from '../hero';

@Component({
  selector: 'app-data-child',
  template: `
    <p>我是子組件放航,父組件傳遞的值是:"{{hero.name}}"</p>
  `
})
export class DataChildComponent {

  // 該屬性需要從父組件傳遞過來
  @Input() hero: Hero;

  constructor() { }
}

父組件通過[hero]="parentHero"把parentHero屬性綁定到子組件上去

import {Component} from '@angular/core';
import {Hero} from '../hero';

@Component({
  selector: 'app-data-parent',
  styleUrls: ['./data-parent.component.css'],
  template: `
    <app-data-child [hero]="parentHero"></app-data-child>
    `
})
export class DataParentComponent {

  parentHero: Hero = new Hero();

  constructor() {
    this.parentHero.name = '我是父組件定義的';
  }
}

? ? ? ?@Input的使用就是這么的簡單的烈拒,除了[hero]="parentHero"單向綁定,我們也可以使用[(hero)]="parentHero"雙向綁定广鳍。

1.1缺菌、通過setter截聽輸入屬性值的變化

? ? ? ?使用輸入屬性的 setter、getter方法來攔截父組件中值的變化搜锰,一邊是在setter函數(shù)里面做一些相應的處理伴郁,然后getter函數(shù)里面返回。 我們還是繼續(xù)在上面的基礎上做一個簡單的修改蛋叼。對子組件做一個簡單的修改把父組件傳遞過來的Hero對象里面的名字都改成大寫焊傅。

import {Component, Input} from '@angular/core';
import {Hero} from '../hero';

@Component({
  selector: 'app-data-child',
  template: `
    <p>我是子組件,父組件傳遞的值是:"{{hero.name}}"</p>
  `
})
export class DataChildComponent {

  private _hero: Hero;

  // 該屬性需要從父組件傳遞過來狈涮,我們把Hero對象里面的name改成大寫
  @Input()
  set hero(hero: Hero) {
    // 把父組件傳遞過來的數(shù)據(jù)裝換成大寫
    const name = (hero.name && hero.name.toUpperCase()) || '<no name set>';
    this._hero = new Hero();
    this._hero.name = name;
  }

  get hero(): Hero {
    return this._hero;
  }

  constructor() {
  }
}

1.2狐胎、通過ngOnChanges()鉤子來攔截輸入屬性值的變化

? ? ? ?使用OnChanges生命周期鉤子接口的ngOnChanges() 方法來監(jiān)測輸入屬性值的變化并做出回應。當輸入屬性值變化的時候會回調(diào)ngOnChanges()方法歌馍。

ngOnChanges()鉤子:當Angular(重新)設置數(shù)據(jù)綁定輸入屬性時響應握巢。 該方法接受當前和上一屬性值的SimpleChanges對象
當被綁定的輸入屬性的值發(fā)生變化時調(diào)用,首次調(diào)用一定會發(fā)生在ngOnInit()之前松却。

? ? ? ?關(guān)于通過ngOnChanges()鉤子來攔截屬性值的變化暴浦。想強調(diào)的一點就是溅话。數(shù)據(jù)變化指的是屬性指向的地址發(fā)生改變才會回調(diào)ngOnChanges()函數(shù)。所以對于一些自定義的數(shù)據(jù)類型(class)要特別小心歌焦。

? ? ? ?我們還是用一個簡單的例子來說明怎么通過通過ngOnChanges()鉤子來攔截輸入屬性值的變化飞几。子組件需要父組件傳遞一個string屬性過來,通過ngOnChanges()鉤子攔截到屬性的變化独撇,把數(shù)據(jù)都改為大寫的屑墨。

子組件,把父組件傳遞過來的數(shù)據(jù)改為大寫

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

@Component({
  selector: 'app-data-child',
  template: `
    <p>我是子組件纷铣,父組件傳遞的值是:"{{inputString}}"</p>
  `
})
export class DataChildComponent implements OnChanges {

  // 該屬性需要從父組件傳遞過來
  @Input()
  inputString: string;

  // 攔截inputString的變化,并且把他變成大寫
  ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
    for (const propName in changes) {
      if (changes.hasOwnProperty(propName)) {
        const changedProp = changes[propName];
        const to = JSON.stringify(changedProp.currentValue);
        // 我們這里只想要inputString屬性的變化
        if (propName === 'inputString') {
          if (changedProp.isFirstChange()) {
            // 第一次數(shù)據(jù)設置
          } else {
            // 不是第一次
          }
          this.inputString = to.toUpperCase();
        }
      }
    }
  }
}

父組件相關(guān)代碼

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

@Component({
  selector: 'app-data-parent',
  styleUrls: ['./data-parent.component.css'],
  template: `
    <app-data-child [(inputString)]="inputString"></app-data-child>
    <button (click)="onValueChangeClick()">改變值</button>
    `
})
export class DataParentComponent {

  inputString: string;

  constructor() {
    this.inputString = 'nihao';
  }

  onValueChangeClick() {
    this.inputString = 'change';
  }
}

二卵史、通過@Output讓父組件監(jiān)聽子組件的事件

? ? ? ?通過@Output注解指明一個輸出屬性(EventEmitter類型)。通過在子組件里面暴露一個EventEmitter屬性搜立,當事件發(fā)生時程腹,子組件利用該屬性 emits(向上彈射)事件。父組件綁定到這個事件屬性儒拂,并在事件發(fā)生時作出回應寸潦。子組件的EventEmitter屬性是一個輸出屬性,所以需要帶有@Output裝飾器社痛。這一部分可以類比JAVA里面的接口的使用见转。相當于在父組件里面實現(xiàn)接口,在子組件里面調(diào)用接口蒜哀。

? ? ? ?通過@Output讓父組件監(jiān)聽子組件的事件的使用也非常簡單斩箫。我們分為三個步驟:

  1. 子組件里面明確我們要把什么事件拋出去給父組件(定義一個@Output()注解修飾的EventEmitter屬性),
  2. 拋出事件撵儿。子組件做了什么動作之后拋出事件乘客。調(diào)用EventEmitter屬性的emit()方法拋出事件。
  3. 父組件里面綁定事件處理器淀歇,當子組件有事件拋出來的時候會調(diào)用父組件的處理函數(shù)易核。

? ? ? ?我還是以一個非常簡單的例子來說明,我們在子組件里面定義一個button浪默,當button點擊的時候牡直。把事件拋給父組件。統(tǒng)計點擊的次數(shù)。

子組件相關(guān)代碼

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

@Component({
  selector: 'app-data-child',
  template: `
    <button (click)="vote(true)">點擊</button>
  `
})
export class DataChildComponent {

  // @Output定義一個準備回調(diào)父組件的事件EventEmitter也是可以傳遞參數(shù)的
  @Output() voted = new EventEmitter<boolean>();


  vote(agreed: boolean) {
    // 把事件往上拋出去,可以帶參數(shù)
    this.voted.emit(agreed);
  }
}

父組件相關(guān)代碼

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

@Component({
  selector: 'app-data-parent',
  styleUrls: ['./data-parent.component.css'],
  template: `
    <p>點擊 {{clickCount}} 次</p>
    <app-data-child (voted)="onVoted($event)"></app-data-child>
  `
})
export class DataParentComponent {

  clickCount = 0;

  /**
   * 子組件拋上來的事件
   */
  onVoted(agreed: boolean) {
    this.clickCount++;
  }
}

三怔球、父子組件通過本地變量互動

? ? ? ?在父組件模板里,新建一個本地變量來代表子組件(指向子組件)踢步。然后利用這個變量來讀取子組件的屬性和調(diào)用子組件的方法。

? ? ? ?一個本地變量互動的簡單實例咙崎,在父組件里面有兩個按鈕:一個開始按鈕桩盲、一個結(jié)束按鈕胳喷。調(diào)用子組件里面的開始和結(jié)束方法湃番。在下面代碼中chiild指向的就是子組件,然后通過chiild來調(diào)用子組件里面的方法厌蔽。

子組件相關(guān)代碼

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

@Component({
  selector: 'app-data-child',
  template: `<p>{{message}}</p>`,
  styleUrls: ['./data-child.component.css']
})
export class DataChildComponent {

  message = '初始值';

  onStart(): void {
    this.message = '父組件告訴開始了';
  }

  onEnd(): void {
    this.message = '父組件告訴結(jié)束了';
  }
}

父組件相關(guān)代碼

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

@Component({
  selector: 'app-data-parent',
  template: `
    <button (click)="chiild.onStart()">開始</button>
    <button (click)="chiild.onEnd()">結(jié)束</button>
    <app-data-child #chiild></app-data-child>
  `,
  styleUrls: ['./data-parent.component.css']
})
export class DataParentComponent {
}

父子組件通過本地變量互動缺點是牵辣,本地變量的作用范圍只是html(模板)文件里面摔癣。在ts文件里面沒辦法使用奴饮。并且只能是單向的,只能在父組件的模板里面調(diào)用子組件的屬性或者方法择浊。

四戴卜、父組件通過@ViewChild()調(diào)用子組件里面的屬性方法

? ? ? ?父子組件通過本地變量互動的缺點是變量只能在模板里面使用,沒辦法在ts文件代碼里面使用琢岩。@ViewChild()就是來解決這個辦法的投剥。
當父組件類需要訪問子組件屬性或者方法的時候,可以把子組件作為 ViewChild担孔,注入到父組件里面江锨。

? ? ? ?我們還是對上面的例子做一個簡單的修改,父組件告訴子組件開始和結(jié)束糕篇。

子組件代碼

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

@Component({
  selector: 'app-data-child',
  template: `<p>{{message}}</p>`,
  styleUrls: ['./data-child.component.css']
})
export class DataChildComponent {

  message = '初始值';

  onStart(): void {
    this.message = '父組件告訴開始了';
  }

  onEnd(): void {
    this.message = '父組件告訴結(jié)束了';
  }
}

父組件代碼

import {Component, ViewChild} from '@angular/core';
import {DataChildComponent} from './data-child.component';

@Component({
  selector: 'app-data-parent',
  template: `
    <button (click)="start()">開始</button>
    <button (click)="end()">結(jié)束</button>
    <app-data-child #chiild></app-data-child>
  `,
  styleUrls: ['./data-parent.component.css']
})
export class DataParentComponent {

  @ViewChild(DataChildComponent)
  private childComponent: DataChildComponent;

  start(): void {
    this.childComponent.onStart();
  }

  end(): void {
    this.childComponent.onEnd();
  }

}

? ? ? ?當在一個父組件里面有同一個子組件多個的時候啄育,又應該怎么處理呢。

import {Component, ViewChild} from '@angular/core';
import {DataChildComponent} from './data-child.component';

@Component({
  selector: 'app-data-parent',
  template: `
    <button (click)="start()">開始</button>
    <button (click)="end()">結(jié)束</button>
    <app-data-child #chiild1></app-data-child>
    <app-data-child #chiild2></app-data-child>
  `,
  styleUrls: ['./data-parent.component.css']
})
export class DataParentComponent {

  @ViewChild('chiild1')
  private childComponent1: DataChildComponent;
  @ViewChild('chiild2')
  private childComponent2: DataChildComponent;

  start(): void {
    this.childComponent1.onStart();
    this.childComponent2.onStart();
  }

  end(): void {
    this.childComponent1.onEnd();
    this.childComponent2.onEnd();
  }

}

五拌消、父子組件通過服務通訊

? ? ? ?父組件和它的子組件共享同一個服務(父組件和子組件使用的是同一個服務實例挑豌,說白了就是同一個對象)。父子組件間通過發(fā)布訂閱的消息機制來實現(xiàn)通訊墩崩,一個發(fā)布消息氓英,一個訂閱消息。說白了就是觀察值模式鹦筹。

? ? ? ?我們用一個父子組件的相互通信來做一個簡單的說明铝阐。MissionService就是中間服務,MissionService里面有兩個bservable屬性铐拐。用來發(fā)布訂閱消息的饰迹。在一個組件里面發(fā)布另一個組件里面訂閱。

service->MissionService

import {Injectable} from '@angular/core';
import {Subject} from 'rxjs';

@Injectable()
export class MissionService {

  // Subject可以看著是一個橋梁或者代理
  private childToParentSubject = new Subject<string>();
  private parentToChildSubject = new Subject<string>();

  // 定義觀察者(Observable變量在定義的時候都會在后面加上$)
  childToParentObservable$ = this.childToParentSubject.asObservable();
  parentToChildObservable$ = this.parentToChildSubject.asObservable();

  // 父組件給子組件發(fā)送消息余舶,這樣parentToChildObservable$就能收到消息
  parentSendMessageToChild(mission: string) {
    this.parentToChildSubject.next(mission);
  }

  // 子組件給父組件發(fā)送消息啊鸭,這樣childToParentObservable$就能收到消息
  childSendMessageToParent(astronaut: string) {
    this.childToParentSubject.next(astronaut);
  }
}

子組件對應代碼

import {Component, OnDestroy} from '@angular/core';
import {MissionService} from './mission.service';
import {Subscription} from 'rxjs';

@Component({
  selector: 'app-service-child',
  template: `
    <p>收到父組件的消息: {{message}}</p>
    <button (click)="sendMessage()">發(fā)送消息</button>
  `,
  styleUrls: ['./service-child.component.css']
})
export class ServiceChildComponent implements OnDestroy {
  message = '';
  subscription: Subscription;

  constructor(private missionService: MissionService) {
    // 訂閱消息
    this.subscription = missionService.parentToChildObservable$.subscribe(
      mission => {
        this.message = mission;
      });
  }

  // 發(fā)送消息
  sendMessage() {
    this.missionService.childSendMessageToParent('我是子組件給你發(fā)消息了哈');
  }

  ngOnDestroy() {
    // 組件銷毀的時候,subscription需要取消訂閱
    this.subscription.unsubscribe();
  }

}

父組件對應代碼

import {Component, OnDestroy} from '@angular/core';
import {MissionService} from './mission.service';
import {Subscription} from 'rxjs';

@Component({
  selector: 'app-service-parent',
  template: `
    <p>收到子組件的消息: {{message}}</p>
    <button (click)="sendMessage()">發(fā)送消息</button>
    <app-service-child></app-service-child>
  `,
  styleUrls: ['./service-parent.component.css'],
  providers: [MissionService]
})
export class ServiceParentComponent implements OnDestroy{

  message = '';
  subscription: Subscription;

  constructor(private missionService: MissionService) {
    // 訂閱消息匿值,當數(shù)據(jù)改變的時候赠制,會調(diào)用到改函數(shù)
    this.subscription = missionService.childToParentObservable$.subscribe(
      astronaut => {
        this.message = astronaut;
      });
  }

  sendMessage() {
    this.missionService.parentSendMessageToChild('我是父組件給你發(fā)消息了哈');
  }

  ngOnDestroy(): void {
    // 取消訂閱
    this.subscription.unsubscribe();
  }


}


? ? ? ?本文涉及到的所有例子下載地址:DEMO下載地址。demo里面的例子可能會稍微復雜一點。


? ? ? ?關(guān)于組件間的交互钟些,我們就講這么多烟号,貌似看起來也不是很復雜,咱也是個初學者(我是做android,以前也沒有接觸過前段方面的知識)政恍。這里我想在說一句最好的文檔還是官方文檔汪拥,強烈推薦大家看官方文檔 https://www.angular.cn/guide/component-interaction#parent-interacts-with-child-via-emlocal-variableem

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末篙耗,一起剝皮案震驚了整個濱河市迫筑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宗弯,老刑警劉巖脯燃,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蒙保,居然都是意外死亡辕棚,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門邓厕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逝嚎,“玉大人,你說我怎么就攤上這事详恼〔咕” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵单雾,是天一觀的道長赚哗。 經(jīng)常有香客問我,道長硅堆,這世上最難降的妖魔是什么屿储? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮渐逃,結(jié)果婚禮上够掠,老公的妹妹穿的比我還像新娘。我一直安慰自己茄菊,他們只是感情好疯潭,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著面殖,像睡著了一般竖哩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上脊僚,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天相叁,我揣著相機與錄音,去河邊找鬼。 笑死增淹,一個胖子當著我的面吹牛椿访,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播虑润,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼成玫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了拳喻?” 一聲冷哼從身側(cè)響起哭当,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎舞蔽,沒想到半個月后荣病,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體码撰,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡渗柿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了脖岛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朵栖。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖柴梆,靈堂內(nèi)的尸體忽然破棺而出陨溅,到底是詐尸還是另有隱情,我是刑警寧澤绍在,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布门扇,位于F島的核電站,受9級特大地震影響偿渡,放射性物質(zhì)發(fā)生泄漏臼寄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一溜宽、第九天 我趴在偏房一處隱蔽的房頂上張望吉拳。 院中可真熱鬧,春花似錦适揉、人聲如沸留攒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炼邀。三九已至,卻和暖如春剪侮,著一層夾襖步出監(jiān)牢的瞬間拭宁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留红淡,地道東北人不狮。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像在旱,于是被迫代替她去往敵國和親摇零。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理桶蝎,服務發(fā)現(xiàn)驻仅,斷路器,智...
    卡卡羅2017閱讀 134,651評論 18 139
  • 學習資料來自 Angular.cn 與 Angular.io登渣。 模板語法 在線例子 在 Angular 中噪服,組件扮...
    小鐳Ra閱讀 3,730評論 0 3
  • 卡夫卡的短篇,群中有個改寫作業(yè)胜茧,我便找出來讀一讀粘优。讀至開頭就發(fā)現(xiàn),我很多年前就已讀過呻顽。然而那時候氣盛雹顺,讀完只是覺得...
    蘋果與烤翅閱讀 194評論 0 2
  • 不同的對象引用嬉愧,本質(zhì)是體現(xiàn)對象的可達性和垃圾回收。String 不可變喉前,Immutable 類没酣,被聲明成為 fin...
    那有一只羊閱讀 440評論 0 0
  • 今天中午吃飽飯,我媽媽給我買的太陽能板機器人制作的玩具到了卵迂,我非常的高興裕便,因為我特別喜歡拼裝的東西。我和我...
    碎片幻影閱讀 778評論 0 0