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 нет класса, который вы динамически добавляете, эффекта не будет. Проверяйте, что ваши классы действительно описаны в стилях.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ