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;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值