Angular2 入門教程

一、 入門

1、初識Angular2

硬知識:Angular2與Angular的區別

(1)依賴加載:Angular1是依賴前置,angular2是按需加載

(2)數據綁定:

Angular1 在啓動時會給所有的異步交互點打補丁:
超時、
Ajax 請求、
瀏覽器事件、
Websockets,等等
在那些交互點,Angular 會對 scope 對象進行變動檢查,如果發現有變動就激發相應的監視器
重新運行變動檢查,檢查是否有更多的變化發生,重新運行監視器

Angular 2 使用 zone.js 機制使摘要循環不再被需要。簡單的非 Angular 指定代碼可以透明地激發一個Angular 2 摘要。
zone.js的設計靈感來源於Dart語言,它描述JavaScript執行過程的上下文,可以在異步任務之間進行持久性傳遞,它類似於Java中的TLS(線程本地存儲)技術,zone.js則是將TLS引入到JavaScript語言中的實現框架。

寫一個Angular2的Hello World應用相當簡單,分三步走:

  1. 引入Angular2預定義類型
import {Component,View,bootstrap} from "angular2/angular2";

import是ES6的關鍵字,用來從模塊中引入類型定義。在這裏,我們從angular2模塊庫中引入了三個類型: Component類、View類和bootstrap函數。

  1. 實現一個Angular2組件

實現一個Angular2組件也很簡單,定義一個類,然後給這個類添加註解:

@Component({selector:"ez-app"})
@View({template:"<h1>Hello,Angular2</h1>"})
class EzApp{}

class也是ES6的關鍵字,用來定義一個類。@Component和@View都是給類EzApp附加的元信息, 被稱爲註解/Annotation。

@Component最重要的作用是通過selector屬性(值爲CSS選擇符),指定這個組件渲染到哪個DOM對象上。 @View最重要的作用是通過template屬性,指定渲染的模板。

  1. 渲染組件到DOM

將組件渲染到DOM上,需要使用自舉/bootstrap函數:

bootstrap(EzApp);

這個函數的作用就是通知Angular2框架將EzApp組件渲染到DOM樹上。

2、註解/Annotation

ES6規範裏沒有裝飾器。這其實利用了traceur的一個實驗特性:註解。給一個類 加註解,等同於設置這個類的annotations屬性:

//註解寫法
@Component({selector:"ez-app"})
class EzApp{...}
等同於:

class EzApp{...}
EzApp.annotations = [new Component({selector:"ez-app"})];

很顯然,註解可以看做編譯器(traceur)層面的語法糖,但和python的裝飾器不同, 註解在編譯時僅僅被放在annotation裏,編譯器並不進行解釋展開 - 這個解釋的工作是 Angular2完成的

二、組件開發–模板語法

1、最簡單的模板

有兩種方法爲組件指定渲染模板:

  1. 內聯模板

可以使用組件的View註解中的template屬性直接指定內聯模板:

@View({
    template : `<h1>hello</h1>
                <div>...</div>`
})

在ES6中,使用一對`符號就可以定義多行字符串,這使得編寫內聯的模板輕鬆多了。

  1. 外部模板

也可以將模板寫入一個單獨的文件:

<!--ezcomp-tpl.html-->
<h1>hello</h1>
<div>...</div>
然後在定義組件時,使用templateUrl引用外部模板:

@View({
    templateUrl : "ezcomp-tpl.html"
})

2、directives - 使用組件

在Angular2中,一個組件的模板內除了可以使用標準的HTML元素,也可以使用自定義的組件!

這是相當重要的特性,意味着Angular2將無偏差地對待標準的HTML元素和你自己定義的組件。這樣, 你可以建立自己的領域建模語言了,這使得渲染模板和視圖模型的對齊更加容易,也使得模板的語義性 更強:

@Component({selector:"ez-app"})
        @View({
            directives:[EzCard],
            template:`
                <div class="ez-app">
                    <h1>EzApp</h1>
                    <ez-card></ez-card>
                </div>`
        })
        class EzApp{}

        @Component({selector : "ez-card"})
        @View({
            directives:[EzLogo],
            template : `
                <div class="ez-card">
                    <h1>EzCard</h1>
                    <ez-logo></ez-logo>
                </div>`
        })
        class EzCard{}

        @Component({selector : "ez-logo"})
        @View({
            template : `
                <div class="ez-logo">
                    <h1>EzLogo</h1>
                </div>`
        })
        class EzLogo{}

        bootstrap(EzApp);

聲明要在模板中使用的組件

不過,在使用自定義組件之前,必需在組件的ViewAnnotation中通過directives屬性聲明這個組件:

@View({
    directives : [EzComp],
    template : "<ez-comp></ez-comp>"
})

你應該注意到了,directives屬性的值是一個數組,這意味着,你需要在這裏聲明所有你需要在模板 中使用的自定義組件。

3、{{model}} - 文本插值

在模板中使用可以{{表達式}}的方式綁定組件模型中的表達式,當表達式變化時, Angular2將自動更新對應的DOM對象:

import {Component,View,bootstrap} from "angular2/angular2";

        @Component({selector:"ez-app"})
        @View({
            template:`
                <div>
                    <h1>{{title}}</h1>
                    <div>
                        <span>{{date}}</span> 來源:<span>{{source}}</span>
                    </div>
                </div>
            `
        })
        class EzApp{
            constructor(){
                this.title = "證監會:對惡意做空是有監測的";
                this.date = "2015年07月11日 15:32:35";
                this.source = "北京晚報";
            }
        }

        bootstrap(EzApp);

4、[property] - 綁定屬性

在模板中,也可以使用一對中括號將HTML元素或組件的屬性綁定到組件模型的某個表達式, 當表達式的值變化時,對應的DOM對象將自動得到更新:

import {bind,Component,View,bootstrap} from "angular2/angular2";

        @Component({selector:"ez-app"})
        @View({
               template:`<h1 [style.color]="color">Hello,Angular2</h1>`
        })
        class EzApp{

            constructor(){
                this.color = 'red';
                this.d = ["red", "green", "blue", "yellow", "black", "grey"];
                var self = this;
                var num = 0;
                setInterval(function () {
                    num++;
                    if (num + 1 == self.d.length) {
                        num = 0
                    }
                    self.color = self.d[num];
                }, 500);
            }            
        }

        bootstrap(EzApp);

以上的代碼,h1標籤會每秒自動變顏色。

5、(event) - 監聽事件

在模板中爲元素添加事件監聽很簡單,使用一對小括號包裹事件名稱,並綁定 到表達式即可:

import {Component,View,bootstrap} from "angular2/angular2";

        @Component({selector:"ez-app"})
        @View({
            template:`  
                <h1>Your turn! <b>{{sb}}</b></h1>
                <button (click)="roulette()">ROULETTE</button>
            `
        })
        class EzApp{
            constructor(){
                this.names = ["Jason","Mary","Linda","Lincoln","Albert","Jimmy"];
                this.roulette();
            }
            //輪盤賭
            roulette(){
                var idx = parseInt(Math.random()*this.names.length);
                this.sb = this.names[idx];
            }
        }

        bootstrap(EzApp);

上面的代碼實例爲DOM對象h1的click事件添加監聽函數onClick()。

另一種等效的書寫方法是在事件名稱前加on-前綴:

@View({template : `<h1 on-click="onClick()">HELLO</h1>`})

6、#var - 局部變量

有時模板中的不同元素間可能需要互相調用,Angular2提供一種簡單的語法將元素 映射爲局部變量:添加一個以#或var-開始的屬性,後續的部分表示變量名, 這個變量對應元素的實例。

在下面的代碼示例中,我們爲元素h1定義了一個局部變量v_h1,這個變量指向 該元素對應的DOM對象,你可以在模板中的其他地方調用其方法和屬性:

@View({
    template : `
        <h1 #v_h1>hello</h1>
        <button (click) = "#v_h1.textContent = 'HELLO'">test</button>
    `
})

如果在一個組件元素上定義局部變量,那麼其對應的對象爲組件的實例:

@View({
    directives:[EzCalc],
    template : "<ez-calc #c></ez-calc>"
})

在上面的示例中,模板內的局部變量c指向EzCalc的實例。

三、條件邏輯

1、NgIf

有時我們需要模板的一部分內容在滿足一定條件時才顯示, NgIf發揮作用的場景,它評估屬性ngIf的值是否爲真,來決定是否渲染 template元素的內容:

@View({
    template : `<!--根據變量trial的值決定是否顯示廣告圖片-->
                <template *ngIf="trial==true">
                    <img src="ad.jpg">
                </template>
                <!--以下是正文-->
                <pre>...
` })

2、ngSwitch

<container-element [ngSwitch]="switch_expression">
  <some-element *ngSwitchCase="match_expression_1">...</some-element>
  <some-element *ngSwitchCase="match_expression_2">...</some-element>
  <some-other-element *ngSwitchCase="match_expression_3">...</some-other-element>
  <ng-container *ngSwitchCase="match_expression_3">
    <!-- use a ng-container to group multiple root nodes -->
    <inner-element></inner-element>
    <inner-other-element></inner-other-element>
  </ng-container>
  <some-element *ngSwitchDefault>...</some-element>
</container-element>

3、ngFor

<li *ngFor="let item of items; let i = index; trackBy: trackByFn">...</li>
<li template="ngFor let item of items; let i = index; trackBy: trackByFn">...</li>

NgFor provides several exported values that can be aliased to local variables:

  • index will be set to the current loop iteration for each template context.
  • first will be set to a boolean value indicating whether the item is the first one in the iteration.
  • last will be set to a boolean value indicating whether the item is the last one in the iteration.
  • even will be set to a boolean value indicating whether this item has an even index.
  • odd will be set to a boolean value indicating whether this item has an odd index.

四、屬性與事件聲明

1、屬性聲明–暴露成員變量

屬性是組件暴露給外部世界的調用接口,調用者通過設置不同的屬性值來定製 組件的行爲與外觀:

image

在Angular2中爲組件增加屬性接口非常簡單,只需要在Component註解的 properties屬性中聲明組件的成員變量就可以了:

//EzCard 
@Component({
    properties:["name","country"]
})

上面的代碼將組件的成員變量name和country暴露爲同名屬性,這意味着在EzApp 的模板中,可以直接使用中括號語法來設置EzCard對象的屬性:

//EzApp
@View({
    directives : [EzCard],
    template : "<ez-card [name]="'雷鋒'" [country]="'中國'"></ez-card>"
})

提醒:如果要在模板中使用自定義的指令(組件是一種指令),必須在View註解的directives 屬性中提前聲明!

示例代碼:爲EzCard調用添加name和country屬性!

import {Component,View,bootstrap} from "angular2/angular2";

        //根組件 - EzApp
        @Component({selector:"ez-app"})
        @View({
            directives:[EzCard],
            template:`
                <div class="ez-app">
                    <h1>EzApp</h1>
                    <ez-card [name]="'frank'" [country]="'China'"></ez-card>
                </div>`
        })
        class EzApp{}       

        //具有屬性接口的組件 - EzCard
        @Component({
            selector:"ez-card",
            properties:["name","country"]
        })
        @View({
            template : `<div class='ez-card'>
                    My name is <b>{{name}}</b>, 
                    I am from <b>{{country}}</b>.</div>`
        })
        class EzCard{
            constructor(){
                this.name = "Mike";
                this.country = "Sweden";
            }
        }

        //渲染組件
        bootstrap(EzApp);

2、事件聲明 - 暴露事件源

與屬性相反,事件從組件的內部流出,用來通知外部世界發生了一些事情:
image

在Angular2中爲組件增加事件接口也非常簡單:定義一個事件源/EventEmitter, 然後通過Component註解的events接口包括出來:

//EzCard
@Component({
    events:["change"]
})
class EzCard{
    constructor(){
        this.change = new EventEmitter();
    }
}

上面的代碼將組件EzCard的事件源change暴露爲同名事件,這意味着在調用者 EzApp組件的模板中,可以直接使用小括號語法掛接事件監聽函數:

//EzApp
@View({
    template : "<ez-card (change)="onChange()"></ez-card>"
})

每次EzCard觸發change事件時,EzApp的onChange()方法都將被調用。

import {Component,View,bootstrap,EventEmitter} from "angular2/angular2";

        //根組件 - EzApp
        @Component({selector:"ez-app"})
        @View({
            directives:[EzCard],
            template:`
                <div class="ez-app">
                    <h1>EzApp</h1>
                    <ez-card (change)="onChange($event)"></ez-card>
                    <pre>{{evtStr}}</pre>
                </div>`
        })
        class EzApp{
            constructor(){
                this.evtStr
            }
            onChange(evt){
                console.log("sth. occured");
                this.evtStr = JSON.stringify(evt,null,"\t");
            }
        }       

        //具有事件接口的組件 - EzCard
        @Component({
            selector:"ez-card",
            events:["change"]
        })
        @View({
            template : `<div class='ez-card'>
                    My name is <b>{{name}}</b>, 
                    I am from <b>{{country}}</b>.</div>`
        })
        class EzCard{
            constructor(){
                this.name = "Mike";
                this.country = "Sweden";
                this.change = new EventEmitter();
                //模擬觸發事件
                setTimeout(()=>this.change.next({
                    src:"EzCard",
                    desc:"模擬事件"
                }),1000);
            }
        }

        //渲染組件
        bootstrap(EzApp);

五、form

1、NgForm - 表單指令

NgForm指令爲表單元素/form建立一個控件組對象,作爲控件的容器; 而NgControlName指令爲則爲宿主input元素建立一個控件對象,並將該控件加入到NgForm 指令建立的控件組中:

image

局部變量

通過使用#符號,我們創建了一個引用控件組對象(注意,不是form元素!)的局部變量f。 這個變量最大的作用是:它的value屬性是一個簡單的JSON對象,鍵對應於input元素的 ng-control屬性,值對應於input元素的值:

image

聲明指令依賴

NgForm指令和NgControlName指令都包含在預定義的數組變量formDirectives中,所以我們在 組件註解的directives屬性中直接聲明formDirectives就可以在模板中直接使用這些指令了:

//angular2/ts/src/forms/directives.ts
export const formDirectives = CONST_EXPR([
  NgControlName,
  NgControlGroup,

  NgFormControl,
  NgModel,
  NgFormModel,
  NgForm,

  NgSelectOption,
  DefaultValueAccessor,
  CheckboxControlValueAccessor,
  SelectControlValueAccessor,

  NgRequiredValidator
]);

爲示例代碼中的select元素也使用NgControlName指令,並在反饋中顯示所選擇 的搜索類別!

import {Component,View,bootstrap,NgIf} from "angular2/angular2";
        //引入form指令集
        import {formDirectives} from "angular2/forms";

        //EzApp組件
        @Component({selector:"ez-app"})
        @View({
            directives:[formDirectives,NgIf],
            template:`
                <form #f="form" (submit)="search(f.value)">
                    <select ng-control="kw2">
                        <option selected value="web">網頁</option>
                        <option value="news">新聞</option>
                        <option value="image">圖片</option>
                    </select>
                    <input type="text" ng-control="kw">
                    <button type="submit">搜索</button>
                </form>
                <!--給個簡單的反饋-->
                <h1 *ng-if="kw!=''">正在搜索 {{kw}} {{kw2}}</h1>
            `,
            styles:[`form{background:#90a4ae;padding:5px;}`]            
        })
        class EzApp{
            constructor(){
                this.kw = "";
                this.kw2="";
            }
            search(val){
                this.kw = val.kw;
                this.kw2 = val.kw2;
                //假裝在搜索,2秒鐘返回
                setTimeout(()=>this.kw="",2000);
            }
        }



        bootstrap(EzApp);

2、NgControlName - 命名控件指令

如前所述,NgControlName指令必須作爲NgForm或NgFormModel的後代使用, 因爲這個指令需要將創建的控件對象添加到祖先(NgForm或NgFormModel)所創建 的控件組中。

NgControlName指令的選擇符是[ng-control],這意味着你必須在一個HTML元素上 定義ng-control屬性,這個指令纔會起作用。

屬性:ngControl

NgControlName指令爲宿主的DOM對象創建一個控件對象,並將這個對象以ngControl屬性 指定的名稱綁定到DOM對象上:

<form #f="form">
    <input type="text" ng-control="user">
    <input type="password" ng-control="pass">
</form>

在上面的代碼中,將創建兩個Control對象,名稱分別爲user和pass。

屬性/方法:ngModel

除了使用控件組獲得輸入值,NgControlName指令可以通過ngModel實現模型 與表單的雙向綁定:

<form>
    <input type="text" ng-control="user" [(ng-model)]="data.user">
    <input type="password" ng-control="pass" [(ng-model)]="data.pass">
</form>`

ngModel即是NgControlName指令的屬性,也是它的事件,所以下面 的兩種寫法是等價的:

<input type="text" ng-control="user" [(ng-model)]="data.user">
//等價於
<input type="text" ng-control="user" [ng-model]="data.user" (ng-model)="data.user">

3、NgCongrolGroup - 命名控件組

NgControlGroup指令的選擇符是[ng-control-group],如果模板中的某個元素具有這個屬性, Angular2框架將自動創建一個控件組對象,並將這個對象以指定的名稱與DOM對象綁定。

控件組可以嵌套,方便我們在語義上區分不同性質的輸入:

image

和NgControlName指令一樣,NgControlGroup指令也必須作爲NgForm或NgFormModel的 後代使用,因爲這個指令需要將創建的控件組對象添加到祖先(NgForm或NgFormModel)所創建 的控件組中。

<ez-app></ez-app>
    <script type="module">
        import {Component,View,bootstrap,NgIf} from "angular2/angular2";
        import {formDirectives} from "angular2/forms";

        @Component({selector:"ez-app"})
        @View({
            directives:[NgIf,formDirectives],
            template:`
                <form #f="form">
                    <div>基本信息</div>
                    <!--聲明控件組-->
                    <ul ng-control-group="basic">
                        <li>姓名:<input type="text" ng-control="name"></li>
                        <li>地址:<input type="text" ng-control="address"></li>
                        <li>電話:<input type="text" ng-control="telephone"></li>
                    </ul>
                    <div>專業技能</div>
                    <!--聲明控件組-->
                    <ul ng-control-group="expertise">
                        <li>英語:<input type="checkbox" ng-control="english"></li>
                        <li>科技:<input type="checkbox" ng-control="tech"></li>
                        <li>運動:<input type="checkbox" ng-control="sport"></li>
                    </ul>
                </form>
                <!--調試:實時轉儲模型的值-->
                <pre>{{decode(f.value)}}</pre>
            `,
            styles:[`
                div{padding:5px;background:#b3e5fc;color:red;}
                form{background:#e1f5fe;}
                ul{list-style:none;padding:5px;margin:0px;}
                li{line-height:30px;}
            `]
        })
        class EzApp{
            decode(val){
                return JSON.stringify(val,null,"\t");
            }
        }

        bootstrap(EzApp);
    </script>

輸出結果:

{
    "basic": {
        "name": "123",
        "address": "123",
        "telephone": "123"
    },
    "expertise": {
        "english": true,
        "tech": true,
        "sport": true
    }
}

4、NgFormControl - 綁定已有控件對象

與NgControlName指令不同,NgFormControl將已有的控件/Control對象綁定到DOM元素 上。當需要對輸入的值進行==初始化==時,可以使用NgFormControl指令。

下面的代碼中,使用NgFormControl指令將DOM元素綁定到組件EzComp的成員 變量movie上,我們需要在構造函數中先創建這個Control對象:

@View({
    //將輸入元素綁定到已經創建的控件對象上
    template : `<input type="text" [ng-form-control]="movie">`
})
class EzComp{
    constructor(){
        //創建控件對象
        this.movie = new Control("Matrix II - Reload");
    }
}

控件/Control是Angular2中對錶單輸入元素的抽象,我們使用其value屬性,就可以獲得對應的 輸入元素的值。

與NgControlName指令的另一個區別是,NgFormControl不需要NgForm或NgFormModel的祖先。

<ez-app></ez-app>
    <script type="module">
        import {Component,View,bootstrap} from "angular2/angular2";
        import {Control,formDirectives} from "angular2/forms";

        @Component({selector:"ez-app"})
        @View({
            directives:[formDirectives],
            template:`
                <div>
                    <ul>
                        <!--將輸入元素綁定到已經創建的控件對象-->
                        <li>姓名:<input type="text" [ng-form-control]="name"></li>
                        <li>地址:<input type="text" [ng-form-control]="address"></li>
                        <li>電話:<input type="text" [ng-form-control]="telephone"></li>
                        <li>工作:<input type="text" [ng-form-control]="company"></li>
                    </ul>
                </div>
                <!--調試:轉儲模型信息-->
                <pre>{{dump()}}</pre>
            `,
            styles:[`
                form{background:#e1f5fe;}
                ul{list-style:none;padding:10px;margin:0px;}
                li{line-height:30px;}
            `]
        })
        class EzApp{
            constructor(){
                //創建控件對象
                this.name = new Control("Jason");
                this.address = new Control("London U.K.");
                this.telephone = new Control("114");
                this.company = new Control("114");
            }
            dump(){
                //讀取控件對象的值
                var val = {
                    name : this.name.value,
                    address : this.address.value,
                    telephone : this.telephone.value,
                    company : this.company.value
                }
                return JSON.stringify(val,null,"\t");
            }
        }



        bootstrap(EzApp);
    </script>

5、NgFormModel - 綁定已有控件組

NgFormModel指令類似於NgControlGroup指令,都是爲控件提供容器。但區別在於, NgFormModel指令將已有的控件組綁定到DOM對象上:

@View({
    template : `
        <!--綁定控件組與控件對象-->
        <div [ng-form-model]="controls">
            <input type="text" ng-control="name">
            <input type="text" ng-control="age">
        </div>`
})
class EzComp{
    constructor(){
        //創建控件組及控件對象
        this.controls = new ControlGroup({
            name :new Control("Jason"),
            age : new Control("45")
        });
    }
}

NgFormModel指令可以包含NgControlGroup指令,以便將不同性質的輸入分組。

六、 @input和@output

先做個比方,然後奉上代碼比如:

<talk-cmp [talk]="someExp" (rate)="eventHandler($event.rating)">

input:
[talk]=”someExp” 這個標籤可以理解爲一個專門的監聽器,監聽父組件傳遞過來的someExp參數,並存入自身組件的talk變;好像是開了個後門,允許且只允許父組件的someExp進入,一旦進入立刻抓進一個叫talk的牢房,然後==子組件==中就可以通過@Input來定義這個變量talk然後使用它。

output:
(rate)=”eventHandler(event.rating) 這個意思是, 當子組件的click事件被觸發,就執行父組件的eventHandler函數,並把子組件的參數 event.rating傳遞給父組件的eventHandler函數;就好像,當小孩子一哭(執行click事件),他的母親立刻把他抱在懷裏(執行母親的eventHandler),同時母親獲得了小孩子的一些參數(event.rating)

1、@input()

父組件 father.component.ts 提供數據

import {Component} from "@angular/core";
@Component({
    selector: "my-father",
    templateUrl: "father.html"
})
export class FatherComponent {
    data: Array<Object>;
    constructor() {
        this.data = [
            {
                "id": 1,
                "name": "html"
            },
            {
                "id": 2,
                "name": "css"
            },
            {
                "id": 3,
                "name": "angular"
            },
            {
                "id": 4,
                "name": "ionic"
            },
            {
                "id": 5,
                "name": "node"
            }
        ]
    }
}

模板文件 father.html

<h1>父組件</h1>
// 包含子組件, 並使用屬性傳遞數據過去
<my-child [info]="data"></my-child>

子組件 child.component.ts 獲取數據

import {Component, Input} from "@angular/core";
@Component({
    selector: "my-child",
    templateUrl: "child.html"
})
export class ChildComponent {   
    // 使用@Input獲取傳遞過來的數據
    @Input()
    info: Array<Object>;
    constructor() {

    }
}

子組件 child.html模板文件

<ul>
    <li *ngFor="let item of info">
        {{item.name}}
    </li>
</ul>

2、@Output()

子組件three-link.component.ts

  1. 引入
import {Component, OnInit, Output, EventEmitter} from "@angular/core";
  1. 定義輸出變量
export class ThreeLinkComponent {
    province: string;
    // 輸出一下參數
    @Output() provinceOut = new EventEmitter();   
    constructor() {
        this.province = "陝西";
    } 
}
  1. 事件出發,發射變量給父組件
provinceChange() {
    // 選擇省份的時候發射省份給父組件
    this.provinceOut.emit(this.province);
}

父組件模板

<!--三級聯動組件-->
<three-link (provinceOut)="recPro($event)"></three-link>

父組件

// 函數接受子函數傳遞過來的變量, 子函數中emit的時候觸發這個函數。

recPro(event) {
   this.province = event;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章