Содержание
Встроенные директивы предназначены для отображения контента по условию (*ngIf, *ngSwitch), повторения контента для каждого элемента в массиве (*ngFor), для применения стилей к html-элементам (ngStyle) или назначения им классов (ngCLass). Существуют и менее распространенные директивы, их мы рассмотрим ниже. Также пользователь может создавать свои директивы при необходимости.
Директивы обеспечивают выполнение самых распространенных и фундаментальных задач разработки веб-приложений и закладывают основу для адаптации контента, отображаемого для пользователя, на основании данных в приложении.
Директива *ngFor
Начнем с этой директивы для того, чтобы отобразить наши демо данные из условной «базы данных» в таблице. Для этого идеально подходит встроенная директива *ngFor.
Для начала нам нужно изменить шаблон. Давайте удалим блок form-inline, который мы добавляли в предыдущем разделе (он нужен был только для демонстрации). Также добавим директиву *ngFor второй строке в таблице, т.к. первая строка это заголовок он будет одним и неизменным. Нам нужно, чтобы для каждого товара повторялась именно строка таблицы и в ней выводились данные для каждого из них.
<div class="container">
<div class="row">
<h3 class="mr-3 mt-2">{{ title }}</h3>
<table class="table table-striped text-center mt-3">
<thead class="thead-dark">
<tr>
<th>ID</th>
<th>Название</th>
<th>Цена</th>
<th>Кол-во</th>
</tr>
</thead>
<tr *ngFor="let product of products">
<td>{{ product.id }}</td>
<td>{{ product.name }}</td>
<td>${{ product.price }}</td>
<td>{{ product.quantity }}</td>
</tr>
</table> <!-- /table -->
</div>
</div>
Теперь более подробно о том, что мы сделали.
Тегу tr мы добавляем директиву *ngFor (синтаксис именно такой, со *), в директиве мы указываем, что на каждой итерации переменная product будет принимать значение следующего элемента в массиве products. Массив products был определен в нашем сервисе src/app/shared/data.service.ts и его значения получены в компоненте src/app/all-servers/all-servers.component.ts. Элементами данного массива являются объекты, в каждом из которых заданы свойства id, name, price, quantity.
Далее, при помощи строковой интерполяции, мы в каждой ячейке выводим значение необходимого свойства ({{products.name}}). Таким образом после изменения кода у вас должны вывестись все 4 товара по порядку.
Директива *ngFor имеет несколько разных значений, которые могут быть присвоены переменным.
| Значение | Тип данных | Описание |
|---|---|---|
| index | (number) | Это порядковый номер текущего элемента. Отсчет начинается с 0 |
| odd | (boolean) | Вернет true, если текущий элемент нечетный |
| even | (boolean) | Вернет true, если текущий элемент четный |
| first | (boolean) | Вернет true, если текущий элемент является первым |
| last | (boolean) | Вернет true, если текущий элемент является последним |
Директива *ngIf
Следующей наиболее используемой встроенной директивой является *ngIf. Она позволяет вносить какие-либо изменения в шаблон при выполнении заданных условий. Давайте рассмотрим пример как мы можем ее использовать.
Скажем, мы хотим выводить товары с остатком (свойство quantity) больше 3.
Для этого внесем изменения в наш шаблон:
...
<table class="table table-striped text-center mt-3">
<thead class="thead-dark">
<tr>
<th>ID</th>
<th>Название</th>
<th>Цена</th>
<th>Кол-во</th>
</tr>
</thead>
<ng-container *ngFor="let product of products">
<tr *ngIf="product.quantity > 3">
<td>{{ product.id }}</td>
<td>{{ product.name }}</td>
<td>${{ product.price }}</td>
<td>{{ product.quantity }}</td>
</tr>
</ng-container>
</table> <!-- /.table -->
...
Итак, тут вы видите, что я добавил новый тег ng-container и прописал ему директиву *ngFor, а тегу tr *ngIf. Это сделано потому, что в Angular (начиная со второй версии, если не ошибаюсь) нельзя использовать две директивы в одном теге. Т.е. невозможно прописать их обе тегу tr, это вызовет ошибку. Было бы проще, если бы нам нужно было вывести данные не в таблице, а в обычных тегах div, тогда мы могли бы просто добавить один лишний тег только для условия ngIf. В случае с таблицами пришлось изменить шаблон именно на такой.
После того как мы определяем переменную product, в которую, напомню, входит объект товара, мы с помощью условия *ngIf="product.quantity > 3" выводим только те товары, кол-во которых больше 3. Вот таким образом работает данная директива.
Также условие можно расширить, к примеру, написать
*ngIf="product.quantity > 3 && product.price == 500"
Т.е. будут выведены товары, если выполнены оба условия (остаток больше 3 и цена равная 500).
*ngIf…else и *ngIf…then…else
В Angular есть возможность использовать if...else конструкцию. Мы рассмотрим их на примерах, не относящихся к нашей практической части. Синтаксис в таком случае будет следующий
<div *ngIf="isLoggedIn; else loggedOut"> Добро пожаловать! </div> <ng-template #loggedOut> Пожалуйста, авторизуйтесь. </ng-template>
В данном случае будет проверенна переменная isLoggedIn, и если она вернет true, то будет выведен блок div. В этом случае ng-template даже не будет отображаться в DOM. Если же isLoggedIn вернет false, тогда будет отображен шаблон #loggedOut.
Еще один вариант записи
<ng-container
*ngIf="isLoggedIn; then loggedIn; else loggedOut">
</ng-container>
<ng-template #loggedIn>
<div>
Добро пожаловать!
</div>
</ng-template>
<ng-template #loggedOut>
<div>
Пожалуйста, авторизуйтесь.
</div>
</ng-template>
Эта запись использует следующую логику: ngIf = expression ? then : else;, т.е. если isLoggedIn=true, тогда будет загружен шаблон #loggedIn, если false — #loggedOut
Директивы ngClass и ngStyle
Давайте начнем с директивы ngClass. Синтаксис данной директивы такой — [ngClass]="выражение" и в зависимости от результата выполнения данного выражения, директива может принимать:
- Строку — имена классов, разделенные пробелами. Например,
"col-md-6 text-center mb-2" - Массив — каждый элемент массива назначается как отдельный класс. Например,
["col-md-6", "text-center", "mb-2"] - Объект (или правильнее сказать тип Map) — каждое свойство объекта задается как новый класс. В качестве значения свойства записывается выражение, которое должно вернуть true, чтобы данный класс был добавлен элементу.
Для начала проверим первый вариант и будем выводить класс для заголовка таблицы с помощью директивы ngClass. Для этого изменим шаблон, и в качестве выражения передадим функцию headClass(), которая просто будет возвращать строку с названием класса.
... <table class="table table-striped text-center mt-3"> <thead [ngClass]="headClass()"> ...
...
editTitle() {
this.title = 'Новый тайтл';
}
headClass() {
return 'thead-dark';
}
}
В принципе, это не обязательно было делать с помощью функции, можно было бы просто объявить переменную, задать ей значение thead-dark, и потом вызвать в шаблоне, но тогда без круглых скобок.
Функция у нас получилась самая примитивная, но она могла бы возвращать какое-либо значение в зависимости от выполнения условий. Например, если сегодня четный день недели тогда return false, в противном случае return "thead-dark". Вообщем все, что вы можете сделать при помощи обычного JavaScript, просто тут показано как применить это к Angular.
После этого проверьте изменения, заголовок у таблицы должен остаться черного цвета. Если посмотреть с помощью консоли разработчика в браузере, то увидим следующее

Также можно вернуть массив классов
... <table [ngClass]="tableClass()"> <thead [ngClass]="headClass()"> ...
...
headClass() {
return 'thead-dark';
}
tableClass() {
return ['table', 'table-striped', 'text-center', 'mt-3'];
}
}
Теперь давайте рассмотрим наиболее сложный вариант использования ngClass, когда в качестве выражения передается map. Основное отличие map и объекта заключается в том, что в качестве пары 'ключ': значение для map можно передать любой тип данных (в данном случае в качестве значения свойства мы будем передавать условие, которое нам будет возвращать true или false).
Давайте изменим наш шаблон (обратите внимание, что я изменил условие в строке 14, чтобы выводились все товары):
...
<ng-container *ngFor="let product of products">
<tr *ngIf="product.quantity > 0">
<td>{{ product.id }}</td>
<td>{{ product.name }}</td>
<td>${{ product.price }}</td>
<td><span class="badge" [ngClass]="quantityBagde(product.quantity)">{{ product.quantity }}</span></td>
</tr>
</ng-container>
...
И добавим новый метод в класс нашего компонента:
...
tableClass() {
return ['table', 'table-striped', 'text-center', 'mt-3'];
}
quantityBagde(quantity) {
return {
'badge-success': quantity >= 3,
'badge-danger': quantity < 3
}
}
В последней ячейке мы добавляем тег span с классом badge, а затем добавляем директиву [ngClass], которая в зависимости от количества товаров будет нам возвращать нужны класс — bg-success если больше или равно 3 и bg-danger если 2 и меньше. Сохраните изменения и проверьте, что у вас получилось.

Директива [ngStyle] позволяет задать несколько свойств при помощи объекта-карты. Качестве ключа могут передаваться два типа: просто css-свойство или с единицами измерения. Мы рассмотрим оба варианта для демонстрации. Внесем изменения в шаблон:
...
<ng-container *ngFor="let product of products">
<tr *ngIf="product.quantity > 0">
<td>{{ product.id }}</td>
<td>{{ product.name }}</td>
<td [ngStyle]="getStyles(product.price)">${{ product.price }}</td>
<td><span class="badge" [ngClass]="quantityBagde(product.quantity)">{{ product.quantity }}</span></td>
</tr>
</ng-container>
...
И добавим метода для нашего компонента:
...
getStyles(price) {
return {
fontSize: '1.1rem',
'letterSpacing.px': 1,
backgroundColor: price > 300 ? 'green' : 'red'
}
}
...
Мы применили стилевые свойства к ячейке с ценой. Изменили размера шрифта, отступы между буквами/цифрами и в зависимости от стоимости меняем фоновый цвет ячейки.
Директива ngSwitch
Директива [ngSwitch] используется для выбора элементов, которые будут включены в документ HTML, на основании сравнения выражения с результатами отдельных выражений, определяемых в директивах *ngSwitchCase. Если ни одно из значений *ngSwitchCase не совпадает, используется элемент, к которому применена директива *ngSwitchDefault
Давайте изменим наш шаблон
...
<ng-container *ngFor="let product of products">
<tr *ngIf="product.quantity > 0">
<td>{{ product.id }}</td>
<td>{{ product.name }}</td>
<td [ngStyle]="getStyles(product.price)">${{ product.price }}</td>
<td [ngSwitch]="product.quantity">
<span class="badge" [ngClass]="quantityBagde(product.quantity)">{{ product.quantity }}</span><br>
<span *ngSwitchCase="1">Остался последний</span>
<span *ngSwitchCase="2">Осталось всего два</span>
<span *ngSwitchCase="3">Осталось еще три</span>
<span *ngSwitchCase="5">Товара достаточно</span>
<span *ngSwitchDefault>Значение по умолчанию</span>
</td>
</tr>
</ng-container>
...
Тут важно отметить, что мы использовали переменную которую определяли в шаблоне (product.quantity), но можно указать также переменную или метод, которые были определены в классе компонента. Просто я выбрал этот вариант для удобства.
*ngSwitchDefault сработает в том случае, если нет ни одного *ngSwitchCase удовлетворяющего условию (как в случае с товаром с id=1)
Значения, которые указываются в *ngSwitchCase являются выражениями, т.е. могут быть переданы не только строки, но также переменные или методы. Если вы хотите сравнить *ngSwitchCase со строкой, тогда после двойных кавычек нужно указать еще одинарные — *ngSwitchCase="'string'". Если же вы укажите *ngSwitchCase="string", то Angular будет искать переменную string в компоненте.
Директива ngTemplateOutlet
Данная директива используется для повторения блока с контентом в разных местах шаблона.
К примеру, у нас есть блок рекламы который мы хотим вывести перед началом статьи, и после окончания. Этот код одинаков. В таком случае мы можем использовать директиву ngTemplateOutlet
<!-- Определяем шаблон --> <ng-template #advertisement> <div>--- Код рекламы ---</div> </ng-template> <!-- 1-ый вывод блока --> <ng-template [ngTemplateOutlet]="advertisement"></ng-template> <div id="content"> Статья </div> <!-- 2-ой вывод блока --> <ng-template [ngTemplateOutlet]="advertisement"></ng-template>
Сначала мы определяем шаблон с помощью ссылочной переменной #advertisement (имя вы можете выбрать произвольное), затем в том месте где вы хотите использовать данный шаблон просто тегу ng-template добавляете директиву [ngTemplateOutlet]="advertisement"
Добавить комментарий