核心提示:注解/Annotation- @Component 和 @View给一个类加注解,等同于设置这个类的annotation属性//注解写法@Component({selector:ez-app})cla...
注解/Annotation
- @Component 和 @View 给一个类加注解,等同于设置这个类的annotation属性
//注解写法 @Component({selector:"ez-app"}) class EzApp{...} //以上代码等同于 class EzApp{...} EzApp.annotations = [new Component({selector:"ez-app"})];
注解在编译时仅仅被放在annotation里,编译器并不进行解释展开,这个解释的工作是 Angular2完成的 图片引用---------->annotation.jpg - 模版 - 内联模版
@View({ template : `<h1>hello</h1> <h2>world</h2>` })
- 外部模版
<!--module.html文件中--> <h1>hello</h1> <h2>world</h2> <!--模版调用--> @View({ templateUrl : "module.html" })
- directive使用组件 图片引用-----> component-template.jpg
// class EzApp{EzCard} @Component({selector:"ez-app"}) @View({ directives:[EzCard], template:` <p class="ez-app"> <h1>EzApp</h1> <ez-card></ez-card> </p>` }) class EzApp{} // class EzCard @Component({selector : "ez-card"}) @View({ template : ` <p class="ez-card"> <h1>EzCard</h1> </p>` }) class EzCard{} bootstrap(EzApp);
- {{model}}文本插值
//程序实例 @Component({selector : "ez-app"}) @View({ template:` <p> <h1>{{title}}</h1> <p> <span>{{date}}</span> <span>{{source}}</span> </p> <p>{{content}}</p> </p> }) class EzApp{ constructor(){ this.title = "世界,你好!"; this.date = "2016-10-17 10 : 10 : 10"; this.source = "江西-南昌" this.content = "曾经沧海难为水,除却巫山不是云"; } } bootstrap(EzApp);
- [property]绑定属性 引用图片prop-bind.jpg
// 注意事项 //使用前缀 bind-来进行绑定属性 @View({template:`<h1 bind-text-content="title"></h1>`}) // 使用在属性前加[]进行绑定 @View({template:`<h1 [text-content]="title"></h1>`}) //使用中括号[]绑定属性时应注意以下事项 { //错误,Angular2将找不到表达式 Hello,Angular2 @View({template:`<h1 [text-content]="Hello,Angular2"></h1>`}) //正确,Angular2识别出常量字符串表达式 'Hello,Angular2' @View({template:`<h1 [text-content]="'Hello,Angular2'"></h1>`}) //正确,Angular2识别出常量字符串作为属性textContent的值 @View({template:`<h1 text-content="Hello,Angular2"></h1>`}) }
- (event)监听事件 图片引用event-bind.jpg
//代码实例 前缀on-进行事件绑定 @View({template : `<h1 on-click="onClick()">HELLO</h1>`}) // 为属性添加 括号() @View({template : `<h1 (click)="onClick()">HELLO</h1>`})
- #var局部变量
@View({ template : <h1 #h_title>我是标题</h1> <button (click)= "h_title.contentText = '我改变了'">改变标题</button> })
组件开发-模版的逻辑控制
- 使用条件逻辑 有时我们需要模板的一部分内容在满足一定条件时才显示,比如右边示例中的EzReader组件, 对于试用用户,它将在正文之上额外显示一个广告: 引用图片---->ngif.jpg 这是指令NgIf发挥作用的场景,它评估属性ngIf的值是否为真,来决定是否渲染 template元素的内容:
@View({ template: ` <!--判断trial是否为true--> <template [ng-ig]="trial==true"> <img src = "abc.jpg"/> </template> <!--以下是正文--> <pre>... ` }) //Angular2同时提供了两种语法糖,让NgIf写起来更简单,下面的两种书写方法和上面 的正式语法是等效的 //使用template attribute <img src="ad.jpg" template="ng-if tiral==true"> //使用*前缀 <img src="ad.jpg" *ng-if="tiral==true"> //较好实例说明 <!doctype html> <html> <head> <meta charset="utf-8"> <title>Interpolation</title> <script type="text/javascript" src="lib/system@0.16.11.js"></script> <script type="text/javascript" src="lib/angular2.dev.js"></script> <script type="text/javascript" src="lib/system.config.js"></script> </head> <body> <ez-app></ez-app> <script type="module"> //引入NgIf类型 import {Component,View,bootstrap,NgIf} from "angular2/angular2"; @Component({selector:"ez-app"}) @View({ directives:[EzReader], template:` <ez-reader [trial]="true"></ez-reader> ` }) class EzApp{} @Component({ selector : "ez-reader", properties:["trial"] }) @View({ directives:[NgIf], template : ` <img [src]="banner" *ng-if="trial==true"> <pre>{{content}}</pre>` }) class EzReader{ constructor(){ var self = this; this._trial = true; this.banner = "img/banner.jpg"; this.content = ` “没关系,我已经没有放射性了。”史强对坐在旁边的汪森说,“这两天,我让人家像洗面口袋似的翻出来洗了个遍。 这次会议本来没安排你参加,是我坚决要求请你来的,嘿。我保准咱哥俩这次准能出风头的。” 史强说着,从会议桌上的烟灰缸中拣出一只雪茄屁股,点上后抽一口,点点头,心旷神怡地把烟徐徐吐到对面与会 者的面前,其中就有这支雪茄的原主人斯坦顿,一名美国海军陆战队上校,他向大史投去鄙夷的目光。 这次与会的有更多的外国军人,而且都穿上了军装。在人类历史上,全世界的武装力量第一次面对共同的敌人。 常伟思将军说:“同志们,这次与会的所有人,对目前形势都有了基本的了解,用大史的话说,信息对等了。人类 与外星侵略者的战争已经开始,虽然在四个半世纪后,我们的子孙才会真正面对来自异星的三体人侵者,我们现在与之 作战的仍是人类;但从本质上讲,这些人类的背叛者也可以看成来自地球文明之外的敌人,我们是第一次面对这样的敌人。 下一步的作战目标十分明确,就是要夺取‘审判日’号上被截留的三体信息,这些信息,可能对人类文明的存亡具有重要 意义。 `; } } bootstrap(EzApp); </script> </body> </html>
- 使用分支逻辑 如果组件的模板需要根据某个表达式的不同取值展示不同的片段,可以使用NgSwitch系列指令 来动态切分模板。比如右边示例中的广告组件EzPromotion,需要根据来访者性别的不同推送 不同的广告 引用图片ngswitch.jpg - NgSwitch NgSwitch指令可以应用在任何HTML元素上,它评估元素的ngSwitch属性值,并根据这个值 决定应用哪些template的内容(可以同时显示多个分支)
<ANY [ng-switch]="expression">...</ANY>
- NgSwitchWhen NgSwitchWhen指令必须应用在NgSwitch指令的子template元素上,它通过属性ngSwitchWhen指定一个表达式, 如果该表达式与父节点的NgSwitch指令指定的表达式值一致,那么显示这个template的内容
<ANY [ng-switch]="..."> <!--与变量比较--> <template [ng-switch-when]="variable">...</template> <!--与常量比较--> <template ng-switch-when="constant">...</template> </ANY>
- NgSwitchDefault NgSwitchDefault指令必须应用在NgSwitch指令的子template元素上,当没有NgSwitchWhen指令匹配 时,NgSwitch将显示这个template的内容:
<ANY [ng-switch]="..."> <template ng-switch-default>...</template> </ANY>
*分别类似C语言中的switch case default*
[ng-switch]="sss" ----->类似switch(sss) ng-switch-when = "aaa" ----> 类似case aaa: ng-switch-default = "bbb" ---->类似default:
- NgFor循环逻辑 - 迭代 NgFor指令应用在template元素上,对ngForOf属性指定的数据集中的每一项 实例化一个template的内容
<template ng-for [ng-for-of]="items" > <li>----------</li> </template>
- 使用数据项 我们还可以为数据集的每一项声明一个局部变量,以便在模板内引用
<template ng-for [ng-for-of]="items" #item> <li>{{item}}</li> </template>
- 使用数据项索引 有时还需要数据项在数据集中的索引,我们也可以为数据集的每一项的索引声明一个 局部变量,以便在模板内引用
<template ng-for [ng-for-of]="items" #item #i="index"> <li>[{{i+1}}] {{item}}</li> </template>
- 语法糖 与NgIf类似,Angular2也为NgFor提供了两种语法糖
//使用template attribute <ANY template="ng-for #item of items;#i=index">...</ANY> //使用*前缀 <ANY *ng-for="#item of items;#i=index">...</ANY> //毫无疑问,应当尽量使用*ng-for的简便写法,这可以提高模板的可读性。
组件开发-为模版应用样式
1. styles -- 设置模版样式 1. 内联样式:
@View({ styles:[ `h1{background:#4dba6c;color:#fff}` ] })
2. 外联样式:
<!-- myStyle.css --> h1{ background:#4dba6c;color:#fff; } <!--注解使用styleUrls--> @View({ styleUrls:["myStyle.css"] })
2. ShadowDom -- 封装私有样式 1. 全局仿真策略
<style> h1{ color:red; } </style> <body> <h1>hello world</h1> <h1>second hello world</h1> </body> <!-- 所有h1标签都会相应style中的样式。支持所有浏览器 -->
2. 作用域仿真策略
<style> h1[tab_h1]{ color:red; } </style> <body> <h1 tab_h1>hello world</h1> <h1>second hello world</h1> </body> <!-- 只有带有tab_h1属性的h1标签才会响应属性。支持所有浏览器 -->
3. 原生策略 图片引用sd.strategy.jpg
<!-- 该策略需要浏览器支持ShadowDom --> @Component({selector:"ez-app"}) @View({ template:"<h1>我是H1,我在组件内</h1>", styles:["h1{color:red}"] }) class EzApp{} <!-- 选择EmulatedScopedShadowDomStrategy --> var injectables = [ bind(ShadowDomStrategy) .toFactory((doc) => new EmulatedScopedShadowDomStrategy(doc.head), [DOCUMENT_TOKEN]) ]; /* 以上的同等写法如下 bind(ShadowDomStrategy).toFactory(function(doc){ return new NativeShadowDomStrategy(doc.head); },[DOCUMENT_TOKEN]) */ bootstrap(EzApp,injectables);
组件开发 – 属性和事件
1. 属性声明 - 暴露成员变量 在Angular2中为组件增加属性接口非常简单,只需要在Component注解的 properties属性中声明组件的成员变量就可以了
//EzCard @Component({ properties:["name","country"] }) //EzApp @View({ directives : [EzCard], template : "<ez-card [name]="'雷锋'" [country]="'中国'"></ez-card>" })
2. 事件声明 - 暴露事件源
//EzCard @Component({ events:["change"] }) class EzCard{ constructor(){ this.change = new EventEmitter(); } } //EzApp @View({ template : "<ez-card (change)="onChange()"></ez-card>" }) <? 此处有待详细说明 ?>
组件开发 – 表单输入
1. NgForm - 表单指令
//EzApp组件 @Component({selector:"ez-app"}) @View({ directives:[formDirectives,NgIf], template:` <!-- 在form中声明submit --> <form #f="form" (submit)="search(f.value)"> <select> <option 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}} ...</h1> `, styles:[`form{background:#90a4ae;padding:5px;}`] }) class EzApp{ constructor(){ this.kw = ""; } search(val){ this.kw = val.kw; //假装在搜索,2秒钟返回 setTimeout(()=>this.kw="",2000); } } bootstrap(EzApp);
2. NgControlName - 命名控件指令 1. 属性ngControl
<form #f="form"> <input type="text" ng-control="user"> <input type="password" ng-control="pass"> </form>
2. 属性/方法: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 - 命名控件组 图片引用ngcg.jpg
@Component({selector:"ez-app"}) @View({ directives:[NgIf,formDirectives], template:` <form #f="form"> <p>基本信息</p> <!--声明控件组--> <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> <p>专业技能</p> <!--声明控件组--> <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:[` p{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);
4. NgFormControl - 绑定已有控件对象
@View({ //将输入元素绑定到已经创建的控件对象上 template : `<input type="text" [ng-form-control]="movie">` }) class EzComp{ constructor(){ //创建控件对象 this.movie = new Control("Matrix II - Reload"); } }实例代码
@Component({selector:"ez-app"}) @View({ directives:[formDirectives], template:` <p> <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> </ul> </p> <!--调试:转储模型信息--> <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"); } dump(){ //读取控件对象的值 var val = { name : this.name.value, address : this.address.value, telephone : this.telephone.value } return JSON.stringify(val,null,"\t"); } } bootstrap(EzApp);
5. NgFormModel - 绑定已有控件组
//简单示例 @View({ template : ` <!--绑定控件组与控件对象--> <p [ng-form-model]="controls"> <input type="text" ng-control="name"> <input type="text" ng-control="age"> </p>` }) class EzComp{ constructor(){ //创建控件组及控件对象 this.controls = new ControlGroup({ name :new Control("Jason"), age : new Control("45") }); } }实例项目
@Component({selector:"ez-app"}) @View({ directives:[formDirectives], template:` <p [ng-form-model]="controls"> <ul> <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> </p> <pre>{{dump()}}</pre> `, styles:[` p{background:#e1f5fe;} ul{list-style:none;padding:10px;margin:0px;} li{line-height:30px;} `] }) class EzApp{ constructor(){ this.controls = new ControlGroup({ name : new Control("Jason"), address : new Control("London U.K."), telephone : new Control("114") }); } dump(){ return JSON.stringify(this.controls.value,null,"\t"); } } bootstrap(EzApp);
组件开发 – 调用服务
1. 服务 - 封装可复用代码 在Angular2中,服务用来封装可复用的功能性代码。比如Http服务,封装了ajax 请求的细节,在不同的组件中,我们只需要调用Http服务的API接口就可以给组件增加 ajax请求的功能了
//定义一个简单的算法服务 class EzAlgo{ add(a,b) { return a+b; } sub(a,b) { return a-b; } } //组件定义 @Component({ selector : "ez-app" }) @View({ directives:[formDirectives], template : ` <form> <input type="text" ng-control="a" [(ng-model)]="a"> + <input type="text" ng-control="b" [(ng-model)]="b"> = {{add()}} </form>` }) class EzApp{ constructor(){ this.a = 37; this.b = 128; //实例化服务对象 this.algo = new EzAlgo(); } add(){ var a = this.a, b = this.b; return this.algo.add(a,b); } } bootstrap(EzApp);
2. 注入 - appInjector
//简单叙述 @Component({ selector : "ez-app", //声明依赖 appInjector : [EzAlgo] }) @View(...) class EzApp{ //Angular2框架负责注入对象 constructor(@Inject(EzAlgo) algo){ //已经获得EzAlgo实例了! } } //不使用appInjector,使用bootstrap也可以实现注入 bootstrap(EzApp,EzAlgo);//EzAlgo,类名
3. 注入一个复杂的服务
<? 此处还需仔细思考 ?> //引入Http相关预定义类型 import {Http,httpInjectables} from "angular2/http"; //EzApp组件 @Component({ selector:"ez-app", //注入Http依赖项集合 appInjector:[httpInjectables] }) @View({ directives:[NgFor], template:` <p *ng-for="#album of band.albums"><img [src]="album.cover"></p> `, styles:[` img{height:200px;width:200px;} p{float:left;margin:10px;} `] }) class EzApp{ //注入Http实例对象 constructor(@Inject(Http) http){ this.band = {}; http.get("api/music.json")//GET请求 .map(rsp=>rsp.json())//将相应转化为JSON格式的数据集 .subscribe(data=>this.band=data[0]);//设置band变量为数据集的第一项 } } bootstrap(EzApp);
组件路由 – 原理与应用
1. 路由初体验
//引入路由相关类型定义 import {LocationStrategy,RouteConfig,RouterOutlet,RouterLink,Router,routerInjectables} from "angular2/router"; //EzApp组件 : 路由配置与执行 @Component({selector:"ez-app"}) @View({ directives:[RouterOutlet,RouterLink], template : ` <nav> <!--声明路由入口--> <b router-link="video">video</b> | <b router-link="music">music</b> </nav> <main> <!--声明路由出口--> <router-outlet></router-outlet> </main> ` }) //路由配置 @RouteConfig([ {path:"/video", component:EzVideo,as:"video"}, {path:"/music", component:EzMusic,as:"music"} ]) class EzApp{ constructor(@Inject(LocationStrategy) ls){ ls.pushState = function(){};//simple hack for crash bug. } } //EzVideo组件 @Component({selector:"ez-video"}) @View({ template : ` <h3>视频:轻松一刻</h3> <embed allowscriptaccess="always" height="482" pluginspage="https://get.adobe.com/cn/flashplayer/" flashvars="list=http%3A%2F%2Fus.sinaimg.cn%2F002TFmqWjx06TNSxqGuz0504010000220k01.m3u8%3FKID%3Dunistore%2Cvideo%26Expires%3D1437020410%26ssig%3DNnoWLSjm2v&fid=1034:882b42973162c7ae7acef23bfaccbe8c&logo=2&uid=1971237631" allowfullscreen="true" width="482" quality="high" src="https://js.t.sinajs.cn/t5/album/static/swf/video/player.swf?v1423545453000454353" type="application/x-shockwave-flash" wmode="transparent"> ` }) class EzVideo{} //EzMusic组件 @Component({selector:"ez-music"}) @View({ template : ` <h3>音乐:平凡之路</h3> <embed src="https://player.yinyuetai.com/video/player/2094298/a_0.swf" quality="high" width="480" height="334" align="middle" allowScriptAccess="sameDomain" allowfullscreen="true" type="application/x-shockwave-flash"></embed> ` }) class EzMusic{} //注入路由功能依赖项 bootstrap(EzApp,[routerInjectables]);
2. 路由 - 应用步骤 1.配置路由
router.config([ {path:"/video", component:EzVideo}, {path:"/music", component:EzMusic} ]) //上面的代码中,配置了两条路由: //1.如果用户请求路径为/video,那么将在路由出口中激活组件EzVideo //2.如果用户请求路径为/music,那么将在路由出口中激活组件EzMusic
2. 设置路由出口
@View({ directives:[RouterOutlet], //设置路由出口 template : `<router-outlet></router-outlet>` }) class EzApp{...}
3. 执行路由
@View({ template : ` <span (click)="router.navigate('/video')">video</span> | <span (click)="router.navigate('/music')">music</span> <router-outlet></router-outlet>` }) //我们向navigate()方法传入的路径,就是我们通过config()方法配置的路径。这样, Router就根据这个路径,找到匹配的组件,在RouterOutlet上进行激活。
4. 开始路由前,需要的准备工作
// 1. 引用路由包 <script type="text/javascript" src="lib/router.dev.js"></script> // 2. 引入路由相关的预定义类型 import {LocationStrategy,Router,RouterOutlet,routerInjectables} from "angular2/router"; // 3. 声明路由相关依赖类型 bootstrap(EzApp,[routerInjectables]); //简易项目代码 import {Inject,Component,View,bootstrap} from "angular2/angular2"; import {LocationStrategy,RouterOutlet,Router,routerInjectables} from "angular2/router"; @Component({selector:"ez-app"}) @View({ directives:[RouterOutlet], template : ` <nav> <b (click)="go('/video')">video</b> | <b (click)="go('/music')">music</b> </nav> <main> <!--声明路由出口--> <router-outlet></router-outlet> </main> ` }) class EzApp{ constructor(@Inject(Router) rt,@Inject(LocationStrategy) ls){ ls.pushState = function(){}; this.router = rt; //配置路由 this.router.config([ {path:"/video", component:EzVideo}, {path:"/music", component:EzMusic} ]); } go(path){ //根据给定的url,选中组件并在outlet中激活 this.router.navigate(path); } } @Component({selector:"ez-video"}) @View({ template : ` <h1>I LOVE THIS VIDEO!</h1> ` }) class EzVideo{} @Component({selector:"ez-music"}) @View({ template : ` <h1>THAT'S FANTASTIC MUSIC!</h1> ` }) class EzMusic{} bootstrap(EzApp,[routerInjectables]);
3. RouteConfig - 路由配置注解 
@RouteConfig([ {path:"/video", component:EzVideo}, {path:"/music", component:EzMusic} ]) class EzApp{...}
4. RouterLink - 路由入口指令
//RouterLink并不能直接使用路由项的路径,router-link属性 的值是一个路由项的别名,我们需要在路由配置时通过as属性,为路由项设定别名 @RouteConfig([ {path:"/video", component:EzVideo , as:"video"}, {path:"/music", component:EzMusic , as:"music"} ]) @View({ directives:[RouterOutlet,RouterLink] template : `<nav> <b router-link="video">video</b> | <b router-link="music">music</b> </nav> <router-outlet></router-outlet>` })
5. RouteRegistry - 路由注册表  1. 匹配表/matchers
@RouteConfig([ {path:"/video", component:EzVideo}, {path:"/music", component:EzMusic} ])
2. 重定向表/redirects
@RouteConfig([ {path:"/video", component:EzVideo}, {path:"/music", component:EzMusic}, {path:"/test", redirectTo:"/video"} ])
3.名称表/names
@RouteConfig([ {path:"/video", component:EzVideo,as:"video"}, {path:"/music", component:EzMusic,as:"music"} ])