• Angular 2 中的 ViewChild 和 ViewChildren


    https://segmentfault.com/a/1190000008695459

    ViewChild

    ViewChild 是属性装饰器,用来从模板视图中获取匹配的元素。视图查询在 ngAfterViewInit 钩子函数调用前完成,因此在 ngAfterViewInit 钩子函数中,才能正确获取查询的元素。

    @ViewChild 使用模板变量名

    import { Component, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
    
    @Component({
      selector: 'my-app',
      template: `
        <h1>Welcome to Angular World</h1>
        <p #greet>Hello {{ name }}</p>
      `,
    })
    export class AppComponent {
      name: string = 'Semlinker';
    
      @ViewChild('greet')
      greetDiv: ElementRef;
    
      ngAfterViewInit() {
        console.dir(this.greetDiv);
      }
    }
    

    @ViewChild 使用模板变量名及设置查询条件

    import { Component, TemplateRef, ViewChild, ViewContainerRef, AfterViewInit } from '@angular/core';
    
    @Component({
      selector: 'my-app',
      template: `
        <h1>Welcome to Angular World</h1>
        <template #tpl>
          <span>I am span in template</span>
        </template>
      `,
    })
    export class AppComponent {
    
      @ViewChild('tpl')
      tplRef: TemplateRef<any>;
    
      @ViewChild('tpl', { read: ViewContainerRef })
      tplVcRef: ViewContainerRef;
    
      ngAfterViewInit() {
        console.dir(this.tplVcRef);
        this.tplVcRef.createEmbeddedView(this.tplRef);
      }
    }
    

    @ViewChild 使用类型查询

    child.component.ts

    import { Component, OnInit } from '@angular/core';
    
    @Component({
        selector: 'exe-child',
        template: `
          <p>Child Component</p>  
        `
    })
    export class ChildComponent {
        name: string = 'child-component';
    }
    

    app.component.ts

    import { Component, ViewChild, AfterViewInit } from '@angular/core';
    import { ChildComponent } from './child.component';
    
    @Component({
      selector: 'my-app',
      template: `
        <h4>Welcome to Angular World</h4>
        <exe-child></exe-child>
      `,
    })
    export class AppComponent {
    
      @ViewChild(ChildComponent)
      childCmp: ChildComponent;
    
      ngAfterViewInit() {
        console.dir(this.childCmp);
      }
    }
    

    以上代码运行后,控制台的输出结果:

    ViewChildren

    ViewChildren 用来从模板视图中获取匹配的多个元素,返回的结果是一个 QueryList 集合。

    @ViewChildren 使用类型查询

    import { Component, ViewChildren, QueryList, AfterViewInit } from '@angular/core';
    import { ChildComponent } from './child.component';
    
    @Component({
      selector: 'my-app',
      template: `
        <h4>Welcome to Angular World</h4>
        <exe-child></exe-child>
        <exe-child></exe-child>
      `,
    })
    export class AppComponent {
    
      @ViewChildren(ChildComponent)
      childCmps: QueryList<ChildComponent>;
    
      ngAfterViewInit() {
        console.dir(this.childCmps);
      }
    }
    

    以上代码运行后,控制台的输出结果:

    ViewChild 详解

    @ViewChild 示例

    import { Component, ElementRef, ViewChild } from '@angular/core';
    
    @Component({
      selector: 'my-app',
      template: `
        <h1>Welcome to Angular World</h1>
        <p #greet>Hello {{ name }}</p>
      `,
    })
    export class AppComponent {
      name: string = 'Semlinker';
      @ViewChild('greet')
      greetDiv: ElementRef;
    }
    

    编译后的 ES5 代码片段

    var core_1 = require('@angular/core');
    
    var AppComponent = (function () {
        function AppComponent() {
            this.name = 'Semlinker';
        }
        __decorate([
            core_1.ViewChild('greet'), // 设定selector为模板变量名
            __metadata('design:type', core_1.ElementRef)
    ], AppComponent.prototype, "greetDiv", void 0);
    

    ViewChildDecorator 接口

    export interface ViewChildDecorator {
      // Type类型:@ViewChild(ChildComponent)
      // string类型:@ViewChild('tpl', { read: ViewContainerRef })
      (selector: Type<any>|Function|string, {read}?: {read?: any}): any;
    
      new (selector: Type<any>|Function|string, 
          {read}?: {read?: any}): ViewChild;
    }
    

    ViewChildDecorator

    export const ViewChild: ViewChildDecorator = makePropDecorator(
        'ViewChild',
        [
          ['selector', undefined],
          {
            first: true,
            isViewQuery: true,
            descendants: true,
            read: undefined,
          }
        ],
    Query);
    

    makePropDecorator函数片段

    /*
     * 创建PropDecorator工厂
     * 
     * 调用 makePropDecorator('ViewChild', [...]) 后返回ParamDecoratorFactory
     */
    function makePropDecorator(name, props, parentClass) {
              // name: 'ViewChild'
              // props: [['selector', undefined], 
              //  { first: true, isViewQuery: true, descendants: true, read: undefined}]
      
              // 创建Metadata构造函数
            var metaCtor = makeMetadataCtor(props);
          
            function PropDecoratorFactory() {
                var args = [];
                   ... // 转换arguments对象成args数组
                if (this instanceof PropDecoratorFactory) {
                    metaCtor.apply(this, args);
                    return this;
                }
                ...
                return function PropDecorator(target, name) {
                    var meta = Reflect.getOwnMetadata('propMetadata', 
                        target.constructor) || {};
                    meta[name] = meta.hasOwnProperty(name) && meta[name] || [];
                    meta[name].unshift(decoratorInstance);
                    Reflect.defineMetadata('propMetadata', meta, target.constructor);
                };
                var _a;
            }
               if (parentClass) { // parentClass: Query
                PropDecoratorFactory.prototype = Object.create(parentClass.prototype);
            }
              ...
            return PropDecoratorFactory;
        }
    

    makeMetadataCtor 函数:

    // 生成Metadata构造函数: var metaCtor = makeMetadataCtor(props); 
    // props: [['selector', undefined], 
    // { first: true, isViewQuery: true, descendants: true, read: undefined }]
      function makeMetadataCtor(props) {
            // metaCtor.apply(this, args);
            return function ctor() {
                var _this = this;
                var args = [];
                ... // 转换arguments对象成args数组
                props.forEach(function (prop, i) { // prop: ['selector', undefined]
                    var argVal = args[i]; 
                    if (Array.isArray(prop)) { // argVal: 'greet'
                        _this[prop[0]] = argVal === undefined ? prop[1] : argVal;
                    }
                    else {
                 // { first: true, isViewQuery: true, descendants: true, read: undefined }
                 // 合并用户参数与默认参数,设置read属性值     
                        for (var propName in prop) { 
                            _this[propName] = 
                                argVal && argVal.hasOwnProperty(propName) ? 
                              argVal[propName] : prop[propName];
                        }
                    }
                });
            };
    }
    

    我们可以在控制台输入 window['__core-js_shared__'] ,查看通过 Reflect API 保存后的metadata信息

    接下来我们看一下编译后的 component.ngfactory.js 代码片段,查询条件 @ViewChild('greet')

    我们再来看一下前面示例中,编译后 component.ngfactory.js 代码片段,查询条件分别为:

    1.@ViewChild('tpl', { read: ViewContainerRef })

    2.@ViewChild(ChildComponent)

    通过观察不同查询条件下,编译生成的 component.ngfactory.js 代码片段,我们发现 Angular 在创建 AppComponent 实例后,会自动调用 AppComponent 原型上的 createInternal 方法,才开始创建组件中元素,所以之前我们在构造函数中是获取不到通过 ViewChild 装饰器查询的视图元素。另外,配置的视图查询条件,默认都会创建一个 jit_QueryList 对象,然后根据 read 查询条件,创建对应的实例对象,然后添加至 QueryList 对象中,然后在导出对应的查询元素到组件对应的属性中。

    总结

    ViewChild 装饰器用于获取模板视图中的元素,它支持 Type 类型或 string 类型的选择器,同时支持设置 read 查询条件,以获取不同类型的实例。而 ViewChildren 装饰器是用来从模板视图中获取匹配的多个元素,返回的结果是一个 QueryList 集合。

  • 相关阅读:
    一次线上bug引起的反思
    本地调试接口返回信息不对 以及 jar冲突问题
    404问题记录
    Intelij IDEA 配置Tomcat时找不到 “Application Server”
    java多线程处理问题
    DataTemplate和ControlTemplate的关系
    WP模板
    wp中的动画
    wp中的位图
    2013.7.22-7.28开发资料汇总
  • 原文地址:https://www.cnblogs.com/itfantasy/p/6705422.html
Copyright © 2020-2023  润新知