日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
創(chuàng)新互聯(lián)Angular教程:Angular 組件交互

組件之間的交互

本文包含了常見(jiàn)的組件通訊場(chǎng)景,也就是讓兩個(gè)或多個(gè)組件之間共享信息的方法。

我們提供的服務(wù)有:網(wǎng)站設(shè)計(jì)制作、網(wǎng)站設(shè)計(jì)、微信公眾號(hào)開(kāi)發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、南通ssl等。為1000+企事業(yè)單位解決了網(wǎng)站和推廣的問(wèn)題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的南通網(wǎng)站制作公司

參閱現(xiàn)場(chǎng)演練 / 下載范例。

通過(guò)輸入型綁定把數(shù)據(jù)從父組件傳到子組件。

?HeroChildComponent ?有兩個(gè)輸入型屬性,它們通常帶?@Input? 裝飾器。

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

import { Hero } from './hero';

@Component({
  selector: 'app-hero-child',
  template: `
    

{{hero.name}} says:

I, {{hero.name}}, am at your service, {{masterName}}.

` }) export class HeroChildComponent { @Input() hero!: Hero; @Input('master') masterName = ''; }

第二個(gè) ?@Input? 為子組件的屬性名 ?masterName ?指定一個(gè)別名 ?master?(譯者注:不推薦為起別名,請(qǐng)參閱風(fēng)格指南).

父組件 ?HeroParentComponent ?把子組件的 ?HeroChildComponent ?放到 ?*ngFor? 循環(huán)器中,把自己的 ?master ?字符串屬性綁定到子組件的 ?master ?別名上,并把每個(gè)循環(huán)的 ?hero ?實(shí)例綁定到子組件的 ?hero ?屬性。

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

import { HEROES } from './hero';

@Component({
  selector: 'app-hero-parent',
  template: `
    

{{master}} controls {{heroes.length}} heroes

` }) export class HeroParentComponent { heroes = HEROES; master = 'Master'; }

運(yùn)行應(yīng)用程序會(huì)顯示三個(gè)英雄:

測(cè)試一下!

端到端測(cè)試,用于確保所有的子組件都如預(yù)期般初始化并顯示出來(lái):

// ...
const heroNames = ['Dr IQ', 'Magneta', 'Bombasto'];
const masterName = 'Master';

it('should pass properties to children properly', async () => {
  const parent = element(by.tagName('app-hero-parent'));
  const heroes = parent.all(by.tagName('app-hero-child'));

  for (let i = 0; i < heroNames.length; i++) {
    const childTitle = await heroes.get(i).element(by.tagName('h3')).getText();
    const childDetail = await heroes.get(i).element(by.tagName('p')).getText();
    expect(childTitle).toEqual(heroNames[i] + ' says:');
    expect(childDetail).toContain(masterName);
  }
});
// ...

通過(guò) setter 截聽(tīng)輸入屬性值的變化

使用一個(gè)輸入屬性的 setter,以攔截父組件中值的變化,并采取行動(dòng)。

子組件 ?NameChildComponent ?的輸入屬性 ?name ?上的這個(gè) setter,會(huì) trim 掉名字里的空格,并把空值替換成默認(rèn)字符串。

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

@Component({
  selector: 'app-name-child',
  template: '

"{{name}}"

' }) export class NameChildComponent { @Input() get name(): string { return this._name; } set name(name: string) { this._name = (name && name.trim()) || ''; } private _name = ''; }

下面的 ?NameParentComponent ?展示了各種名字的處理方式,包括一個(gè)全是空格的名字。

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

@Component({
  selector: 'app-name-parent',
  template: `
    

Master controls {{names.length}} names

` }) export class NameParentComponent { // Displays 'Dr IQ', '', 'Bombasto' names = ['Dr IQ', ' ', ' Bombasto ']; }

測(cè)試一下!

端到端測(cè)試:輸入屬性的 setter,分別使用空名字和非空名字。

// ...
it('should display trimmed, non-empty names', async () => {
  const nonEmptyNameIndex = 0;
  const nonEmptyName = '"Dr IQ"';
  const parent = element(by.tagName('app-name-parent'));
  const hero = parent.all(by.tagName('app-name-child')).get(nonEmptyNameIndex);

  const displayName = await hero.element(by.tagName('h3')).getText();
  expect(displayName).toEqual(nonEmptyName);
});

it('should replace empty name with default name', async () => {
  const emptyNameIndex = 1;
  const defaultName = '""';
  const parent = element(by.tagName('app-name-parent'));
  const hero = parent.all(by.tagName('app-name-child')).get(emptyNameIndex);

  const displayName = await hero.element(by.tagName('h3')).getText();
  expect(displayName).toEqual(defaultName);
});
// ...

通過(guò)ngOnChanges()來(lái)截聽(tīng)輸入屬性值的變化

使用 ?OnChanges ?生命周期鉤子接口的 ?ngOnChanges()? 方法來(lái)監(jiān)測(cè)輸入屬性值的變化并做出回應(yīng)。

當(dāng)需要監(jiān)視多個(gè)、交互式輸入屬性的時(shí)候,本方法比用屬性的 ?setter ?更合適。

這個(gè) ?VersionChildComponent ?會(huì)監(jiān)測(cè)輸入屬性 ?major ?和 ?minor ?的變化,并把這些變化編寫成日志以報(bào)告這些變化。

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

@Component({
  selector: 'app-version-child',
  template: `
    

Version {{major}}.{{minor}}

Change log:

  • {{change}}
` }) export class VersionChildComponent implements OnChanges { @Input() major = 0; @Input() minor = 0; changeLog: string[] = []; ngOnChanges(changes: SimpleChanges) { const log: string[] = []; for (const propName in changes) { const changedProp = changes[propName]; const to = JSON.stringify(changedProp.currentValue); if (changedProp.isFirstChange()) { log.push(`Initial value of ${propName} set to ${to}`); } else { const from = JSON.stringify(changedProp.previousValue); log.push(`${propName} changed from ${from} to ${to}`); } } this.changeLog.push(log.join(', ')); } }

?VersionParentComponent ?提供 ?minor ?和 ?major ?值,把修改它們值的方法綁定到按鈕上。

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

@Component({
  selector: 'app-version-parent',
  template: `
    

Source code version

` }) export class VersionParentComponent { major = 1; minor = 23; newMinor() { this.minor++; } newMajor() { this.major++; this.minor = 0; } }

下面是點(diǎn)擊按鈕的結(jié)果。

測(cè)試一下!

測(cè)試確保這兩個(gè)輸入屬性值都被初始化了,當(dāng)點(diǎn)擊按鈕后,?ngOnChanges ?應(yīng)該被調(diào)用,屬性的值也符合預(yù)期。

// ...
// Test must all execute in this exact order
it('should set expected initial values', async () => {
  const actual = await getActual();

  const initialLabel = 'Version 1.23';
  const initialLog = 'Initial value of major set to 1, Initial value of minor set to 23';

  expect(actual.label).toBe(initialLabel);
  expect(actual.count).toBe(1);
  expect(await actual.logs.get(0).getText()).toBe(initialLog);
});

it("should set expected values after clicking 'Minor' twice", async () => {
  const repoTag = element(by.tagName('app-version-parent'));
  const newMinorButton = repoTag.all(by.tagName('button')).get(0);

  await newMinorButton.click();
  await newMinorButton.click();

  const actual = await getActual();

  const labelAfter2Minor = 'Version 1.25';
  const logAfter2Minor = 'minor changed from 24 to 25';

  expect(actual.label).toBe(labelAfter2Minor);
  expect(actual.count).toBe(3);
  expect(await actual.logs.get(2).getText()).toBe(logAfter2Minor);
});

it("should set expected values after clicking 'Major' once", async () => {
  const repoTag = element(by.tagName('app-version-parent'));
  const newMajorButton = repoTag.all(by.tagName('button')).get(1);

  await newMajorButton.click();
  const actual = await getActual();

  const labelAfterMajor = 'Version 2.0';
  const logAfterMajor = 'major changed from 1 to 2, minor changed from 23 to 0';

  expect(actual.label).toBe(labelAfterMajor);
  expect(actual.count).toBe(2);
  expect(await actual.logs.get(1).getText()).toBe(logAfterMajor);
});

async function getActual() {
  const versionTag = element(by.tagName('app-version-child'));
  const label = await versionTag.element(by.tagName('h3')).getText();
  const ul = versionTag.element((by.tagName('ul')));
  const logs = ul.all(by.tagName('li'));

  return {
    label,
    logs,
    count: await logs.count(),
  };
}
// ...

父組件監(jiān)聽(tīng)子組件的事件

子組件暴露一個(gè) ?EventEmitter ?屬性,當(dāng)事件發(fā)生時(shí),子組件利用該屬性 ?emits?(向上彈射)事件。父組件綁定到這個(gè)事件屬性,并在事件發(fā)生時(shí)作出回應(yīng)。

子組件的 ?EventEmitter ?屬性是一個(gè)輸出屬性,通常帶有?@Output? 裝飾器,就像在 ?VoterComponent ?中看到的。

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

@Component({
  selector: 'app-voter',
  template: `
    

{{name}}

` }) export class VoterComponent { @Input() name = ''; @Output() voted = new EventEmitter(); didVote = false; vote(agreed: boolean) { this.voted.emit(agreed); this.didVote = true; } }

點(diǎn)擊按鈕會(huì)觸發(fā) ?true ?或 ?false?(布爾型有效載荷)的事件。

父組件 ?VoteTakerComponent ?綁定了一個(gè)事件處理器(?onVoted()?),用來(lái)響應(yīng)子組件的事件(?$event?)并更新一個(gè)計(jì)數(shù)器。

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

@Component({
  selector: 'app-vote-taker',
  template: `
    

Should mankind colonize the Universe?

Agree: {{agreed}}, Disagree: {{disagreed}}

` }) export class VoteTakerComponent { agreed = 0; disagreed = 0; voters = ['Narco', 'Celeritas', 'Bombasto']; onVoted(agreed: boolean) { if (agreed) { this.agreed++; } else { this.disagreed++; } } }

本框架把事件參數(shù)(用 ?$event? 表示)傳給事件處理方法,該方法會(huì)處理它:

測(cè)試一下!

測(cè)試確保點(diǎn)擊 Agree 和 Disagree 按鈕時(shí),計(jì)數(shù)器被正確更新。

// ...
it('should not emit the event initially', async () => {
  const voteLabel = element(by.tagName('app-vote-taker')).element(by.tagName('h3'));
  expect(await voteLabel.getText()).toBe('Agree: 0, Disagree: 0');
});

it('should process Agree vote', async () => {
  const voteLabel = element(by.tagName('app-vote-taker')).element(by.tagName('h3'));
  const agreeButton1 = element.all(by.tagName('app-voter')).get(0)
    .all(by.tagName('button')).get(0);

  await agreeButton1.click();

  expect(await voteLabel.getText()).toBe('Agree: 1, Disagree: 0');
});

it('should process Disagree vote', async () => {
  const voteLabel = element(by.tagName('app-vote-taker')).element(by.tagName('h3'));
  const agreeButton1 = element.all(by.tagName('app-voter')).get(1)
    .all(by.tagName('button')).get(1);

  await agreeButton1.click();

  expect(await voteLabel.getText()).toBe('Agree: 0, Disagree: 1');
});
// ...

父組件與子組件通過(guò)本地變量互動(dòng)

父組件不能使用數(shù)據(jù)綁定來(lái)讀取子組件的屬性或調(diào)用子組件的方法。但可以在父組件模板里,新建一個(gè)本地變量來(lái)代表子組件,然后利用這個(gè)變量來(lái)讀取子組件的屬性和調(diào)用子組件的方法,如下例所示。

子組件 ?CountdownTimerComponent ?進(jìn)行倒計(jì)時(shí),歸零時(shí)發(fā)射一個(gè)導(dǎo)彈。?start ?和 ?stop ?方法負(fù)責(zé)控制時(shí)鐘并在模板里顯示倒計(jì)時(shí)的狀態(tài)信息。

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

@Component({
  selector: 'app-countdown-timer',
  template: '

{{message}}

' }) export class CountdownTimerComponent implements OnDestroy { intervalId = 0; message = ''; seconds = 11; ngOnDestroy() { this.clearTimer(); } start() { this.countDown(); } stop() { this.clearTimer(); this.message = `Holding at T-${this.seconds} seconds`; } private clearTimer() { clearInterval(this.intervalId); } private countDown() { this.clearTimer(); this.intervalId = window.setInterval(() => { this.seconds -= 1; if (this.seconds === 0) { this.message = 'Blast off!'; } else { if (this.seconds < 0) { this.seconds = 10; } // reset this.message = `T-${this.seconds} seconds and counting`; } }, 1000); } }

計(jì)時(shí)器組件的宿主組件 ?CountdownLocalVarParentComponent ?如下:

import { Component } from '@angular/core';
import { CountdownTimerComponent } from './countdown-timer.component';

@Component({
  selector: 'app-countdown-parent-lv',
  template: `
    

Countdown to Liftoff (via local variable)

{{timer.seconds}}
`, styleUrls: ['../assets/demo.css'] }) export class CountdownLocalVarParentComponent { }

父組件不能通過(guò)數(shù)據(jù)綁定使用子組件的 ?start ?和 ?stop ?方法,也不能訪問(wèn)子組件的 ?seconds ?屬性。

把本地變量(?#timer?)放到(??)標(biāo)簽中,用來(lái)代表子組件。這樣父組件的模板就得到了子組件的引用,于是可以在父組件的模板中訪問(wèn)子組件的所有屬性和方法。

這個(gè)例子把父組件的按鈕綁定到子組件的 ?start ?和 ?stop ?方法,并用插值來(lái)顯示子組件的 ?seconds ?屬性。

下面是父組件和子組件一起工作時(shí)的效果。

測(cè)試一下!

測(cè)試確保在父組件模板中顯示的秒數(shù)和子組件狀態(tài)信息里的秒數(shù)同步。它還會(huì)點(diǎn)擊 Stop 按鈕來(lái)停止倒計(jì)時(shí):

// ...
// The tests trigger periodic asynchronous operations (via `setInterval()`), which will prevent
// the app from stabilizing. See https://angular.io/api/core/ApplicationRef#is-stable-examples
// for more details.
// To allow the tests to complete, we will disable automatically waiting for the Angular app to
// stabilize.
beforeEach(() => browser.waitForAngularEnabled(false));
afterEach(() => browser.waitForAngularEnabled(true));

it('timer and parent seconds should match', async () => {
  const parent = element(by.tagName(parentTag));
  const startButton = parent.element(by.buttonText('Start'));
  const seconds = parent.element(by.className('seconds'));
  const timer = parent.element(by.tagName('app-countdown-timer'));

  await startButton.click();

  // Wait for `` to be populated with any text.
  await browser.wait(() => timer.getText(), 2000);

  expect(await timer.getText()).toContain(await seconds.getText());
});

it('should stop the countdown', async () => {
  const parent = element(by.tagName(parentTag));
  const startButton = parent.element(by.buttonText('Start'));
  const stopButton = parent.element(by.buttonText('Stop'));
  const timer = parent.element(by.tagName('app-countdown-timer'));

  await startButton.click();
  expect(await timer.getText()).not.toContain('Holding');

  await stopButton.click();
  expect(await timer.getText()).toContain('Holding');
});
// ...

父組件調(diào)用@ViewChild()

這個(gè)本地變量方法是個(gè)簡(jiǎn)單明了的方法。但是它也有局限性,因?yàn)楦附M件-子組件的連接必須全部在父組件的模板中進(jìn)行。父組件本身的代碼對(duì)子組件沒(méi)有訪問(wèn)權(quán)。

如果父組件的需要依賴于子組件類,就不能使用本地變量方法。組件之間的父子關(guān)系 組件的父子關(guān)系不能通過(guò)在每個(gè)組件的中各自定義本地變量來(lái)建立。這是因?yàn)檫@兩個(gè)的實(shí)例互相不知道,因此父也就不能訪問(wèn)子中的屬性和方法。

當(dāng)父組件需要這種訪問(wèn)時(shí),可以把子組件作為 ViewChild,注入到父組件里面。

下面的例子用與倒計(jì)時(shí)相同的范例來(lái)解釋這種技術(shù)。 它的外觀或行為沒(méi)有變化。子組件?CountdownTimerComponent?也和原來(lái)一樣。

本地變量切換到 ViewChild 技術(shù)的唯一目的就是做示范。

下面是父組件 ?CountdownViewChildParentComponent?:

import { AfterViewInit, ViewChild } from '@angular/core';
import { Component } from '@angular/core';
import { CountdownTimerComponent } from './countdown-timer.component';

@Component({
  selector: 'app-countdown-parent-vc',
  template: `
    

Countdown to Liftoff (via ViewChild)

{{ seconds() }}
`, styleUrls: ['../assets/demo.css'] }) export class CountdownViewChildParentComponent implements AfterViewInit { @ViewChild(CountdownTimerComponent) private timerComponent!: CountdownTimerComponent; seconds() { return 0; } ngAfterViewInit() { // Redefine `seconds()` to get from the `CountdownTimerComponent.seconds` ... // but wait a tick first to avoid one-time devMode // unidirectional-data-flow-violation error setTimeout(() => this.seconds = () => this.timerComponent.seconds, 0); } start() { this.timerComponent.start(); } stop() { this.timerComponent.stop(); } }

把子組件的視圖插入到父組件類需要做一點(diǎn)額外的工作。

首先,你必須導(dǎo)入對(duì)裝飾器 ?ViewChild ?以及生命周期鉤子 ?AfterViewInit ?的引用。

接著,通過(guò) ?@ViewChild? 屬性裝飾器,將子組件 ?CountdownTimerComponent ?注入到私有屬性 ?timerComponent ?里面。

組件元數(shù)據(jù)里就不再需要 ?#timer? 本地變量了。而是把按鈕綁定到父組件自己的 ?start ?和 ?stop ?方法,使用父組件的 ?seconds ?方法的插值來(lái)展示秒數(shù)變化。

這些方法可以直接訪問(wèn)被注入的計(jì)時(shí)器組件。

?ngAfterViewInit()? 生命周期鉤子是非常重要的一步。被注入的計(jì)時(shí)器組件只有在 Angular 顯示了父組件視圖之后才能訪問(wèn),所以它先把秒數(shù)顯示為 0.

然后 Angular 會(huì)調(diào)用 ?ngAfterViewInit ?生命周期鉤子,但這時(shí)候再更新父組件視圖的倒計(jì)時(shí)就已經(jīng)太晚了。Angular 的單向數(shù)據(jù)流規(guī)則會(huì)阻止在同一個(gè)周期內(nèi)更新父組件視圖。應(yīng)用在顯示秒數(shù)之前會(huì)被迫再等一輪。

使用 ?setTimeout()? 來(lái)等下一輪,然后改寫 ?seconds()? 方法,這樣它接下來(lái)就會(huì)從注入的這個(gè)計(jì)時(shí)器組件里獲取秒數(shù)的值。

父組件和子組件通過(guò)服務(wù)來(lái)通訊

父組件和它的子組件共享同一個(gè)服務(wù),利用該服務(wù)在組件家族內(nèi)部實(shí)現(xiàn)雙向通訊。

該服務(wù)實(shí)例的作用域被限制在父組件和其子組件內(nèi)。這個(gè)組件子樹(shù)之外的組件將無(wú)法訪問(wèn)該服務(wù)或者與它們通訊。

這個(gè) ?MissionService ?把 ?MissionControlComponent ?和多個(gè) ?AstronautComponent ?子組件連接起來(lái)。

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

@Injectable()
export class MissionService {

  // Observable string sources
  private missionAnnouncedSource = new Subject();
  private missionConfirmedSource = new Subject();

  // Observable string streams
  missionAnnounced$ = this.missionAnnouncedSource.asObservable();
  missionConfirmed$ = this.missionConfirmedSource.asObservable();

  // Service message commands
  announceMission(mission: string) {
    this.missionAnnouncedSource.next(mission);
  }

  confirmMission(astronaut: string) {
    this.missionConfirmedSource.next(astronaut);
  }
}

?MissionControlComponent ?提供服務(wù)的實(shí)例,并將其共享給它的子組件(通過(guò) ?providers ?元數(shù)據(jù)數(shù)組),子組件可以通過(guò)構(gòu)造函數(shù)將該實(shí)例注入到自身。

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

import { MissionService } from './mission.service';

@Component({
  selector: 'app-mission-control',
  template: `
    

Mission Control

History

  • {{event}}
`, providers: [MissionService] }) export class MissionControlComponent { astronauts = ['Lovell', 'Swigert', 'Haise']; history: string[] = []; missions = ['Fly to the moon!', 'Fly to mars!', 'Fly to Vegas!']; nextMission = 0; constructor(private missionService: MissionService) { missionService.missionConfirmed$.subscribe( astronaut => { this.history.push(`${astronaut} confirmed the mission`); }); } announce() { const mission = this.missions[this.nextMission++]; this.missionService.announceMission(mission); this.history.push(`Mission "${mission}" announced`); if (this.nextMission >= this.missions.length) { this.nextMission = 0; } } }

?AstronautComponent ?也通過(guò)自己的構(gòu)造函數(shù)注入該服務(wù)。由于每個(gè) ?AstronautComponent ?都是 ?MissionControlComponent ?的子組件,所以它們獲取到的也是父組件的這個(gè)服務(wù)實(shí)例。

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

import { MissionService } from './mission.service';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-astronaut',
  template: `
    

{{astronaut}}: {{mission}}

` }) export class AstronautComponent implements OnDestroy { @Input() astronaut = ''; mission = ''; confirmed = false; announced = false; subscription: Subscription; constructor(private missionService: MissionService) { this.subscription = missionService.missionAnnounced$.subscribe( mission => { this.mission = mission; this.announced = true; this.confirmed = false; }); } confirm() { this.confirmed = true; this.missionService.confirmMission(this.astronaut); } ngOnDestroy() { // prevent memory leak when component destroyed this.subscription.unsubscribe(); } }

注意,這個(gè)例子保存了 ?
subscription ?變量,并在 ?
AstronautComponent ?被銷毀時(shí)調(diào)用 ?
unsubscribe()? 退訂。 這是一個(gè)用于防止內(nèi)存泄漏的保護(hù)措施。實(shí)際上,在這個(gè)應(yīng)用程序中并沒(méi)有這個(gè)風(fēng)險(xiǎn),因?yàn)?nbsp;?
AstronautComponent ?的生命期和應(yīng)用程序的生命期一樣長(zhǎng)。但在更復(fù)雜的應(yīng)用程序環(huán)境中就不一定了。

不需要在 ?
MissionControlComponent ?中添加這個(gè)保護(hù)措施,因?yàn)樗鳛楦附M件,控制著 ?
MissionService ?的生命期。

History 日志證明了:在父組件 ?MissionControlComponent ?和子組件 ?AstronautComponent ?之間,信息通過(guò)該服務(wù)實(shí)現(xiàn)了雙向傳遞。

測(cè)試一下!

測(cè)試確保點(diǎn)擊父組件 ?MissionControlComponent ?和子組件 ?AstronautComponent ?兩個(gè)的組件的按鈕時(shí),History 日志和預(yù)期的一樣。

// ...
it('should announce a mission', async () => {
  const missionControl = element(by.tagName('app-mission-control'));
  const announceButton = missionControl.all(by.tagName('button')).get(0);
  const history = missionControl.all(by.tagName('li'));

  await announceButton.click();

  expect(await history.count()).toBe(1);
  expect(await history.get(0).getText()).toMatch(/Mission.* announced/);
});

it('should confirm the mission by Lovell', async () => {
  await testConfirmMission(1, 'Lovell');
});

it('should confirm the mission by Haise', async () => {
  await testConfirmMission(3, 'Haise');
});

it('should confirm the mission by Swigert', async () => {
  await testConfirmMission(2, 'Swigert');
});

async function testConfirmMission(buttonIndex: number, astronaut: string) {
  const missionControl = element(by.tagName('app-mission-control'));
  const announceButton = missionControl.all(by.tagName('button')).get(0);
  const confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex);
  const history = missionControl.all(by.tagName('li'));

  await announceButton.click();
  await confirmButton.click();

  expect(await history.count()).toBe(2);
  expect(await history.get(1).getText()).toBe(`${astronaut} confirmed the mission`);
}
// ...

文章標(biāo)題:創(chuàng)新互聯(lián)Angular教程:Angular 組件交互
本文來(lái)源:http://www.dlmjj.cn/article/dhspgso.html