У меня есть компонент, использующий проекцию контента.
<ng-content select = "button">
</ng-content>
Я хочу сделать что-то подобное в своем компоненте:
@ContentChild('button') button: ElementRef;
Однако, когда я проверяю this.button, это undefined как в ngAfterViewInit, так и в ngAfterContentInit.
Как я могу выбрать свой дочерний контент, если это собственный элемент, а не пользовательский компонент?





Ни один из декораторов запросов не может выбирать собственные элементы без ссылки.
@ViewChild('button')
@ViewChildren('button')
@ContentChild('button')
@ContentChildren('button')
Это не селектор CSS.
Декораторы запросов ищут в графе зависимостей соответствующий тип и выбирают их. Соответствующий тип может быть ссылкой на шаблон, компонентом или директивой.
Рекомендуемый подход для выбора определенных элементов в содержимом - пометить его директивой.
@Directive({...})
export class MyButtonDirective {}
Теперь вы можете запросить это в своем родительском компоненте.
@ContentChild(MyButtonDirective) button: MyButtonDirective;
Когда вы используете строковое значение, оно относится к ссылке на переменную шаблона. Они написаны как #someValue и могут быть ссылкой на шаблон, компонентом или директивой.
<button #myButton></button>
Теперь вы можете ссылаться на приведенное выше как
@ViewChild('myButton')
Я не знаю, работает ли это для ContentChild, из-за того, как работают представления. Переменная шаблона #myButton может не отображаться в родительском графе зависимостей.
В качестве альтернативы вы можете ввести ElementRef родительского компонента, а затем использовать собственный JavaScript для поиска детей, которые вы ищете. Это плохая практика с точки зрения Angular, потому что идея состоит в том, чтобы отделить ваше приложение от DOM.
@ConnorsFan круто. Я обычно так не делаю. Если я видел #myButton в шаблоне и не видел, чтобы он использовался где-либо в этом шаблоне, я мог бы удалить его. Так что вы должны назвать их как-то вроде #myButtonForParent.
@ConnorsFan круто, это действительно работает, но это означает, что AppComponent должен знать, что он должен использовать кнопку с определенным шаблоном ref myButton. Это то, о чем ему не следует знать. Вы так не думаете?
@Yulian Я создал общие директивы кнопок, в которых в качестве селектора используется только button. Затем вы можете запросить те, а другие компоненты не знают, что это происходит. Вы просто должны быть осторожны, никто другой не использует тот же селектор.
Важно понимать, что селектор, который вы передаете @ContentChild, является нет селектором CSS, и вы не можете найти дочерний контент на основе его идентификатора, класса или типа элемента. Вы можете искать только по имени идентификатора, которое вы ему дали, используя оператор #.
Итак, учитывая компонент под названием my-comp и следующее использование:
<my-comp><button #buttonName>Click me</button></my_comp>
Вы можете найти его, используя:
@ContentChild('buttonName') button: ElementRef;
Редактировать
Как отмечено в комментарии OP к ответу ckTag (он опередил меня на минуту), это означает, что вызывающий код должен знать, что вы ожидаете, что контент будет помечен определенным именем.
Если ваш компонент действительно использует нужно, чтобы знать подробности своего дочернего содержимого, лучшим ответом будет предоставление типа директивы для @ContentChild:
@ContentChild(MyButtonComponent, { read: ElementRef })
button: ElementRef;
Это также лучше, потому что теперь вы можете реально ожидать, что будете знать тип контента; в конце концов, код вызова может вставить #button в div, если ему так хочется.
Это означает попрощаться с использованием обычного элемента HTML button, но имеет и другие преимущества. Вы можете, например, предоставить сообщение, которое будет направлять ваш код вызова, например:
<ng-content></ng-content>
<div *ngIf = "button">Error: Please provide a MyButtonComponent</div>
@CobusKruger на самом деле, вызывающий код не должен разрешать ничего, кроме собственных элементов кнопки, если у вас есть <ng-content select = "button"> </ng-content>, не так ли?
@Yulian Я пропустил, что вы использовали select. Я все еще обычно использую это со своими собственными директивами, а это означает, что вызывающему коду не нужно знать такие вещи, как «использовать это ссылочное имя шаблона для кнопки». Таким образом, требование ссылочной переменной шаблона с заданным именем технически может быть тем ответом, который вы просили, но, как вы предположили, это неинтуитивно.
Я собирался опубликовать ответ со ссылочной переменной шаблона, и я могу подтвердить, что он работает с
@ContentChild, если вы получите доступ к нему вngAfterContentInit. См. этот stackblitz.