Вопрос или проблема
В данный момент я пытаюсь воспроизвести логику, которую Angular использует для карт Angular Material. По функциональности все работает, но мое оформление не применяется, когда я пытаюсь переключиться с css-селекторов на селекторы классов. Вот немного кода для моего случая:
card.component.ts
import {
ChangeDetectionStrategy,
Component,
Directive,
HostBinding,
ViewEncapsulation,
} from '@angular/core';
@Directive({
selector: `[card-header], [cardHeader]`,
host: { class: 'card-header' },
})
export class CardHeader {}
@Directive({
selector: `[card-content], [cardContent]`,
host: { class: 'card-content' },
})
export class CardContent {}
@Directive({
selector: `[card-footer], [cardFooter]`,
host: { class: 'card-footer' },
})
export class CardFooter {}
@Component({
selector: 'app-card',
standalone: true,
templateUrl: './card.component.html',
styleUrl: './card.component.scss',
host: {
class: 'app-card',
},
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
})
export class CardComponent {}
card.component.html
<ng-content></ng-content>
card.component.scss
app-card {
display: flex;
flex-direction: column;
box-sizing: border-box;
position: relative;
border-radius: 4px;
background-color: black;
color: white;
padding-bottom: 14px;
padding-top: 14px;
}
.card-header {
display: flex;
font-size: 14px;
padding: 0px 16px;
margin-bottom: 1rem;
}
.card-content {
display: block;
padding: 0 16px;
}
.card-footer {
display: block;
padding: 12px 16px 0px;
margin-top: 1rem;
border-top: 1px solid #444;
}
app.component.ts
<app-card>
<div card-header>Содержимое заголовка</div>
<div card-content>Основное содержимое</div>
<div card-footer>Содержимое нижнего колонтитула</div>
</app-card>
Стили приложения card применяются, но не применяются стили card-header, card-content и card-footer. Я могу найти атрибуты в DOM на элементе, но классов нет. Моя цель – получить такую же функциональность, как у карты Angular Material, просто с моим собственным оформлением и как мой собственный компонент.
Я попробовал написать пример, и он сработал: StackBlitz
Я думаю, что причина, по которой это не работает, заключается в том, что вы используете самостоятельный компонент, но директива не является самостоятельной директивой.
Таким образом, директива не выполняется. <div card-header>Содержимое заголовка</div>
останется как есть.
Ответ или решение
Чтобы корректно использовать классовые селекторы в Angular-директивах, необходимо внимательно следить за тем, как реализованы ваши компоненты и директивы, а также за тем, как Angular обрабатывает дерево компонентов. Давайте подробно разберем вашу ситуацию и предложим пути решения.
Проблема с применением классовых селекторов
В вашем коде объявлены три директивы: CardHeader
, CardContent
и CardFooter
, каждая из которых использует декоратор @Directive
с указанием селектора для атрибутов и установки класса через host
. Вы наблюдаете, что классы card-header
, card-content
и card-footer
не применяются к элементам в размещаемом контенте. На основе кода и описания ситуации можно сделать вывод, что проблема связана с тем, как работает механизм внедрения и инициализации директив в Angular.
Важные моменты при использовании директив в Angular
-
Селекторы и инкапсуляция: При использовании
ng-content
в вашем компоненте, Angular не автоматически инициализирует директивы на этих элементах. То есть, когда вы используете<ng-content>
, дочерние элементы могут не иметь доступа к директивам, если они не были созданы внутриapp-card
компонента. -
Стоять на своем пути: Стандартной практикой является создание директив, которые могут без труда использоваться внутри компонентов. В данном случае вы можете создавать связанные директивы и импортировать их в соответствующий модуль компонента или использовать их как "standalone directives".
Решение проблемы
Чтобы ваше стилизованное содержимое корректно отображалось, я рекомендую добавить директивы как "standalone". Ниже приведен пример, как это можно сделать:
Изменение директив на standalone
import { Directive } from '@angular/core';
@Directive({
selector: '[card-header], [cardHeader]',
standalone: true,
host: { class: 'card-header' }
})
export class CardHeader {}
@Directive({
selector: '[card-content], [cardContent]',
standalone: true,
host: { class: 'card-content' }
})
export class CardContent {}
@Directive({
selector: '[card-footer], [cardFooter]',
standalone: true,
host: { class: 'card-footer' }
})
export class CardFooter {}
Импортирование директив в основном компоненте
После того как вы сделали директивы standalone, убедитесь, что вы импортируете их в ваш основной модуль или в компонент, который использует app-card
.
import { CardHeader, CardContent, CardFooter } from './card.component';
@Component({
selector: 'app-card',
standalone: true,
templateUrl: './card.component.html',
styleUrls: ['./card.component.scss'],
imports: [CardHeader, CardContent, CardFooter] // Импорт директив здесь
})
export class CardComponent {}
Тестирование и отладка
После установки директив как standalone и их импорта в компонент, проведите тестирование вашего приложения:
- Убедитесь, что страница загружается без ошибок.
- Проверьте в инструментах разработчика, появятся ли классы (
card-header
,card-content
,card-footer
) на соответствующих элементах. - Убедитесь, что стили применяются корректно.
Заключение
Переход на использование классовых селекторов в Angular-директивах может вызывать сложности, особенно в контексте вложенных компонентов и механизма инкапсуляции. Однако, следуя вышеизложенным рекомендациям и сделав ваши директивы standalone, вы обеспечите их правильное обнаружение и применение в вашем приложении. Такой подход не только приведет к корректному отображению стилей, но и повысит модульность и переиспользуемость вашего кода.