Каковы все допустимые селекторы для 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