Angular5 Component通訊

Angular5是一個很是強大的前端框架,學習曲線較陡,掌握了不一樣模塊(主要指Component)之間的通訊機制,就等於掌握了Angular5。下面詳盡列舉各類通訊方式供您參考:javascript

1、從Component代碼到Template

1. 做爲Html內容

使用場景:頁面加載,自動顯示title的值,字號爲h3。css

html標籤,花括號裏面的是模板表達式(template expression):html

<h3>{{title}}</h3>

ts代碼,寫在Component類內部:前端

title = "前端編程"。

2. 做爲DOM property的值

使用場景:頁面加載,自動顯示圖片。
<img scr="{{imageUrl}}" style="height:30px">
ts代碼:
imageUrl="http://xxx.xxx.com/1.png";java

2、響應事件

1. 標準Html tag的標準事件

使用場景:如按鈕的點擊事件:
<button (click)="onSave($event)">保存</button>
ts代碼:express

onSave(event) {

....

}

3、父子Component間通訊

1. 經過@Input綁定輸入屬性

使用場景:利用第三方Component展現某個對象的細節編程

Angular5不容許綁定到一個不一樣Component的屬性,除非使用@Input或者(之後說)@Output顯式地申明。數組

好比傳遞一個當前的收貨地址到一個地址細節的Component:瀏覽器

<app-address-detail [address]="currentAddress"></app-address-detail>
ts代碼:
@Input() address: Address;ruby

2. 經過@ViewChild而且使用Component方式,父---->子

使用場景:頁面一部分一個點擊事件,致使另外一部分產生變化

好比:點擊一個按鈕,顯示點擊次數,其中顯示點擊次數的邏輯由另一個Component負責。

子Component代碼:

@Component({ selector: 'app-number', template: `<b>{{message}}</b>` }) export class NumberComponent { message: string = ''; count: number = 0; increaseByOne() { this.count = this.count + 1; this.message = "次數: " + this.count; } @Component({ selector: 'app-number', template: `<b>{{message}}</b>` }) export class NumberComponent { message: string = ''; count: number = 0; increaseByOne() { this.count = this.count + 1; this.message = "次數: " + this.count; }

父template:

<button type="button" (click)="increase()">增長</button>

<app-number></app-number>

父Component代碼:

export class NumberParentComponent { @ViewChild(NumberComponent) private numberComponent: NumberComponent; increase() { this.numberComponent.increaseByOne(); } } export class NumberParentComponent { @ViewChild(NumberComponent) private numberComponent: NumberComponent; increase() { this.numberComponent.increaseByOne(); } }

3. @ViewChild和ElementRef,經過Directive

使用場景:點擊頁面一部分,另外一部分顏色發生變化
須要獲取Directive所在的整個元素(element),在Directive方法內部,修改那個元素的Dom屬性,顏色。

子Directive代碼:

@Directive({ selector: '[chColor]' }) export class ChColorDirective implements AfterViewInit{ constructor(private elementRef: ElementRef) { } ngAfterViewInit(){ this.elementRef.nativeElement.style.color = 'green'; } change(changedColor: String){ this.elementRef.nativeElement.style.color = changedColor; } } @Directive({ selector: '[chColor]' }) export class ChColorDirective implements AfterViewInit{ constructor(private elementRef: ElementRef) { } ngAfterViewInit(){ this.elementRef.nativeElement.style.color = 'green'; } change(changedColor: String){ this.elementRef.nativeElement.style.color = changedColor; } }

父Template代碼:

<p chColor>改變個人顏色</p>
<div>
  修改顏色:
  <input type="button" name="red" (click)="changeColor('red')"> 紅色
</div>

4. @ViewChildren,經過Component

使用場景:好比刪除一組子Component,每一個子Com前面有checkbox。
父Component的Template:

<section>
  <h4 *ngIf="todos.getAll().length">Todo列表</h4>
  <todo-item *ngFor="let todo of todos.getAll()" [todo]="todo"></todo>
</section>

父component代碼:

export class TodoAppComponent implements AfterViewInit { @ViewChildren(TodoComponent) todoComponents: QueryList<TodoComponent>; constructor(private todos: TodoList) {} ngAfterViewInit() { // viewChildren在這個地方變得可用 } export class TodoAppComponent implements AfterViewInit { @ViewChildren(TodoComponent) todoComponents: QueryList<TodoComponent>; constructor(private todos: TodoList) {} ngAfterViewInit() { // viewChildren在這個地方變得可用 }

由於Angular的DOM編譯器會先處理父Component,而後再處理children,這樣在初始化的時候,todosComponent是未定義的,undefined。他們的值在ngAfterViewInit函數裏面設置。好比把獲取到的todosComponent編程數組再賦值給todos。
得到了子Component列表以後,就能夠按照需求處理了。

5. 往一個模板裏插入一塊動態內容,單槽

使用場景:父Component往子Component裏面插入一塊Html
往什麼地方插呢?這是由<ng-content>標籤訂義了插槽。
子Card Component的Template:

<div class="card"> <!--單槽插入點--> <ng-content></ng-content> </div> <div class="card"> <!--單槽插入點--> <ng-content></ng-content> </div> 

父Component的Template:

<card> <!--下面是動態內容--> <div class="card-content"> <p>This is dynamic content</p> </div> </card> <card> <!--下面是動態內容--> <div class="card-content"> <p>This is dynamic content</p> </div> </card>

6. 往一個模板裏插入多塊內容,多槽

使用場景:父Component往子Component裏面插入多塊Html內容,插入地點由selector匹配

子Card Component的Template:

<div>
  <!--tag-->
  <ng-content select="header"></ng-content>
  <!--css 選擇器-->
  <ng-content select="div.body"></ng-content>
  <!--attribute-->
  <ng-content select="[card][body]"></ng-content>
  <!--帶值的attribute-->
  <ng-content select="[card-type=body]"></ng-content>
</div>

父Component的Template:

<card>
  <header>...</header>
  <div class="body">我是body</div>
  <div body card>...</div>
  <div card-type="body">...</div>
</card>

7. 當Template涉及三個Components,@ContentChild

使用場景:在第二個children裏面獲取第三個Component信息

@Component({ selector: 'app-footer', template: '<ng-content></ng-content>' }) class FooterComponent{} @Component(...) class TodoAppComponent implements AfterContentInit { @ContentChild(FooterComponent) footer: FooterComponent; ngAfterContentInit() { // this.footer now points to the instance of 'FooterComponent' } } @Component({ selector: 'demo-app', template: ` <content> <todo-app> <app-footer> <small>Yet another todo app!</small> </app-footer> </todo-app> </content> ` }) export class AppComponent{} @Component({ selector: 'app-footer', template: '<ng-content></ng-content>' }) class FooterComponent{} @Component(...) class TodoAppComponent implements AfterContentInit { @ContentChild(FooterComponent) footer: FooterComponent; ngAfterContentInit() { // this.footer now points to the instance of 'FooterComponent' } } @Component({ selector: 'demo-app', template: ` <content> <todo-app> <app-footer> <small>Yet another todo app!</small> </app-footer> </todo-app> </content> ` }) export class AppComponent{}

這裏AppComponent使用TodoAppComponent並在它的一對tag之間傳遞FooterComponent給它,咱們稱FooterComponent是TodoAppComponent的content child。

8. 內層Component向父Component發事件

使用場景:父Component刪除子Component,可是刪除操做在子Component上,好比一個按鈕。
子Component暴露一個EventEmitter爲屬性,而後綁定到父Component的一個函數,綁定以後,父Component就開始偵聽這個事件了。
子Component:

@Component({ template: ` <div> <button (click)="delete()">刪除</button> </div>` }) export class HeroDetailComponent{ // this component makes a request but it can't actually delete a hero deleteRequest = new EventEmitter<Hero>(); delete() { this.deleteRequest.emit(this.hero); } } @Component({ template: ` <div> <button (click)="delete()">刪除</button> </div>` }) export class HeroDetailComponent{ // this component makes a request but it can't actually delete a hero deleteRequest = new EventEmitter<Hero>(); delete() { this.deleteRequest.emit(this.hero); } }

父Component的Template:
<app-hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero">
當子Component觸發事件時,Angular調用父Component的deleteHero方法,把要傳遞的hero做爲$event變量傳遞出來。

4、同一個Component,修改Html另外一部分的Native屬性

1. 經過@ViewChild和模板引用變量訪問Native Element

使用場景:獲取一行子Html並修改該Html屬性,顏色
Template代碼:

<div>
  姓名:<input type="text" #name><br/>
</div>

ts代碼:

export class ExampleComponent implements AfterViewInit { @ViewChild('name') private name: ElementRef; ngAfterViewInit() { this.name.nativeElement.style.color = 'red'; } } export class ExampleComponent implements AfterViewInit { @ViewChild('name') private name: ElementRef; ngAfterViewInit() { this.name.nativeElement.style.color = 'red'; } }

5、非父子Component間通訊

1. 經過路由傳遞參數(Observable)

使用場景:地址列表,還有兩個按鈕(上一個,下一個地址)瀏覽歷史地址,不但願每切換一個地址都從新建立一個新的地址Component,而是複用一個地址Component。
當router建立新的Component時,會調用ngOnInit()函數,這樣寫:

 this.address = this.route.paramMap .switchMap(params: ParamMap) => this.service.getAddress(params.get('id'))); this.address = this.route.paramMap .switchMap(params: ParamMap) => this.service.getAddress(params.get('id')));

這裏this.service.getAddress返回一個Observable<Address>對象。

2. 經過路由傳遞參數(非Observable)

使用場景:地址列表,從列表中每選擇一個,切換爲該地址的詳細信息,若是想顯示另一個地址的詳細信息,必須先回到列表頁面;這頁意味着每選擇一個地址都從新建立一個新的地址Component實例。

let id = this.route.snapshot.paramMap.get('id'); this.address = this.service.getAddress(id); let id = this.route.snapshot.paramMap.get('id'); this.address = this.service.getAddress(id);

3. 複雜參數經過可選參數傳遞(Optional Parameters)

使用場景:須要傳遞參數的場景太多,不可能每個參數都對應一個route,若是那樣的話路由配置會至關複雜,這時候可選參數就派上用場了,不須要更改路由配置。
觀察地址欄,可選參數不一樣於路由參數,也不是查詢參數,而是矩陣參數,矩陣參數也是一個標準,並不是angular發明的,形式以下:
;id=15;foo=foo

發送端ts代碼:
this.router.navigate(['/list', {id: addressId, foo: 'foo'}]);
接收端仍是經過ActivatedRoute獲取地址Id:

this.addresses = this.route.paramMap .switchMap(params: ParamMap) => // (+) before 'params.get()' turns the string into a number this.selectedId = +params.get('id'); return this.service.getAddresses(); }); this.addresses = this.route.paramMap .switchMap(params: ParamMap) => // (+) before 'params.get()' turns the string into a number this.selectedId = +params.get('id'); return this.service.getAddresses(); });

6、DOM和Directive之間的通訊

1. Directive更改Host元素的外觀和行爲,@HostListener

使用場景:一個Html tag,好比p,應用了一個Directive,當用戶鼠標移到這個p上面,高亮這個p元素;鼠標移開,取消高亮。
分析這個需求,要求Directive能監聽到Host元素的事件(Hover, Leave等),這是@HostListener的做用,把@HostListener放到某個函數前面,當事件發生時就會調用這個函數。

Template:
<p appHighlight>點亮我</p>
Directive代碼:

@HostListener('mouseenter') onmouseenter() { this.hightlight('yellow'); } @HostListener('mouseleave') onmouseleave() { this.highlight(null); } private highlight(color: string) { this.el.nativeElement.style.backgroundColor = color; } @HostListener('mouseenter') onmouseenter() { this.hightlight('yellow'); } @HostListener('mouseleave') onmouseleave() { this.highlight(null); } private highlight(color: string) { this.el.nativeElement.style.backgroundColor = color; }

以上設置背景顏色的方式假定了咱們的程序運行在瀏覽器環境下。可是Angular瞄準的是跨平臺,因此Angular提供了一種平臺獨立的方式來設置因素的屬性,Renderer2正是用於這種目的。
this.render.setStyle(el.nativeElement, 'width', '200px');

2. @HostBinding更改Host元素屬性

使用場景:更改Host元素的外觀或者行爲,經過把Directive內部的屬性和Host元素的屬性綁定起來,實現了,內部屬性一發生變化,Host元素相應的屬性就會發生變化。
Directive代碼:
@HostBinding('class.card-outline')private isHovering: boolean;
而後在Directive內部的事件處理函數中更改isHovering的值,從而間接改變Host元素的屬性。

3. Resolver

使用場景:由一個Component導航到另一個Component,可能要事先到服務器取出數據,若是數據存在,則繼續導航;若是不存在,則取消導航,Resolver就是取數據的。
路由配置:

path: 'crisis-center',
component: CrisisCenterComponent,
children: [
  {
    path: '',
    Component: CrisisListComponent,
    children: [
      {
        path: ':id',
        component: CrisisDetailComponent,
        canDeactivate: [CanDeactivateGuard],
        resolve: {
          crisis: CrisisDetailResolver
        }
    }
  }
];