JavaRush /Курсы /Модуль 4: Node.js, Next.js и Angular /Атрибутивные директивы в Angular:

Атрибутивные директивы в Angular: ngClass, ngStyle

Модуль 4: Node.js, Next.js и Angular
14 уровень , 3 лекция
Открыта

1. Динамическое управление CSS-классами

В Angular атрибутивные директивы — это такие директивы, которые добавляют или изменяют поведение/стилизацию существующего элемента, но не влияют на структуру DOM.

Примеры из жизни:

  • Подсветить выбранный элемент списка.
  • Сделать кнопку неактивной при определённом условии.
  • Динамически менять цвет текста, если значение превышает порог.

Вместо того чтобы писать кучу условий в CSS или вручную управлять классами через JavaScript, Angular предлагает декларативный и лаконичный способ — прямо в шаблоне, через директивы.

Синтаксис и базовые примеры

ngClass позволяет добавлять, удалять или комбинировать CSS-классы на элементе в зависимости от условий в вашем компоненте.

Простой пример

<button [ngClass]="'btn-primary'">Сохранить</button>

Здесь к кнопке всегда будет применён класс btn-primary.

Динамический класс

<button [ngClass]="isActive ? 'btn-success' : 'btn-danger'">
  {{ isActive ? 'Активен' : 'Не активен' }}
</button>

Если isActive === true, кнопка будет зелёной (btn-success), иначе — красной (btn-danger).

Множественные классы

ngClass принимает не только строку, но и массив или объект:

<div [ngClass]="['first-class', 'second-class']">...</div>

или

<div [ngClass]="{ 'highlight': isSelected, 'error': hasError }">
  {{ message }}
</div>

Пояснение:

  • Если isSelected == true, добавится класс highlight.
  • Если hasError == true, добавится класс error.

Всё вместе: объект с условиями

<li *ngFor="let user of users"
    [ngClass]="{
      'admin': user.isAdmin,
      'online': user.isOnline,
      'selected': selectedUser === user
    }">
  {{ user.name }}
</li>

Пояснение:
- Если пользователь админ — класс admin.
- Если онлайн — класс online.
- Если выбран — класс selected.

Пример в контексте приложения

Допустим, у нас есть список задач, и мы хотим выделять выполненные задачи:

// task-list.component.ts
export class TaskListComponent {
  tasks = [
    { title: 'Купить хлеб', done: false },
    { title: 'Выучить Angular', done: true }
  ];
}
<!-- task-list.component.html -->
<ul>
  <li *ngFor="let task of tasks"
      [ngClass]="{ 'done': task.done }">
    {{ task.title }}
  </li>
</ul>
/* styles.css */
.done {
  text-decoration: line-through;
  color: gray;
}

2. ngStyle: динамическое управление стилями

ngStyle позволяет задавать CSS-стили прямо из компонента, причём значения могут рассчитываться на лету.

Простой пример

<div [ngStyle]="{ color: 'red', 'font-weight': 'bold' }">
  Важное сообщение!
</div>

Динамические стили

<div [ngStyle]="{ color: isError ? 'red' : 'green' }">
  {{ isError ? 'Ошибка' : 'Всё хорошо' }}
</div>

Использование переменных

export class ExampleComponent {
  fontSize = 18;
}
<p [ngStyle]="{ 'font-size.px': fontSize }">
  Текст с динамическим размером
</p>

.px после имени свойства указывает, что значение будет с единицей измерения px.

Пример с вычисляемыми стилями

export class ProgressBarComponent {
  progress = 45; // %
}
<div class="progress-bar-bg">
  <div class="progress-bar-fill"
       [ngStyle]="{ width: progress + '%' }"></div>
</div>
.progress-bar-bg {
  width: 100%;
  height: 20px;
  background: #eee;
}
.progress-bar-fill {
  height: 100%;
  background: #4caf50;
  transition: width 0.3s;
}

Сравнение: ngClass vs ngStyle

ngClass ngStyle
Что управляет? CSS-классами Инлайновыми стилями (style="...")
Как задаётся? Строка, массив, объект Объект: { 'color': 'red' }
Когда использовать? Для сложных стилей, переиспользуемых классов Для динамических, вычисляемых стилей

Совет:
Старайтесь использовать ngClass для стандартных стилей (цвета, размеры и т.п.), а ngStyle — для уникальных или динамически вычисляемых значений (например, ширина прогресс-бара, opacity, transform).

3. Практика: динамическое оформление списка задач

Давайте расширим наше мини-приложение "Список задач", чтобы добавить динамическое оформление:

// task-list.component.ts
export class TaskListComponent {
  tasks = [
    { title: 'Купить хлеб', done: false, urgent: true },
    { title: 'Выучить Angular', done: true, urgent: false }
  ];
}
<!-- task-list.component.html -->
<ul>
  <li *ngFor="let task of tasks"
      [ngClass]="{ 'done': task.done, 'urgent': task.urgent }"
      [ngStyle]="{
        'font-size.px': task.urgent ? 20 : 16,
        'background': task.done ? '#e0e0e0' : '#fff'
      }">
    {{ task.title }}
  </li>
</ul>
.done {
  text-decoration: line-through;
  color: gray;
}
.urgent {
  color: red;
}

Что происходит:
- Если задача выполнена — зачёркнута и серая.
- Если задача срочная — красная и крупнее.
- Если задача и выполнена, и срочная — оба класса применяются (и оба стиля работают).

4. Полезные нюансы

Как работает Angular под капотом?

  • ngClass добавляет/удаляет классы через свойство classList DOM-элемента.
  • ngStyle модифицирует инлайновые стили элемента.

Можно ли комбинировать обычный class и ngClass?

Да! Всё, что вы напишете просто как class="my-static-class", будет применено вместе с тем, что "добавит" ngClass.

<button class="btn" [ngClass]="{ 'btn-success': isActive }">
  Кнопка
</button>

Если isActive — true, то итоговый HTML будет:

<button class="btn btn-success">Кнопка</button>

Можно ли использовать выражения?

Да, в квадратных скобках можно писать любые корректные выражения TypeScript.

<div [ngClass]="getClass(user)"></div>

Где getClass(user) — функция, возвращающая строку, массив или объект.

Порядок применения классов и стилей

  • Если класс или стиль задан и через обычный атрибут, и через директиву, Angular объединяет их.
  • Если одно и то же свойство задано и через ngStyle, и через CSS-класс, инлайновый стиль (ngStyle) имеет приоритет.

Как избежать "CSS-каши"?

  • Не злоупотребляйте инлайновыми стилями: их сложно переопределять и поддерживать.
  • Для сложных условий лучше выносить вычисления в методы компонента или геттеры.

5. Типичные ошибки при использовании ngClass и ngStyle

Ошибка №1: Передача строки с несколькими классами без массива

<!-- Ошибка: строка без пробелов воспринимается как один класс -->
<div [ngClass]="'btn btn-primary'"></div>

Angular корректно обработает такую строку, но если вы динамически формируете строку с пробелами, убедитесь, что она именно строка, а не массив. Лучше явно использовать массив или объект для сложных условий.

Ошибка №2: Использование обычного class и [ngClass] с одинаковыми классами

Если один и тот же класс указан и через class="...", и через [ngClass], Angular просто объединит их. Это редко приводит к ошибкам, но может запутать при отладке.

Ошибка №3: Пробелы в ключах объекта

<div [ngClass]="{ 'done ': isDone }"></div> <!-- ошибка из-за пробела -->

Ошибка №4: Неучёт единиц измерения в ngStyle

<div [ngStyle]="{ 'font-size': fontSize }"></div>

Если fontSize = 20, то без единицы измерения браузер не применит стиль. Нужно писать так:

<div [ngStyle]="{ 'font-size.px': fontSize }"></div>

Ошибка №5: Слишком сложные выражения в шаблоне

Не превращайте шаблон в математический квест. Если вычисление класса или стиля сложное — вынесите его в метод компонента или геттер.

Ошибка №6: Попытка добавить несуществующий класс

Если в CSS нет класса, который вы динамически добавляете, эффекта не будет. Проверяйте, что ваши классы действительно описаны в стилях.

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ