【问题标题】:What are all the valid selectors for ViewChild and ContentChild?ViewChild 和 ContentChild 的所有有效选择器是什么?
【发布时间】:2018-08-16 04:17:00
【问题描述】:

我正在寻找有效选择器的完整列表,可用于通过@ViewChild@ContentChild 访问子组件/DOM 元素。

说我有孩子HelloComponent:

我知道我可以添加一个模板#ref 并对其进行查询,如下所示:

<hello #myHello></hello>

@ViewChild('myHello') myHello: HelloComponent;

或者我可以直接查找该组件(无需模板#ref):

@ViewChild(HelloComponent) myHello: HelloComponent;

this issue 中提到可以使用以下选择器:

我们目前支持 CSS 选择器的子集:
* 元素选择器
* 属性选择器(包括值)
* :not(...) 伪选择器
* 以上组合(包括,)

但是当我在 Stackblitz 中测试这些以验证 (here's the link to that) 时,我实际上无法让前三个中的任何一个工作。 (检查控制台以查看 undefined 以了解我无法使用的选择器类型。我不确定这些选择器是否有问题,或者实际列表是否不同。)

那么,哪些选择器会起作用?另外,@ViewChild@ContentChild@ViewChildren@ContentChildren 的列表是否相同?

【问题讨论】:

  • AFAIK,问题中的评论是错误的,谈论@Component@Directive选择器,而不是@ViewChild选择器。

标签: angular


【解决方案1】:

首先,正如@JB Nizet 在 cmets 中已经提到的那样the comment in the issue 是错误的:它与查询选择器无关,而是指directive selector

让我们看看我们可以使用什么样的选择器进行查询。

用于查询的 Angular 文档 states

选择器 - 用于查询的指令类型或名称。

指令类型

似乎任何人都应该清楚(1) 我们可以查询任何由@Component@Directive 装饰器修饰的类

@Component({
  selector: 'some-comp',
  template: '...'
})
export class SomeComp {}

@Directive({
  selector: '[someDir]'
})
export class SomeDir {}

@Component({
  selector: 'host-comp',
  template: `
    <some-comp someDir></some-comp>
  `
})
export class HostComp {
  @ViewChild(SomeComp) someComp: SomeComp;
  @ViewChild(SomeDir) someDir: SomeDir;
}

用于查询的名称

对我来说,这是令人困惑的描述。

原来这里的名字是(2)template reference variable的名字是一个字符串

@Component({
  selector: 'host-comp',
  template: `
    <some-comp #someComp></some-comp>
  `
})
export class HostComp {
  @ViewChild('someComp') someComp: SomeComp;
}

我们可能会在这里结束,但现在是时候看看 angular 源代码并深入研究一下了。

隐藏行为

让我们看看 Angular 编译器用来读取查询元数据的 the code

private _queryVarBindings(selector: any): string[] { return selector.split(/\s*,\s*/); }

private _getQueryMetadata(q: Query, propertyName: string, typeOrFunc: Type|Function):
      cpl.CompileQueryMetadata {
  let selectors: cpl.CompileTokenMetadata[];
  if (typeof q.selector === 'string') {
    selectors =
        this._queryVarBindings(q.selector).map(varName => this._getTokenMetadata(varName));
  } else {
    if (!q.selector) {
      this._reportError(
          syntaxError(
              `Can't construct a query for the property ...`),
          typeOrFunc);
      selectors = [];
    } else {
      selectors = [this._getTokenMetadata(q.selector)];
    }
}

从前面的代码我们可以得出结论:

让我们应用我们从上面的代码中学到的东西。

我们(3)可以通过使用多个模板引用变量除以,来查询多个值

@Component({
  selector: 'a',
  template: '...'
})
export class A {}

@Component({
  selector: 'b',
  template: '...'
})
export class B {}

@Component({
  selector: 'host-comp',
  template: `
    <a #a></a>
    <b #b></b>
  `
})
export class HostComp {
  @ViewChildren('a, b') components;

  ngAfterViewInit() {
    console.log(this.components); // [A, B]
  }
}

(4) 可以查询在组件或指令上定义的提供程序。(另见@Ilia Volk 添加的示例)

@Component({
  selector: 'a',
  template: '...',
  providers: [SomeService]
})
export class A {}

@Component({
  selector: 'host-comp',
  template: `<a></a>`
})
export class HostComp {
  @ViewChild(SomeService) someService: SomeService;
}

由于字符串可以作为提供者的标记,我们可以(5) 查询通过字符串标记定义的多个提供者

@Component({
  selector: 'a',
  providers: [{ provide: 'tokenA', useValue: 'TokenAValue' }],
  template: '...'
})
export class A { }

@Component({
  selector: 'b',
  providers: [{ provide: 'tokenB', useValue: 'TokenBValue' }],
  template: '...'
})
export class B { }

@Component({
  selector: 'host-comp',
  template: `
    <a #a></a>
    <b #b></b>
  `
})
export class HostComp {
  @ViewChildren('tokenA, tokenB') stringTokenProviders;

  ngAfterViewInit() {
    console.log(this.stringTokenProviders); // ['TokenAValue', 'TokenBValue']
  }
}

我们的下一站是核心包中angular returns us the value of particular query的地方:

export function getQueryValue(
    view: ViewData, nodeDef: NodeDef, queryValueType: QueryValueType): any {
  if (queryValueType != null) {
    // a match
    switch (queryValueType) {
      case QueryValueType.RenderElement:
        return asElementData(view, nodeDef.nodeIndex).renderElement;
      case QueryValueType.ElementRef:
        return new ElementRef(asElementData(view, nodeDef.nodeIndex).renderElement);
      case QueryValueType.TemplateRef:
        return asElementData(view, nodeDef.nodeIndex).template;
      case QueryValueType.ViewContainerRef:
        return asElementData(view, nodeDef.nodeIndex).viewContainer;
      case QueryValueType.Provider:
        return asProviderData(view, nodeDef.nodeIndex).instance;
    }
  }
}

上面代码中的RenderElement 是一些我们无法查询的内部令牌。

ElementRef可以通过模板引用变量查询,也可以使用read option

(6)TemplateRef可以通过selector查询

@Component({
  selector: 'host-comp',
  template: `
    <ng-template></ng-template>
  `
})
export class HostComp {
  @ViewChild(TemplateRef) template;
}

当然还有ViewContainerRefread 选项。

Provider 可以通过使用read 选项或通过我在此答案中间描述的选择器获得。

【讨论】:

  • (4) Provider defined on a component or directive can be queried.的用例是什么
【解决方案2】:

简而言之查看孩子 可以使用角度指令、组件选择器、引用变量

【讨论】:

    猜你喜欢
    • 2016-03-23
    • 1970-01-01
    • 2012-06-23
    • 2021-07-09
    • 2017-04-21
    • 1970-01-01
    • 2019-06-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多