zoukankan      html  css  js  c++  java
  • 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
      }
    

    视图查询

    • 拿到任何带有@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);
        })
      }
    
    
  • 相关阅读:
    ios面试题(二)
    ios之自定义UINavigationBar
    ios之自定义导航栏上的返回按钮
    ios之键盘的自定义
    ios之UITabelViewCell的自定义(xib实现2)
    ios之UITabelViewCell的自定义(xib实现)
    ios之UITabelViewCell的自定义(代码实现)
    ios 登录功能学习研究
    Create Table操作
    C#数据库查询和操作大全
  • 原文地址:https://www.cnblogs.com/fangdongdemao/p/14191302.html
Copyright © 2011-2022 走看看