组件
# 装饰器
@Component
可以把一个类标记为 Angular 组件,常用元数据配置如下,此外还继承Directive,
选项 | 用法 |
---|---|
changeDetection | 更改变更策略 |
templateUrl | 模板URL |
template | 组件内模板 |
styleUrls | 样式URl,string:[] |
styles | 内联样式,string:[] |
animations | 动画相关 |
encapsulation | 模板和css的样式封装策略 |
# 组件周期
组件的声明周期需要实现@angular/core
库中的接口,可以对组件
或指令
进行操作,以下为常用的周期,按照执行顺序排序
# 常用周期
钩子 | 用法 |
---|---|
ngOnchanges | 当有输入属性发生改变时,可执行多次 |
ngOnInit | 当初始化完成后,执行一次 |
ngAfterViewInit | 视图初始化完成,执行一次 |
ngAfterContentInit | 插槽的内容初始化完成,执行一次 |
ngOnDestroy | 组件或指令销毁,释放资源的地方 |
# 组件交互
# 父传子
使用
@Input
装饰器标记字段为输入属性
@Input() hero: Hero; // 定义输入属性
@Input('master') masterName: string; // 自定义别名
// 父组件
<son [hero]="hero"></son>
<son [master]="master"></son>
2
3
4
5
6
使用set 拦截
// set拦截输入属性
@Input() set hero(hero){
this._hero = hero;
}
2
3
4
通过ngOnchanges获取输入属性的变化
ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
let log: string[] = [];
for (let propName in changes) {
let changedProp = changes[propName];
// do something
}
}
2
3
4
5
6
7
# 子传父
使用
@Output()
装饰器定义一个输出属性为EventEmitter实例
// 定义一个输出属性voted,值为EventEmitter实例,泛型为Boolean类型
@Output() voted:EventEmitter = new EventEmitter<boolean>();
didVote = false;
vote(agreed: boolean) {
this.voted.emit(agreed);
this.didVote = true;
}
2
3
4
5
6
7
8
EventEmitter<T>
继承自 RXJS的Subject
EventEmitter
:在带有@Output指令的组件中使用,以同步或异步方式发送自定义事件,并通过订阅实例为这些事件注册处理程序,因此接收事件传递也可以用一下方法,由父组件获取子组件实例
this.voted.emit(parmas);
this.voted.subscribe(...); // 订阅子组件的事件
2
# 父子组件简单互动
通过本地变量
在父组件模板里,新建一个本地变量来代表子组件,然后利用这个变量来读取子组件的属性和调用子组件的方法,注意此方法仅能在父组件模板中使用
<app-son #son></app-son>
<button (click)="son.method()"></button>
<p>{{son.props}}</p>
2
3
使用
@ViewChild
、@ViewChildren
来获取一个或一组的子组件实例
# 元数据属性:
- selector - 用于查询的指令类型或名字
- read - 从查询到的元素中读取另一个令牌
- static- 如果为true,则在运行更改检测之前解析查询结果;如果为false,则在更改检测之后解析。 默认为false
# 使用方法
- 任何带有
@Component
或@Directive
装饰器的类 - 字符串形式的模板引用变量(比如可以使用
@ViewChild('cmp')
来查询<my-component #cmp></my-component>
- 组件树中任何当前组件的子组件所定义的提供商(比如
@ViewChild(SomeService) someService: SomeService
) - 任何通过字符串令牌定义的提供商(比如
@ViewChild('someToken') someTokenVal: any
) TemplateRef
(比如可以用@ViewChild(TemplateRef) template;
来查询<ng-template></ng-template>
)
# 例子
// <my-component #cmp></my-component>
@ViewChild('cmp') public cmp:component;
// <my-component app-trim></my-component>
@ViewChild(TrimDirective) public dir:TrimDirective;
// <my-component #cmp></my-component>
@ViewChildren('cmp') public dom:QueryList<SonFormComponent>;
// <my-component app-trim></my-component>
@ViewChildren(TrimDirective) public dom:QueryList<TrimDirective>;
2
3
4
5
6
7
8
QueryList:一个不可修改的条目列表,当应用状态变化时,Angular 会保证它是最新的
# 非父子组件
使用单例服务共享信息
单例服务在应用中只会实例化一次(根模块注入),可以此来设置全局共享变量
使用subject来广播事件
组件共享同一个服务,利用该服务在组件家族内部实现双向通讯,
@Injectable()
export class MissionService {
// Observable string sources
private missionAnnouncedSource = new Subject<string>();
// Observable string streams
missionAnnounced$ = this.missionAnnouncedSource.asObservable();
// Service message commands
announceMission(mission: string) {
this.missionAnnouncedSource.next(mission);
}
}
2
3
4
5
6
7
8
9
10
11
12
使用ngrx状态管理工具进行数据管理
# 组件样式
# 范围化
在
@Component
的元数据中指定的样式只会对该组件的模板生效
@Component({
selector: 'app-root',
template: `
<h1>Tour of Heroes</h1>
<app-hero-main [hero]="hero"></app-hero-main>
`,
styles: ['h1 { font-weight: normal; }']
})
2
3
4
5
6
7
8
# 样式引入
- css
@import
语法引入相对路径,例@import './hero-details-box.css';
- 全局样式需要在
angular.json
中配置
# 视图封装模式
通过修改元数据encapsulation
,来配置组件的视图封装模式
enum:ViewEncapsulation | 用法 |
---|---|
Emulated(默认) | 全局样式能进来,组件样式不能出 |
None | 全局样式能进来,组件样式能出去 |
shadowDom | 全局样式进不来,组件样式出不去 |
Native | 使用原生shadowDom |
# 动态组件
一般来讲,切换不同的组件仅需要*NgIf
,*NgSwitch
等指令来切换组件
而对于动态组件,则是通过动态加载组件来展示
# 1:确认组件容器
通过指令,在模板中指定组件的插入点,使用ViewContainerRef
来获取插入点视图的访问权
import { Directive, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[dynamic-component-container]',
})
export class AdDirective {
constructor(
public viewRef: ViewContainerRef
) { }
ngOnInit(){}
}
// <div dynamic-component-container></div>
// 这样就表明动态组件的容器为此div,
// 当然使用<ng-template></ng-template>是最佳选择,它不会渲染任何额外的输出
2
3
4
5
6
7
8
9
10
11
12
13
14
ViewContainerRef可以对该容器中的组件进行一系列操作
clear()
:清除容器中的视图createComponent()
:实例化一个组件,并把它插入到指定index,多次执行会append多个组件
# 2:组件映射
使用ComponentFactoryResolver
映射一个componentFactory
类
constructor(
public viewRef: ViewContainerRef,
+ public factory:ComponentFactoryResolver
) { }
+ ngOnInit(){
+ const component = this.factory.resolveComponentFactory(SonComponent)
+ }
2
3
4
5
6
7
# 3:挂载组件实例
ngOnInit(){
const component = this.factory.resolveComponentFactory(SonFormComponent)
+ const son:ComponentRef<SonFormComponent> = this.viewRef.createComponent(component);
}
2
3
4
至此就可将SonComponent
添加至<div>
容器中
# 4:完整样例
import { Directive, ViewContainerRef,ComponentFactoryResolver } from '@angular/core';
@Directive({
selector: '[dynamic-component-container]',
})
export class AdDirective {
constructor(
public viewRef: ViewContainerRef,
public factory:ComponentFactoryResolver
) { }
ngOnInit(){
// 创建一个组件实例
const componentFactory = this.factory.resolveComponentFactory(component);
// 清除当前视图
this.viewRef.clear();
// 挂载组件实例
const son:ComponentRef<SonFormComponent> = this.viewRef.createComponent(componentFactory);
// 获取组件的变量或方法
son.instance.id;
son.instance.methods();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 需要注意
ViewContainerRef
:获取视图权限,操作动态添加进行视图的组件ComponentRef
:ViewContainerRef
实例化组件的返回类型ViewRef
:ComponentRef
的hostView
属性,ViewContainerRef
的get()
操作方法返回都是此类型ComponentFactoryResolver
:将一个组件映射为ComponentFactory
类ComponentFactory
:ViewContainerRef
挂载组件所需要的参数