Не удается расширить Angular Material MatDivider в пользовательском компоненте-обертке

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

Я создаю собственный обертку-компонент в Angular, чтобы расширить MatDivider от Angular Material, но сталкиваюсь с проблемами, когда стили и свойства компонента из MatDivider не применяются правильно. Цель состоит в том, чтобы абстрагировать MatDivider, чтобы пользователи моей библиотеки не нуждались в прямом импорте Angular Material.

Вот моя текущая настройка:

Обертка-компонент (TypeScript):

import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';
import { MatDivider } from '@angular/material/divider';

/**
 * `DividerComponent` является оберткой вокруг MatDivider от Angular Material.
 * Он расширяет MatDivider и предназначен для предоставления той же функциональности
 * 
 */
@Component({
  selector: 'aal-divider',
  standalone: true,
  template: '',
  styleUrls: ['./divider.component.scss'],
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DividerComponent extends MatDivider {}

SCSS (divider.component.scss):

@use '@angular/material' as mat;
@use '../../themes/theming' as *;

@include mat.divider-theme($aal--mat-light-theme);

Проблема

Когда я пытаюсь использовать DividerComponent, предполагаемые стили и поведение из MatDivider не кажутся примененными, даже несмотря на то, что я расширил класс. Я вижу класс mat-divider в DOM, но он не отображается как ожидалось.

Вопрос

  1. Существует ли конкретное ограничение в Angular Material, которое препятствует расширению компонентов, таких как MatDivider?
  2. Существуют ли другие подходы к обертке MatDivider эффективно, чтобы я мог управлять темой без необходимости пользователям напрямую импортировать Angular Material?

Любые рекомендации или предложения по обходным путям будут очень признательны!

Примечание: Главное, на что нужно обратить внимание в этом подходе, заключается в том, что существует дополнительный элемент, который оборачивает ваш материал-компонент, поэтому, если есть какой-либо CSS-селектор, такой как mat-list > mat-divider, он не будет применяться из-за дополнительного вставленного HTML-элемента.


Вам нужно добавить MatListModule в массив импортов, чтобы использовать директиву.

imports: [MatDividerModule],

Вам также нужно всегда включать mixins темы компонента внутри обертки, я использовал :host в качестве элемента-обертки.

@use '@angular/material' as mat;
@use '../styles' as *;
:host {
  @include mat.divider-theme($theme);
}

Полный код:

TS:

import {
  ChangeDetectionStrategy,
  Component,
  ViewEncapsulation,
} from '@angular/core';
import { MatDividerModule } from '@angular/material/divider';
import { MatListModule } from '@angular/material/list';
import { MatDivider } from '@angular/material/divider';

/**
 * `DividerComponent` является оберткой вокруг MatDivider от Angular Material.
 * Он расширяет MatDivider и предназначен для предоставления той же функциональности
 *
 */
@Component({
  selector: 'aal-divider',
  standalone: true,
  imports: [MatDividerModule],
  template: '<mat-divider></mat-divider>',
  styles: [
    `
    @use '@angular/material' as mat;
    @use '../styles' as *;
    :host {
      @include mat.divider-theme($theme);
    }
  `,
  ],
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DividerComponent extends MatDivider {}

/**
 * @title Основной разделитель
 */
@Component({
  selector: 'divider-overview-example',
  templateUrl: 'divider-overview-example.html',
  standalone: true,
  imports: [MatListModule, DividerComponent, MatDividerModule],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DividerOverviewExample {}

HTML:

<mat-list>
  <mat-list-item>Элемент 1</mat-list-item>
  <aal-divider></aal-divider>
  <mat-list-item>Элемент 2</mat-list-item>
  <aal-divider></aal-divider>
  <mat-list-item>Элемент 3</mat-list-item>
</mat-list>

<hr />

<mat-list>
  <mat-list-item>Элемент 1</mat-list-item>
  <aal-divider></aal-divider>
  <mat-list-item>Элемент 2</mat-list-item>
  <aal-divider></aal-divider>
  <mat-list-item>Элемент 3</mat-list-item>
</mat-list>

Демо Stackblitz

Ответ или решение

Проблема расширения Angular Material MatDivider в пользовательском компоненте

Создание пользовательского компонента, который оборачивает элементы Angular Material, в частности MatDivider, может вызвать несколько сложностей из-за особенностей обработки стилей и структуры DOM. В этом ответе мы разберем возможные причины проблем с корректным наследованием стилей и поведением MatDivider, а также предложим альтернативные подходы для решения данной ситуации.

Проектирование и текущая реализация

Для начала, рассмотрим вашу текущее определение компонента DividerComponent, который вы создали для оборачивания MatDivider:

import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';
import { MatDivider } from '@angular/material/divider';

@Component({
  selector: 'aal-divider',
  standalone: true,
  template: '',
  styleUrls: ['./divider.component.scss'],
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DividerComponent extends MatDivider {}

В приведенном коде наблюдаются некоторые ключевые моменты:

  1. Неправильный шаблон: Ваша реализация включает пустой шаблон (template: ''), что не позволяет MatDivider правильно отобразиться в DOM.
  2. Структура стилей: В SCSS-стилях вы пытаетесь применить тему divider-theme, что нуждается в дополнительной обертке, такой как :host.

Причины проблемы

  1. Структура DOM: Когда вы наследуете от MatDivider, Angular Material добавляет своего рода обертку, что может вызвать проблемы с применением глобальных стилей, например, селекторов типа mat-list > mat-divider, так как в результирующем HTML-дереве может быть лишний элемент.

  2. Отсутствие передачи контента: Поскольку шаблон пустой, MatDivider не получает контент для рендеринга.

  3. Импорт модулей: Вы не указали в imports необходимые модули, такие как MatDividerModule, что может касаться всех директив, используемых в вашем компоненте.

Рекомендации по улучшению

Для исправления проблем с вашим пользовательским компонентом, вам следует:

  1. Обновить шаблон: Замените пустой шаблон на корректное использование MatDivider.
template: '<mat-divider></mat-divider>',
  1. Использование :host: Примените SCSS-миксины темы внутри селектора :host, чтобы гарантировать, что стили применяются корректно.
:host {
  @include mat.divider-theme($theme);
}
  1. Импорт MatDividerModule: Не забудьте о включении модуля в массив imports.
imports: [MatDividerModule],

Полный обновленный код

Следует объединить все вышеперечисленные изменения в окончательном варианте вашего компонента:

import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';
import { MatDividerModule } from '@angular/material/divider';
import { MatDivider } from '@angular/material/divider';

@Component({
  selector: 'aal-divider',
  standalone: true,
  imports: [MatDividerModule],
  template: '<mat-divider></mat-divider>',
  styles: [`
    @use '@angular/material' as mat;
    :host {
      @include mat.divider-theme($theme);
    }
  `],
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DividerComponent extends MatDivider {}

Заключение

Избегание проблем с переносом стилей и функциональности MatDivider в пользовательский компонент подразумевает корректное использование Angular и пакета Material. Обязательно уделяйте внимание структуре HTML, применяемым стилям и необходимым импортам. Эти действия помогут вам создать более предсказуемый и управляемый интерфейс для пользователей вашего библиотеки, не требующих прямого импорта Angular Material.

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

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