Содержание
Встроенные директивы предназначены для отображения контента по условию (*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"
Добавить комментарий