Как использовать селекторы классов при использовании директив Angular

Вопрос или проблема

В данный момент я пытаюсь воспроизвести логику, которую 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

  1. Селекторы и инкапсуляция: При использовании ng-content в вашем компоненте, Angular не автоматически инициализирует директивы на этих элементах. То есть, когда вы используете <ng-content>, дочерние элементы могут не иметь доступа к директивам, если они не были созданы внутри app-card компонента.

  2. Стоять на своем пути: Стандартной практикой является создание директив, которые могут без труда использоваться внутри компонентов. В данном случае вы можете создавать связанные директивы и импортировать их в соответствующий модуль компонента или использовать их как "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 и их импорта в компонент, проведите тестирование вашего приложения:

  1. Убедитесь, что страница загружается без ошибок.
  2. Проверьте в инструментах разработчика, появятся ли классы (card-header, card-content, card-footer) на соответствующих элементах.
  3. Убедитесь, что стили применяются корректно.

Заключение

Переход на использование классовых селекторов в Angular-директивах может вызывать сложности, особенно в контексте вложенных компонентов и механизма инкапсуляции. Однако, следуя вышеизложенным рекомендациям и сделав ваши директивы standalone, вы обеспечите их правильное обнаружение и применение в вашем приложении. Такой подход не только приведет к корректному отображению стилей, но и повысит модульность и переиспользуемость вашего кода.

Оцените материал
Добавить комментарий

Капча загружается...