Каковы все допустимые селекторы для ViewChild и ContentChild?

Я ищу полный список допустимых селекторов, которые я могу использовать для доступа к дочерним компонентам / элементам DOM через @ViewChild а также @ContentChild,

Скажи у меня есть ребенок HelloComponent:

Я знаю, что могу добавить шаблон #ref и запросить его, вот так:

<hello #myHello></hello>

@ViewChild('myHello') myHello: HelloComponent;

Или я могу искать этот компонент напрямую (без шаблона #ref):

@ViewChild(HelloComponent) myHello: HelloComponent;

В этом выпуске упоминается, что можно использовать следующие селекторы:

в настоящее время мы поддерживаем подмножество селекторов CSS:
* селекторы элементов
* атрибуты селекторов (включая значения)
*: не (...) псевдоселектор
* комбинация вышеперечисленного (в том числе,)

Но когда я проверил их в Stackblitz, чтобы убедиться ( вот ссылка на это), я не смог заставить ни одну из первых трех работать. (Проверьте консоль, чтобы увидеть undefined для типов селекторов я не мог работать. Я не уверен, что делаю что-то не так с этими селекторами или фактический список другой.)

Итак, какие селекторы будут работать? Кроме того, список одинаков для @ViewChild, @ContentChild, @ViewChildren, а также @ContentChildren?

1 ответ

Решение

Прежде всего, как @JB Nizet уже упоминал в комментариях, комментарий в проблеме неправильный: он не имеет ничего общего с селектором запросов, а скорее относится к селектору директив.

Давайте рассмотрим, какие селекторы мы можем использовать для запросов.

В угловой документации говорится, что для запросов:

селектор - тип директивы или имя, используемое для запроса.

Тип директивы

Кажется, всем должно быть ясно, что (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) - это имя ссылочной переменной шаблона, которая является строкой:

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

мы могли бы закончить здесь, но пришло время взглянуть на угловой исходный код и погрузиться немного глубже.

Скрытое поведение

Давайте посмотрим на код, используемый угловым компилятором для чтения метаданных запроса:

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)];
    }
}

Из предыдущего кода мы можем сделать вывод, что:

  • если селектор является строкой, деленной на , тогда мы можем построить более одного селектора.

  • с другой стороны, если селектор не является строкой, то мы можем получить только один селектор

  • компилятор использует this._getTokenMetadata метод для извлечения информации из переданного селектора, но это тот же метод, который используется для извлечения метаданных провайдера https://github.com/angular/angular/blob/4c089c1d931c0ea35591837706de205a75a61ccb/packages/compiler/src/metadata_resolver.ts#L1073-L1077

Давайте применим наши знания из приведенного выше кода.

Мы (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 возвращает нам значение определенного запроса:

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 может быть запрошен через ссылку на шаблон или с помощью опции чтения

(6) TemplateRef может быть запрошен через selector:

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

и конечно так же как ViewContainerRef через read вариант.

Provider можно получить с помощью read вариант или через селектор, как я описал в середине этого ответа.

Вкратце View child Вы можете использовать директиву anglular, селектор компонентов, ссылочную переменную

Что такое сценарий использования css selector? Вводить ElementRef?@ViewChild, @ContentChild, @ViewChildren, а также @ContentChildren могут использоваться с разными директивами одновременно. Один из способов - объявить один и тот же токен внутри провайдера. https://stackblitz.com/edit/angular-5ktrcm

Другие вопросы по тегам