angular2 組件
首先了解angular2 組件的含義
angular2的應用就是一系列組件的集合
我們需要創建可復用的組件供多個組件重復使用
組件是嵌套的,實際應用中組件是相互嵌套使用的
組件中的數據調用可以使用inputs和outputs
一個組件可以是一種指令
一個組件可以包含前端表現及后端邏輯
一個組件可以是一個代碼片段,能夠獨立運行
進一步理解指令
一個指令就是一個組件
一個指令可以裝飾指令,用於改變DOM
一個指令可以是模板指令,可以改變element
一個實際例子
一輛車有門、方向盤、窗等等,假設車就是母組件,方向盤就是子組件
在angular2中組件是可以多級次的,那么他們間的交流就是數據交換,這種機制就是數據流
在當前angular2版本的數據綁定是單向的,父組件數據向子組件數據傳遞,再向子子組件數據傳遞
子組件可以使用event向父組件傳遞數據
所以我們可以說有兩種方式數據綁定
我們能夠通過判斷ngmodel來實現這兩種數據綁定方式
實例分析
myApp.ts
import {bootstrap} from 'angular2/platform/browser';
import {carComponent} from "./car.component";
bootstrap(carComponent);
這里首先說明angular入口,即我們需要啟動的組件
這里看到首先要引入bootstrap,用於下面加載組件,其次引入我們需要的組件carComponent,也就是我們下面要定義的一個class
car.component.ts
///<reference path="../node_modules/angular2/typings/browser.d.ts"/> import {Component} from 'angular2/core'; import {door} from './door.component'; @Component({ selector: "carTag", template: ` <h3 class="titles">Mother Car Component</h3> <input type ="text" #textInput bind-value="text" /> <button on-click="onCarChange(textInput.value)">Change</button> <div class="child-style"> <door [textLevel1]="text" (changed)="onCarChange($event)"> </door> </div> `, directives: [door], styles:[ ` .titles { color:#0099FF } .child-style { background-attachment: initial;
background-size: initial;
background-origin: initial;
background-clip: initial;
background-position: initial;
background-repeat: initial;">
} `] }) export class carComponent { text: string="輸入文字"; onCarChange(value) { this.text ="向子組件傳遞數據:"+value; } }
door.component.ts
///<reference path="../node_modules/angular2/typings/browser.d.ts"/> import {Component, EventEmitter} from 'angular2/core'; @Component({ selector: "door", template: ` <h2>Child Component</h2> <input type ="text" #textInput value="{{textLevel1}}"> <button (click)="onDoorChange(textInput.value)">Change</button> `, inputs: ['textLevel1'], outputs: ['changed'] }) export class door { textlevel: string; changed = new EventEmitter(); onDoorChange(value) { this.textlevel ="向父組件傳遞數據:"+ value; this.changed.emit(value); } } }
Car.html
<!DOCTYPE html> <html> <head> <script>document.write('<base href="' + document.location + '" />');</script> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- CSS file --> <link href="css/bootstrap.min.css" rel="stylesheet" /> <!-- IE polyfills, keep the order please --> <script src="node_modules/es6-shim/es6-shim.min.js"></script> <script src="node_modules/systemjs/dist/system-polyfills.js"></script> <script src="node_modules/angular2/bundles/angular2-polyfills.js"></script> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="node_modules/rxjs/bundles/Rx.js"></script> <script src="node_modules/angular2/bundles/angular2.dev.js"></script> <script src="node_modules/typescript/lib/typescript.js"></script> <!-- Agular 2 Router --> <script src="node_modules/angular2/bundles/router.dev.js"></script> <!-- Config Agular 2 and Typescript --> <script> System.config({ transpiler: 'typescript', typescriptOptions: { emitDecoratorMetadata: true }, packages: {'app': {defaultExtension: 'ts'}} }); System.import('app/myApp') .then(null, console.error.bind(console)); </script> </head> <!-- Run the application --> <body> <carTag>Loading Sample...</carTag> </body> </html>
組件引用
第一行,目前版本的bug,所有自定義組件在當前版本下需要加入
下面引入Component,@Companent是typescript的一種注解語言,用於配置我們當前組件,具體里面有很多的參數,后面細說。
引入我們需要的組件(import)
組件配置
先看看selector,即標簽,也就是我們在html中要使用的標簽名稱,類似於標准的html標簽(如<div>,<h1>),我們知道一個component可以是一個指令,carComponent 就是一個director,通過定義selector來標識這個指令。
template:該組件的模板,可以理解為該組件的視圖,在template中就可以使用剛剛定義的標簽,也就是在selector中定義標簽名稱,方法就像使用標准html的標簽一樣。此處是一個內嵌的模板,使用一對“`”符號來標記模板中的內容。
在該模板中可以看到input標簽中定義了一個局部變量,Angular2提供一種簡單的語法將元素 映射為局部變量:添加一個以#或var-開始的屬性,后續的部分表示變量名, 這個變量對應元素的實例。在input的value處我們看到該值綁定了carComponent組件的一個屬性,使用{{}}綁定,這是angular的一種插值綁定語法,另一種方式可使用使用一對中括號將HTML元素或組件的屬性綁定到組件模型的某個表達式, 當表達式的值變化時,對應的DOM對象將自動得到更新如<input type ="text" #textInput [value]="text" />注意value后的text是carComponent的一個屬性,而value是input對象的屬性,還有一種表達方式你也可以使用bind-前綴進行屬性綁定如<input type ="text" #textInput bind-value="text" />
該模板button中(click)代表着事件 ,使用一對小括號包裹事件名稱,並綁定 到表達式,也可使用事件名稱前加on-前綴如
<button on-click="onCarChange($event)">Change</button>
textInput即是前面在input中定義的局部變量代表着input的實例,onCarChange是carComponent中定義的方法,接收 一個參數,這里是為了觸發text屬性變化來通知子組件,當然也可以直接在input中進行定義,如
<input type ="text" #textInput bind-value="text" (change)="onCarChange(textInput.value)" />
效果是一樣的。
該模板中使用了door標簽,也就是我們在door組件中定義的標簽,其中的textLevel1和changed是在door組件中定義的輸入和輸出項
組件嵌套及溝通
在car組件中的template中使用door標簽,就意味着需要加入door組件,故形成了嵌套關系,那么首先需要import 引入door組件import {door} from './door.component';
在模板中我們使用door組件的door標簽,那么就需要配置directives指令集
directives:指令集,注意是一個集合,也就是說在該組件中嵌入多個組件(需要先import),由於上面在模板中使用了door指令所以在該集合中必須加入door指令
我們看到在car組件中的模板是這樣使用car組件的
<door [textLevel1]="text" (changed)="onCarChange($event)">
這里加入了door標簽,而[textLevel1]是door標簽的一個屬性,可以理解為input標簽的value屬性 ,那么這個屬性的定義需要在door組件中的inputs中加入
inputs: ['textLevel1']
表示door標簽使用textLevel1來接收上級傳來的數據,[textLevel1]="text"表示door組件的textlevel1屬性綁定了car組件的text屬性,通過這樣設計就實現了父組件向子組件的數據傳遞。那么子組件又是如何向父組件傳遞數據的呢?就是使用outputs,outputs提供了一個事件的集合,利用子組件定義的事件來向上級傳遞數據,如(changed)=" onCarChange ($event)"中所示,(changed)是door組件定義的事件並在outputs配置中做了聲明 ,此處注意調用的是父組件的事件處理函數。
outputs: ['changed']
$event是該事件相關的數據,這里就是textlevel1的值
組件定義
為了能夠讓其他組件能夠使用所以需要export該組件,組件的定義和class一樣,使用typescirpt語法,組件中可以定義屬性,方法,可以有公共的也可以是私有的,如果是私有的需要加入private聲明,需要注意的是如果使用了事件需要引入EventEmitter。如import {Component, EventEmitter} from 'angular2/core';
事件定義使用
changed = new EventEmitter();
使用Emit 傳遞事件如this.changed.emit(value);
組件的數據交互
Angular2相比angular1有一個很大的改變就是默認情況下的綁定是單向綁定,也是就是說視圖上的數據變更不能直接更新model了,需要程序化進行更新,這樣做的目的是為了提高效率,當然目前版本還保留了[(ngModel)]這種綁定方式,可以實現雙向綁定。那么組件間的數據相互如何傳遞呢,父-〉子,子-〉父,子-〉子?
父到子的數據傳遞
在父組件中使用子組件就是簡單的使用子組件標簽方式,那么可以將子組件的一個屬性綁定到父組件的一個屬性上就實現了父組件數據向子組件傳遞。簡單理解下
在父組件中定義一個子組件<child [childProperty]=”parentProperty”></child>
這很類似於在父組件中使用<input [value]=”parentProperty” ></input>
通過這種方式子組件就能夠獲取父組件的數據了,需要注意的是子組件需要將用於綁定的屬性公布出去,即使用inputs:[‘childProperty’],這里是個數組,可以公布多個屬性供外部綁定。
總結來說就是可以通過將父組件的一個屬性綁定到子組件中公布的一個屬性上就實現了父組件向子組件傳遞數據的效果。
子到父的數據傳遞
子組件向父組件傳遞數據是通過事件的方式,首先需要在子組件中定義事件,子組件的事件被觸發時引發父組件的事件響應,同時將事件參數傳遞給父組件的響應函數,這樣就完成了子組件向父組件傳遞數據。如下所示
<childLevel1 [parentMsg]="parentMsg" (childLevel1Changed)="OnChildLevel1MsgChanged($event)"> </childLevel1>
[parentMsg]是子組件的一個屬性,而值來自父組件的parentMsg,這樣實現了父組件向子組件傳遞parenMs數據,另外(childLevel1Changed)是子組件中定義的事件,而OnChildLevel1MsgChanged是父組件的一個事件響應函數,而$event代表着事件源。從這個例子我們分析看看數據流。下面是子組件的事件定義:
outputs: ['childLevel1Changed'] <button (click)="OnChildLevel1MsgChanged(childLevel1Text.value)" >changeChild1</button> OnChildLevel1MsgChanged(event) { this.childLevel1Msg = event; this.childLevel1Changed.emit(event); }
事件的發生順序由子組件的按鈕引發交由事件處理程序,事件的參數是childLevel1Text. Value,事件處理程序將子組件定義的事件廣播出去,並傳遞數據源,父組件檢測到子組件的事件並交給父組件的事件處理程序來處理。這樣就將子組件的數據傳遞給了父組件。
子到子的數據傳遞
了解了上面的方式后,那么子到子就簡單了,分成子到父,再父到子。父組件在中間起到協調作用
最后,看到這些有些疑惑了,一個真實應用會有很多的數據交互,如果使用這種機制,那很難去維護這里的關系了,難道沒有更好的方式去實現組件間的數據交互嗎?答案是服務!