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

  • 相关阅读:
    centos7.6 使用yum安装mysql5.7
    解决hadoop本地库问题
    docker-compose 启动警告
    docker 安装zabbix5.0 界面乱码问题解决
    docker 部署zabbix问题
    zookeeper 超时问题
    hbase regionserver异常宕机
    (转载)hadoop 滚动升级
    hadoop Requested data length 86483783 is longer than maximum configured RPC length
    zkfc 异常退出问题,报错Received stat error from Zookeeper. code:CONNECTIONLOSS
  • 原文地址:https://www.cnblogs.com/itfantasy/p/6705422.html
Copyright © 2011-2022 走看看