简述:组件(component)是构成Angular应用的基础和核心.能够这样说,组件用来包装特定的功能,应用程序的有序运行依赖于组件之间的协同工做.css
1. 组件化标准:W3C为了统一组件化的标准方式,提出了Web Component的标准.经过标准化的非侵入方式封装组件,每一个组件包含本身的HTML,CSS,JavaScript代码,
而且不会对页面上其余组件产生影响.Web Component是由一些新技术构成的,还提供了浏览器原声的UI组件标准,因此不须要引入任何外部的依赖.要使用一个已有的
Web Component组件,仅需以下添加一行导入声明,如:
<link rel="import" href="xxxxx.html" />
Web Component标准包括以下四个重要的概念:
1.自定义元素:这个特性容许建立自定义的HTML标记和元素,每一个元素都有属于本身的脚本和样式.
2.模板:模板容许使用<template>标签去预先定义一些内容,但并不随页面加载而渲染,而是能够在运行时使用JavaScript去初始化它.
3.Shadow DOM:经过Shadow DOM能够在文档流中建立一些彻底独立于其余元素的DOM子树,这个特性可让开发者开发一个独立的组件,而且不会干扰到其余DOM元素.
4.HTML导入:一种在HTML文档中引入其余HTML文档的方法,用于导入Web Component的机制.
注意:目前仅有Chrome浏览器对该标准支持最高,其余主流浏览器并未彻底实现Web Component标准.
有关Web Component标准的更多信息可待之后研究,这里并不深究.html
2. Angular组件:在Angular中引入了视图包装(ViewEncapsulation)的概念,容许经过设置ViewEncapsulation.Native选项来使用原生的Shadow DOM.Angular还支持模板,
自定义标签,异步加载组件等.Angular组件是自描述的--能够和宿主元素交互,知道如何以及合适渲染本身,可配置注入服务,有明确的Input和Output定义.全部Angular的组件均可以
独立存在,均可以做为根组件被引导,也能够被路由加载,或者在其余组件中使用.不过一个组件不能单独被启用,它必须被包装到模块(NgModule)中.
组件是Angular应用的最小的逻辑单元,模块则是在组件之上的一层抽象.组件以及其余部件,如指令,管道,服务,路由等均可以被包含到一个模块中.外部引用经过引用这个模块来使用
一系列封装好的功能.bootstrap
3. 建立组件的步骤:
1.从@angular/core中引入Component装饰器.
2.创建一个普通的类,并用@Component修饰它.
3.在@Component中,设置selector自定义标签和template模板.数组
4. 组件装饰器:@Component是TypeScript的语法,它是一个装饰器,任何一个Angular组件类都会用这个装饰器修饰,组件类最终编译成的JavaScript代码以下:
var ContactItemComponent=(function(){
function ContactItemComponent(){};
ContactItemComponent=__decorate([core_1.Component({
selector:'contact-item',
template:`
<div>
<p>xzm</p>
<p>13648301556</p>
</div>
`
})__metadata(`design:paramtypes`,[])
],ContactItemComponent);
return ContactItemComponent;
}());
其中,Angular的@Component会被转换成一个__decorate()方法,元数据的定义经过core_1.Component传入,将ComtactItemComponent这个类装饰器来,使得
ContactItemComponent具备装饰器里定义的元数据属性.浏览器
5. 组件元数据:
5.1 selector:是用于定义组件在HTML代码中匹配的标签,它将称为组件的命名标记.一般状况下都须要设置selector,特俗状况能够忽略,不指定时设置默认为匹配div元素.
selector的命名方式建议使用"烤肉串式"命名,即采用小写字母并以-分隔.
5.2 template是为组件指定一个内联模板.内联模板建议使用ES6的多行字符串``(两个反引号),这样可建立多行.
5.3 templateUrl:是为组件指定一个外部模板的URL地址.
5.4 styles:是为组件制定内联样式,如:
@Component({
styles:[`
li:last-child{
border-bottom:none;
}
`]
})
5.5 styleUrls:是为组件指定一系列用于该组件的外联样式表文件,如:
@Component({
styleUrls:['app/list/item.component.css']
})
注意:styles和styleUrls容许同时指定.同时指定,styles中的样式会被先解析,也就是styles的样式会被styleUrls的覆盖.app
6. 模板:每一个组件都必须设置一个模板,angular才能将组件内容渲染到DOM上,这个DOM元素就是宿主元素.组件能够与宿主元素交互,交互的形式以下:
1.显示数据
2.双向数据绑定
3.监听宿主元素事件以及调用组件方法.
6.1 显示数据:可使用插值语法{{}}来显示组件的数据.
6.2 双向数据绑定,使用[(ngModule)]='property'的语法.
6.3监听宿主元素事件及调用组件方法:()是Angular提供的事件绑定语法糖,经过(eventName)的方式能够轻易地响应UI事件.框架
7. 组件和模块:Angular提供了@NgModule装饰器来建立模块,一个应用能够有多个模块,但只有一个根模块(RootModule),其余模块叫做特性模块(FeatureModule).根模块是启动应用
的入口模块,根模块必须经过bootstrap元数据来指定应用的根组件,而后经过bootstrapModule()方法来启动应用.
7.1 NgModule主要的元素居以下:
1.declarations:用于指定属于这个模块的是视图类(View Class),即指定那些部件组成了这个模块.Angular又组件,指令和管道三种视图类,这些视图类只能属于一个模块,必须
注意不能再次声明属于其余模块的类.
2.exports:导出视图类.当该模块被引入到外部模块时,这个属性指定了外部模块可使用该模块的那些视图类,因此它的值类型跟declarations一致.
3.imports:引入该模块依赖的其余模块或路由,引入后模块里的组件模板才能引用外部对应的组件,指令和管道.
4.providers:指定模块依赖的服务,引入后该模块中的全部组件均可以使用这些服务.
7.2 导出视图类以及导入依赖模块:有时候模块中的组件,指令或管道,可能也会在其余模块中使用,这时可使用exports元数据对外暴露这些组件,指令或管道.而相对应的,若是在一个模块
中想要使用其余模块对外暴露的组件,服务等,除了须要在模块的文件头使用import from导入模块,同时还要在NgModule的元数据import中为该模块制定要导入的依赖模块,这其中的
两个导入(import),前一个是TypeScript的模块导入,后一个是Angular框架的模块导入,但愿这里不要混淆了.
7.3 服务引入:
引入服务有两种方式:
1.经过@NgModule的providers引入,经过它引入的服务,在模块的全部组件均可以使用.
2.经过@Component的providers引入,经过它引入的服务,在组件及其子组件中均可以共用这些引入的服务.异步
8. 组件交互:组件交互就是组件经过必定的方式来访问其余组件的属性或方法,从而实现数据的双向流通.组件交互有不少种方式,非父子关系的组件可经过服务来实现数据交互通讯.
8.1 组件的输入输出属性:Angular除了提供@Input和@Output装饰器语法来处理组件数据的流入流出外,还提共了在组件的元数据中使用inputs,outputs来设置输入输出属性,设置的值必须
为字符串数组,元素的名称须要和成员变量相对应.
如:
@Component({
inputs:['contact'], //'contact' 匹配成员变量contact
outputs['routerNavigate']
})
8.2 父组件向子组件传递数据:父组件的数据是经过子组件的输入属性流入子组件,在子组件完成接收或者拦截,从而实现了数据由上而下的传递.
Angular会从根组件开始启动,并解析整棵组件树,数据以由上而下的方式流向下一级子组件.不过须要注意的是,目标组件的属性必须经过输入属性(@Input)明确的标记才能接收到来自
父组件的数据.
8.3 拦截输入数据:子组件能够拦截输入属性的数据并进行相应的处理.
有以下两种方式:
1.setter拦截输入属性:getter和setter一般一块儿使用,用来对属性进行相关约束.它们提供了对属性读写的封装,使代码结构更清晰,更具可扩展性.setter可对属性进行再封装
处理,对复杂的内部逻辑经过访问权限控制来隔绝外部调用,以免外部的错误调用影响到内部的状态.同时也要把内部复杂的逻辑结构封装成高度抽象可被简单调用的属性,再经过
getter返回要设置的属性值,方便调用者使用.
setter拦截器示例,如:
@Component({
selector:'list-item',
template:`
<div>
<label class='contact-name'>{{contactObj.name}}</label>
</div>
`
})
exports class ListItemComponent impl OnInit{
_contact:object={};
@Input
set contactObj(contact:object){
this.)contact.name=(contact.name && contact,name.trim()) || '名字为空!';
}
get contactObj(){return this._contact;}
}
这里经过settet的方式设置一个contactObj属性对象,其做用是经过对@Input修饰符获取的数据contact(父组件传入的)进行二次处理,再经过getter返回这个contactObj对象.
2.ngOnChanges监听数据变化:ngOnChanges用于及时响应Angular在属性绑定中发生的数据变化,该方法接收一个对象参数,包含当前值和变化前的值.在ngOnInit以前,或者当数据
绑定的输入值发生变化时会触发.
ngOnChange方法接收一个参数,该参数类型是SimpleChanges类.它是Angular的一个基础类,用于处理数据的先后变化,其中包含两个重要成员,分别是previousValue和
currentValue,previousValue是获取变化前的数据,而currentValue是获取变化后的数据.
如:
//父组件代码 detail.component.ts
import { Component } from '@angular/core';
@Component({
selector:'detail',
template:`
<a class='edit' (clikc)='editContact()'>编辑</a>
<change-log [contact]='detail'></changelog>
`
})
export class DetailComponent implements OnInit{
detail:any={};
//...此处省略给 detail获取数据的代码
//完成联系人编辑修改
editContact(){
//...
this.detail=data;
}
}
//子组件代码 changelog.component.ts
import { Component,Input,Onchanges,SimpleChanges } from '@angular/core';
@Component({
selector:'change-log',
template:`
<h4>change log</h4>
<ul>
<li *ngFor="let change of changes">{{}change}</li>
</ul>
`
})
export clas ChangeLogComponent implements OnChanges{
@Input() contact:any={};
changes:string[]=[];
ngOnChange(changes:{[propKey:string]:SimpleChanges}){ // 有关{ [propKey:string]:SimpleChanges }代码的解释请看下面说明
let log:string[]=[];
for(let propName in changes){
let changeProp=changes[propName],
from =JSON.stringify(changeProp.previousValue),
to =JSON.stringify(changeProp.currentValue);
log.push(`${propName} changed from ${from} to ${to}`);
}
this.changes.push(log.join(','));
}
}
注:上面代码中又一行代码{[propKey:string]:SimpleChanges},这是做为一个TypeScript类来定义ngOnChanges方法的参数类型,那么该代码表明的是什么类呢?
直接说,其实就是一个双列集合,或者叫字典,如同Jave里面的Map集合,C#里面的Directory.而在TypeScript这里,字典类的定义比较麻烦,属于可索引类型接口,索引为
字符串类型,也就是[propKey:stirng],其后":SimpleChanges"定义了该字典类型中的元素类型必须为SimpleChanges类型,因为TypeScript的字典是经过接口的语
法方式实现的,因此这里最后又用了一对花括号把它包裹起来,这代表它是一个匿名的接口类型,也就是匿名的可索引接口类型.
8.4 子组件向父组件传递数据:使用事件传递是子组件向父组件传递数据最经常使用的方式.子组件须要实例化一个用来订阅和触发自定义事件的EventEmitter类,这个实例对象是一个由
装饰器@Output修饰的输出属性,当有用户操做行为发生时该事件会被触发,父组件则经过事件绑定的方式来订阅来自子组件触发的事件,即子组件触发具体的事件(自定义)会被其父
组件订阅到.
示例:
//父组件,收藏联系功能
import { Component } from '@angular/core';
@Component({
selector:'collection',
template:`
<contact-collect [contact]="detail" (onCollect)="collectTheContact($event)"></contact-collect>
`
})
export class CollectionComponent implements OnInit{
detail:any={};
collectTheContact(){
this.detail.collection== 0 ? this.detail.collection= 1 : this.detail.collection=0;
}
}
父组件CollectionComponent经过绑定自定义事件onCollect订阅来自子组件触发的事件.当有来自子组件对应的事件被触发,在父组件中可以监听到该事件.
注意:这里的具体业务逻辑功能是在父组件的collectTheCOntact()中实现的.
示例:
//子组件
import { Component ,EventEmitter,Input,Output } from '@angular/core';
@Component({
selector:'contact-collect',
template:`<i [ngClass]='{ collected:contact.collecttion }' (click)='collectTheContact()'>收藏</i>`
})
export class ContactCollectComponent{
@Input() contact:any={};
@Output() onCollect=new EventEmitter<boolean>();
collectTheContact(){
this.onCollect.emit();
}
}
经过输出属性@Output将数据流向父组件,在父组件完成事件的监听,以此来实现从子组件到父组件的数据交互.这个过程父组件数据流入子组件不太同样,其实一个父组件主动流入方式,
子组件的数据是在一些特定的时候或者条件下,由子组件主动发起,父组件被动触发(订阅到)事件来的获得子组件传递的数据.
8.5 经过局部变量实现数据交互:经过建立模板局部变量的方式,来实现父组件与子组件数据交互,即在父组件的模板中为子组件建立一个局部变量,那么这个父组件能够经过这个局部来获取
子组件公共成员变量和函数的权限.模板局部变量的做用域范围仅存在于该模板局部变量的子组件.
示例代码:
import { } from '@angular/core';
@Component({
selector:'collection',
template:`
<contact-collect (click)='collect.collectTheContact()' #collect></contact-collect>
`
})
export class CollectionComponent{}
在父组件模板中的子组件标签上绑定一个局部变量,以#号标记,以此来获取子组件类的实例对象.如上代码#collect就是绑定子组件类的实例对象.
模板局部变量的方式是在子组件方法中实现具体的业务逻辑,和父组件订阅子组件自定义事件的方式实现业务逻辑地方正好相反.
8.6 @ViewChild实现数据交互:使用模板变量只能在模板中使用,不能直接在父组件类里使用,这又必定的局限性.当父组件须要获取子组件中的变量,方法的读写权限时,能够经过@ViewChild
注入的方式来实现.
组件中元数据ViewChild的做用是声明对子组件元素的实例引用,它提供了一个参数来选择将要引用的组件元素,这个参数能够是一个类的实例,也能够是一个字符串.
1.参数为类实例,表示父组件将绑定一个指令或者子组件实例.
2.参数为字符串类型,表示将起到选择器的做用,即至关于在父组件中绑定一个模板局部变量,获取到子组件的一份实例对象的引用.
示例如:
import { } from '@angular/core';
@Component({
selector:'collection',
template:`
<contact-collect (click)='collectTheContact()'></contact-collect>
`
})
export class CollectionComponent{
@ViewChild(ContactCollectComponent) contactCollect:ContactCollectComponent; //子组件类变量ide
collectTheContact(){
this.contactCollect.collectTheContact();
}
}函数
9. 组件内容嵌入:内容嵌入(ng-content)是组件的一个高级功能特性,使用组件的内容嵌入特性能很好的扩从组件的功能,方便代码的复用.内容嵌入一般用来建立可复用的组件,典型的例子
是模态对话框或导航栏.
示例如:
import { Component } from '@angular/core';
@Component({
selector:'example-content',
template:`
<div>
<h4>ng-content 示例</h4>
<div style="backgroud-color:gray;padding:5px;margin:2px;">
<ng-content selector="header"></ng-content>
</div>
</div>
`
})
export class NgContentExampleComponent{}
在上面代码中,使用了<ng-content>标签,这个标签使用来渲染组件嵌入内容的.在<ng-content>中有个selector='header'属性,用于匹配内容,并填充到ng-content中.
有了以上带有内容嵌入的组件,那么就能够在以下的跟组件中使用该组件,例如:
import { Component } from '@angular/core';
@Component({
selector:'app',
template:`
<example-content>
<header>组件动态内容嵌入部分,能够替换该组件中的ng-content标签中的内容</header>
</example-content>
`
})
export class NgContengAppComponent{}
上面用到的selector是一个选择器,与CSS选择器相似,selector='header',表示匹配组件模板调用中<header>标签,固然还有其余的匹配方式,以下:
1.selector='.class-select' :经过标签上的某个CSS类来匹配.
2.selector='[name=footer]' :经过标签上的某个属性值来匹配.
10. 组件的生命周期:组件的生命周期由Angular内部管理,从组件的建立,渲染,到数据变更事件的触发,再到组件从DOM中移除,Angular都提供了一系列的钩子. 10.1 生命周期的钩子:开发者能够实现一个或者多个生命周期钩子(接口),从而在生命周期的各阶段作出适当的处理.这些钩子包含在@Angular/core中, 如下是组件经常使用的生命周期钩子: 1.ngOnChanges:它是用来响应组件输入值(经过@Input装饰去显式指定的变量)发生变化时触发的事件,接收一个SimpleChanges对象,包含当前值和变化前值, 该方法在ngOnInit以前. 2.ngOnInit:用于数据绑定输入性以后初始化组件,该钩子方法会在第一次ngOnChanges以后被调用.使用ngOnInit有如下两个重要缘由: a.组件构造后不久须要进行复杂的初始化. b.须要在输入属性设置完成以后才构建组件. 3.ngDoCheck:用于变化监测,该钩子方法会在每次变化监测发生时被调用.每个变化监测周期内,无论数据值是否发生变化,ngDoCheck都会被调用,该方法须要慎用, 如鼠标移动触发mousemove事件 4.ngAfterContentInit:在组件使用<ng-content>将外部内容嵌入到组件视图后就会调用它,它在第一次ngDoCheck执行后调用,且只执行一次. 5.ngAfterContentChecked:在组件使用了<ng-content>自定义内容的状况下,Angular在这些外部内容嵌入到组件视图后,或者每次变化监测的时候都会调用 ngAfterContentChecked. 6.ngAfterViewInit:会在Angular建立了组件的视图及其子组件视图以后被调用. 7.ngAfterViewChecked:在Angular建立了组件的视图及其子组件视图以后被调用一次,而且在每次子组件变化监测时也会被调用. 8.ngOnDestroy:在销毁指令/组件以前触发.那些不会被垃圾回收器自动回收的资源都应当在ngOnDestory中手动销毁.