angular11源码探索十[viewChild和viewChildren到几个不常用的生命周期用处]
作者:互联网
完整的生命周期
constructor
ngOnChanges
ngOninit
ngDoCheck
* ngAfterContentInit
* ngAfterContentChecked
* ngAfterViewInit
* ngAfterViewChecked
ngOnDestroy
ngOnChanges
每一次
@Input
都会执行一次
ngOnInit
初始化数据的加载
ngOnDestroy
在原件销毁之前
ngDoCheck
每一次执行变更,都会自动执行
ngDoCheck
事件
下面几个生命周期应该属于ngDocheck 里面的
子元件事件
ngAfterViewInit
- 当View里面所有元件都初始化完成后 触发的事件
ngAfterViewChecked
- 当view里面所有元件都完成变更侦测机制后触发的事件
ngAfterViewChecked 父组件里面放入一个子组件,子组件里面有个视图改变操作 <input [(ngModel)]="hello"> hello = 'hello'; 我们在父组件里面监听变化 ngAfterViewChecked() { console.log(1); } 我们发现每次子组件input输入的值发生变化的时候,ngAfterViewChecked 都会执行
这样写好像没什么意义,那我们可以升级下写法
父组件直接拿到变化的值 // 拿到子组件这个函数 @ViewChild(AComponent) a:AComponent ngAfterViewChecked() { console.log(this.a.hello); }
内容元件事件 ng-content
ngAfterContentInit
- 当content里面所有的元件都初始化完成后触发事件
<app-a> <app-b></app-b> </app-a>
父
ng-content
把子组件的内容映射到父组件app-a <ng-content></ng-content> 内容投影 子 app-b 通过ng-content拿到子组件内容 父组件直接拿到<ng-content>的子组件 export class AComponent implements AfterContentInit{ @ContentChild(BComponent) B: BComponent; ngAfterContentInit() { console.log(this.B); } }
ngAfterContentChecked
- 当content里面所有元件都完成后侦测机制后触发事件
viewChild 和viewChildren区别
viewChild
ViewChild(selector: string | Function | Type<any>, opts: { read?: any; static: boolean; }): any
两个参数,Aselector
和opts
selector
: 字符串,类型或者字符串或类型的函数,默认查找与选择器匹配的第一个元素
opts
: 有两个选项
static
确定查找何时解析,查询指定的字符串解析,true
初始化视图时,false
如果你希望在每次更改检测后解决后解决它
获取angular 组件内呈现的DOM元素的引用,可以进行DOM元素的操作
<div #someElement>Sample Code</div>
@ViewChild('someElement') some;
ngAfterViewInit 生命周期可以拿到这个引用
ngAfterViewInit(): void {
this.some.nativeElement
}
视图查询
- 拿到任何带有
@Component
或@Directive
装饰器的类当前视图使用的 - 字符串的模板引用变量,就是上面的dom引用
- 当前组件的子组件定义的提供商
@ViewChild(SomeService) someService: SomeService )
- 任何通过字符串令牌定义的提供商(比如
@ViewChild('someToken') someTokenVal: any
(刚开始我不懂,其实就是你子组件使用啦,就是可以使用)
找到字符串令牌
@Directive({
selector: '[appDir]' ,
providers: [
{provide: 'token', useValue: 'test'},
{provide: TOKEN_URL, useValue: 'test1'},
TestOneService
]
})
页面使用
<div appDir>dfsdsfdsfdsf</div>
ts查找
@ViewChild('token',{static:true}) token:string;
@ViewChild(TOKEN_URL,{static:true}) token1:any;
@ViewChild(TestOneService) test:TestOneService;
ngAfterViewInit() {
console.log(this.token);
console.log(this.token1);
console.log(this.test);
}
viewChildren
new(selector: Type<any>|InjectionToken<unknown>|Function|string,
opts?: {descendants?: boolean, read?: any}): Query;
opts
- descendants 包含所有后代时为true,否则仅包括直系子代。
- read 用于从查询的元素中读取不同的令牌。
区别
@ViewChildren
元素引用列表,而不是单个引用
<input type="text" [(ngModel)]="a">
<input type="text" [(ngModel)]="b">
<input type="text" [(ngModel)]="c">
a = 1
b = 2
c = 2;
@ViewChildren(NgModel) model: QueryList<NgModel>
ngAfterViewInit() {
console.log(this.model.length);// 可以拿到这三个值
}
<div appDir [dir]="['aaa']"></div>
<div appDir [dir]="{sex:'男'}"></div>
父
@ViewChildren(DirDirective) directives!: QueryList<DirDirective>;
ngAfterViewInit() {
this.directives.forEach(val=>{
console.log(val);
})
}
可能会对QueryList
返回的不怎么理解
ViewChild和ViewChildren可用于访问子组件的属性和方法。使用ViewChild和ViewChildren,我们可以获取子组件的引用,从而进一步提供对所有属性和方法的访问。这可以使父组件访问子组件并启用它们之间的通信。
父组件对子组件的值的修改
父
<button (click)="clickDown()">Click</button>
<app-b #app></app-b>
@ViewChild('app') app:BComponent;
clickDown() {
this.app.num=20;
}
子
num=10
父传子的一种方式
<app-a *ngFor="let config of configs" [config]="config"></app-a>
configs = [
{opacity: 0, duration: 500},
{opacity: 1, duration: 600},
];
子
@Input('config') config;
案例
拿到组件实例
<app-a #ccc></app-a>
<app-b #ccc></app-b>
父
export class TwoComponent implements OnInit, AfterViewInit, AfterViewChecked,AfterContentInit {
@ViewChild('ccc') C:AComponent;
@ViewChildren('ccc') cRen:QueryList<AComponent|BComponent>
constructor() {}
ngAfterViewInit() {
console.log(this.C);// 默认拿到第一个
console.log(this.cRen);//默认拿到两个组件的集合
console.log(this.cRen.first); //拿到第一个
console.log(this.cRen.last);// 拿到最后一个
}
ngOnInit(): void {}
}
拿到DOM
<p #ccc></p>
<div #ccc></div>
@ViewChild('ccc') C:ElementRef;
@ViewChildren('ccc') cRen:QueryList<ElementRef>
ngAfterViewInit() {
console.log(this.C);// 默认拿到第一个
console.log(this.cRen.first); //拿到第一个
}
支持多个引用
<p #ccc #aaa></p>
@ViewChild('ccc') C:ElementRef;
@ViewChild('aaa') A:ElementRef;
ngAfterViewInit() {
console.log(this.C);// 默认拿到第一个
console.log(this.A);
}
同理 ViewChildren也是一样的
拿到template的内容
用ViewContainerRef
填充到页面
<ng-template #aaa>
<h1>aaaa</h1>
<h1>bbb</h1>
</ng-template>
@ViewChild('aaa') A: TemplateRef;
constructor(private vRef: ViewContainerRef) {
}
ngAfterViewInit() {
this.vRef.createEmbeddedView(this.A)
}
多级查找
<app-a #q>
<app-b #contentQuery></app-b>
<app-c #contentQuery></app-c>
</app-a>
大盒子
@ViewChild('q') A:AComponent
ngAfterViewInit() {
console.log(this.A);
}
app-a
<ng-content></ng-content>
怎么拿到这两个子组件的列表呢
@ContentChild(BComponent) B;
@ContentChild(CComponent) C;
@ContentChildren('contentQuery') D!:QueryList;
ngAfterContentInit() {
console.log(this.B);
console.log(this.C);
console.log(this.D); //拿到两个啦
}
dom形式类似
<app-a>
<div #contentQuery>xxx</div>
<div #contentQuery>bbb</div>
</app-a>
app-a
<ng-content></ng-content>
@ContentChildren('contentQuery') contentQuery;
ngAfterViewInit() {
console.log(this.contentQuery);
}
ViewChild 的get/set操作
两个要一起用不然会报错,也可以直接使用set,也就是说get是可选的,但是set是必传的
可以拿到修改的记录
<span #foo></span>
_foo!: ElementRef;
@ViewChild('foo')
get foo(): ElementRef {
return this._foo;
}
set foo(value: ElementRef) {
this._foo = value;
}
ngAfterViewInit() {
let text=this.renderer.createText('xxxx')
this.renderer.appendChild(this._foo.nativeElement,text)
}
指令
<div [appTestDir]="'aaa'"></div>
export class TwoComponent implements OnInit, AfterViewInit, AfterViewChecked, AfterContentInit {
_textDir!: TestDirDirective;
@ViewChild(TestDirDirective, {static: true}) //true 初始化视图时
get textDir(): TestDirDirective {
return this._textDir;
}
set textDir(value: TestDirDirective) {
console.log(value.appTestDir); //222
this._textDir = value;
}
ngAfterViewInit() {
console.log(this._textDir.appTestDir);//aaa
}
}
ng-template 把一个组件传入到另一个组件里
<app-b [content]="app"></app-b>
<ng-template #app>
<app-a></app-a>
</ng-template>
app-b
@Input('content') content;
<ng-container *ngTemplateOutlet="content"></ng-container>
升级第二种方式
<app-a>
<ng-template #contentQuery>
<h1>dddddd</h1>
</ng-template>
<ng-template #contentQuery>
<h1>ggggg</h1>
</ng-template>
</app-a>
app-a
<ng-content></ng-content>
<!--第一个-->
<div *ngTemplateOutlet="contentQuery.first"></div>
@ContentChildren('contentQuery') contentQuery:QueryList<TemplateRef<any> | null>;
ngAfterContentInit() {
console.log(this.contentQuery);
}
其实指令也是同理可以拿到的
<sub-comp>
<div some-dir></div>
<div some-dir></div>
</sub-comp>
@Directive({selector: '[some-dir]'})
class SomeDir {
}
大盒子可以直接查到
@ViewChild(SubComp) subComp!: SubComp;
sub-comp
<ng-content></ng-content>
@ContentChildren(SomeDir) foo!: QueryList<SomeDir>;
动态检测子代的变化
<app-a>
<div *ngIf="showing" #cmp>h1 h1</div>
</app-a>
<button (click)="showing=!showing">++</button>
showing: boolean=true;
====
app-a
<ng-content></ng-content>
@ContentChildren('cmp',{descendants:false}) cmp!:QueryList<ElementRef>
ngAfterContentChecked() {
console.log(this.cmp.length);
}
另一个方式
<button (click)="showing=!showing">++</button>
<div *ngIf="showing" #foo>
<h1>我是谁</h1>
</div>
@ViewChildren('foo') foos!: QueryList<any>;
showing: boolean=true;
ngAfterViewInit() {
this.foos.changes.subscribe(value=>{
console.log(value);
})
}
标签:console,angular11,viewChildren,拿到,ViewChild,源码,组件,ngAfterViewInit,log 来源: https://www.cnblogs.com/fangdongdemao/p/14191302.html