编程语言
首页 > 编程语言> > angular11源码探索十[viewChild和viewChildren到几个不常用的生命周期用处]

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

两个参数,Aselectoropts

selector: 字符串,类型或者字符串或类型的函数,默认查找与选择器匹配的第一个元素

opts : 有两个选项

static确定查找何时解析,查询指定的字符串解析,true初始化视图时,false 如果你希望在每次更改检测后解决后解决它

获取angular 组件内呈现的DOM元素的引用,可以进行DOM元素的操作

<div #someElement>Sample Code</div>
@ViewChild('someElement') some;
ngAfterViewInit 生命周期可以拿到这个引用
  ngAfterViewInit(): void {
    this.some.nativeElement
  }

视图查询

找到字符串令牌

@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

区别

@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