Преобразование данных с помощью пайпов¶
28.02.2022
Используйте пайпы для преобразования строк, сумм валют, дат и других данных для отображения на экране. Пайпы — это простые функции, которые можно использовать в шаблонных выражениях для приема входного значения и возврата преобразованного значения. Пайпы полезны тем, что их можно использовать во всем приложении, объявляя каждый пайп только один раз. Например, с помощью пайпа можно отобразить дату в виде April 15, 1988, а не в виде необработанной строки.
Пример приложения, используемый в данной теме, приведен в живом примере.
Angular предоставляет встроенные пайпы для типичных преобразований данных, включая преобразования для интернационализации (i18n), которые используют информацию о локали для форматирования данных. Ниже перечислены часто используемые встроенные пайпы для форматирования данных:
Трубы | Подробнее |
---|---|
DatePipe | Формирование значения даты в соответствии с правилами локали. |
UpperCasePipe | Преобразование текста в верхний регистр. |
LowerCasePipe | Преобразование текста во все строчные регистры. |
CurrencyPipe | Преобразование числа в строку валюты, отформатированную в соответствии с правилами локали. |
DecimalPipe | Преобразование числа в строку с десятичной точкой, отформатированную в соответствии с правилами локали. |
PercentPipe | Преобразование числа в строку с процентами, оформленную в соответствии с правилами локали. |
- Полный список встроенных пайпов приведен в документации pipes API.
- Подробнее об использовании пайпов для интернационализации (i18n) см. в разделе Форматирование данных в зависимости от локали.
Создание пайпов для инкапсуляции пользовательских преобразований и использование пользовательских пайпов в шаблонных выражениях.
Предварительные условия¶
Для использования пайпов необходимо иметь базовое представление о следующем:
- Typescript и программирования HTML5
- Шаблоны в HTML со стилями CSS
- Components
Использование пайпа в шаблоне¶
Чтобы применить пайп, используйте символ пайпа (|
) в выражении шаблона, как показано в следующем примере кода, вместе с именем пайпа, которым является date
для встроенного DatePipe
. Вкладки в примере выглядят следующим образом:
Файлы | Подробности |
---|---|
app.component.html | Использует date в отдельном шаблоне для отображения дня рождения. |
hero-birthday1.component.ts | Использует тот же пайп как часть встроенного шаблона в компоненте, который также устанавливает значение дня рождения. |
1 |
|
1 2 3 4 5 6 7 8 9 10 |
|
Значение birthday
компонента через оператор пайпа, |
, попадает в функцию date
.
Преобразование данных с помощью параметров и цепочек пайпов¶
Используйте необязательные параметры для точной настройки выходных данных пайпа. Например, используйте CurrencyPipe
с кодом страны, например EUR, в качестве параметра. Шаблонное выражение {{ amount | currency:'EUR' }}
преобразует amount
в валюту в евро. После имени пайпа (currency
) следует символ двоеточия (:
) и значение параметра ('EUR'
).
Если пайп принимает несколько параметров, разделяйте их значения двоеточиями. Например, {{ amount | currency:'EUR':'Euros'}}
добавляет второй параметр, строковый литерал 'Euros'
, в выходную строку. В качестве параметра можно использовать любое допустимое выражение шаблона, например, строковый литерал или свойство компонента.
Некоторые пайпы требуют как минимум один параметр и допускают большее количество необязательных параметров, например SlicePipe
. Например, {{ slice:1:5 }}
создает новый массив или строку, содержащую подмножество элементов, начиная с элемента 1
и заканчивая элементом 5
.
Пример: Форматирование даты¶
На вкладках в следующем примере демонстрируется переключение между двумя различными форматами ('shortDate'
и 'fullDate'
):
- Шаблон
app.component.html
использует параметр формата дляDatePipe
(с именемdate
), чтобы показать дату как 04/15/88. - Компонент
hero-birthday2.component.ts
привязывает параметр формата пайпа к свойствуformat
компонента в секцииtemplate
и добавляет кнопку для события click, привязанную к методуtoggleFormat()
компонента. - Метод
toggleFormat()
компонентаhero-birthday2.component.ts
переключает свойствоformat
компонента между короткой формой ('shortDate'
) и более длинной ('fullDate'
).
1 2 3 |
|
1 2 3 4 |
|
1 2 3 4 5 6 7 8 9 10 11 |
|
При нажатии на кнопку Toggle Format формат даты чередуется между 04/15/1988 и Friday, April 15, 1988.
Параметры формата пайпа date
см. в разделе DatePipe.
Пример: Применение двух форматов с помощью цепочки пайпов¶
Цепочка пайпов позволяет сделать так, чтобы выход одного пайпа стал входом для следующего.
В следующем примере цепочки пайпов сначала применяют формат к значению даты, а затем преобразуют отформатированную дату в заглавные символы. Первая вкладка шаблона src/app/app.component.html
связывает DatePipe
и UpperCasePipe
для отображения дня рождения как APR 15, 1988. На второй вкладке шаблона src/app/app.component.html
параметр fullDate
передается в date
перед цепочкой в uppercase
, в результате чего получается FRIDAY, APRIL 15, 1988.
1 2 |
|
1 2 |
|
Создание пайпов для пользовательских преобразований данных¶
Создайте пользовательские пайпы для инкапсуляции преобразований, которые не предусмотрены встроенными пайпами. Затем используйте пользовательские пайпы в шаблонных выражениях так же, как и встроенные пайпы — для преобразования входных значений в выходные значения для отображения.
Пометка класса как пайпа¶
Чтобы пометить класс как пайп и снабдить его конфигурационными метаданными, примените к классу @Pipe
decorator. Используйте UpperCamelCase (общее соглашение для имен классов) для имени класса пайпа и camelCase для соответствующей строки name
. Не используйте дефисы в строке name
. Подробности и примеры см. в разделе Имена пайпов.
Используйте name
в шаблонных выражениях так же, как и для встроенного пайпа.
-
Включите свой пайп в поле
declarations
метаданныхNgModule
для того, чтобы он был доступен шаблону.См. файл
app.module.ts
в примере приложения (живой пример).Подробнее см. раздел NgModules.
-
Зарегистрируйте пользовательские пайпы.
Команда Angular CLI
ng generate pipe
регистрирует пайп автоматически.
Использование интерфейса PipeTransform¶
Реализуйте интерфейс PipeTransform
в своем пользовательском классе пайпа для выполнения трансформации.
Angular вызывает метод transform
со значением привязки в качестве первого аргумента и любыми параметрами в качестве второго аргумента в виде списка и возвращает преобразованное значение.
Пример: Экспоненциальное преобразование значения¶
В игре может потребоваться реализовать преобразование, которое экспоненциально увеличивает значение для повышения силы героя. Например, если количество очков героя равно 2, то при экспоненциальном увеличении силы героя на 10 количество очков будет равно 1024. Для этого преобразования используйте пользовательский пайп.
В следующем примере кода показаны два определения компонентов:
Файлы | Подробности |
---|---|
exponential-strength.pipe.ts | Определяет пользовательский пайп с именем exponentialStrength и методом transform , выполняющим преобразование. Определяет аргумент метода transform (exponent ) для параметра, передаваемого в пайп. |
power-booster.component.ts | Демонстрирует использование пайпа с указанием значения (2 ) и параметра экспоненты (10 ). |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
В браузере отображается следующее:
1 2 3 |
|
Чтобы исследовать поведение пайпа exponentialStrength
в живом примере, измените значение и необязательную экспоненту в шаблоне.
Обнаружение изменений с помощью привязки данных в пайпах¶
Для отображения значений и реагирования на действия пользователя в пайпе используется привязка данных. Если в качестве данных используется примитивное входное значение, например String
или Number
, или ссылка на объект, например Date
или Array
, Angular выполняет пайп всякий раз, когда обнаруживает изменение входного значения или ссылки.
Например, можно изменить предыдущий пример пользовательского пайпа, чтобы использовать двустороннее связывание данных с ngModel
для ввода суммы и повышающего коэффициента, как показано в следующем примере кода.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
Пайп exponentialStrength
выполняется каждый раз, когда пользователь изменяет значение "нормальной мощности" или "коэффициента усиления".
Angular обнаруживает каждое изменение и немедленно запускает пайп. Это хорошо подходит для примитивных входных значений. Однако если вы изменяете что-то внутри составного объекта (например, месяц даты, элемент массива или свойство объекта), вам необходимо понять, как работает обнаружение изменений и как использовать нечистый
пайп.
Как работает обнаружение изменений¶
Angular ищет изменения в значениях, связанных с данными, в процессе change detection, который запускается после каждого события DOM: каждого нажатия клавиши, перемещения мыши, тиканья таймера и ответа сервера. Следующий пример, в котором не используется пайп, демонстрирует, как Angular использует свою стандартную стратегию обнаружения изменений для отслеживания и обновления отображения каждого героя в массиве heroes
. На вкладках примера показано следующее:
Файлы | Подробности |
---|---|
flying-heroes.component.html (v1) | Повторитель *ngFor отображает имена героев. |
flying-heroes.component.ts (v1) | Предоставляет героев, добавляет героев в массив и сбрасывает массив. |
1 2 3 4 5 6 7 8 9 10 11 12 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Angular обновляет отображение каждый раз, когда пользователь добавляет героя. Если пользователь нажимает кнопку Reset, Angular заменяет heroes
новым массивом исходных героев и обновляет отображение. Если добавить возможность удаления или изменения героя, то Angular обнаружит эти изменения и также обновит отображение.
Однако выполнение пайпа для обновления дисплея при каждом изменении приведет к снижению производительности приложения. Поэтому Angular использует более быстрый алгоритм обнаружения изменений для выполнения пайпа, как описано в следующем разделе.
Обнаружение чистых изменений примитивов и ссылок на объекты¶
По умолчанию пайпы определяются как чистые, поэтому Angular выполняет пайп только тогда, когда обнаруживает чистое изменение входного значения. Чистое изменение — это либо изменение примитивного входного значения (такого как String
, Number
, Boolean
или Symbol
), либо изменение объектной ссылки (такой как Date
, Array
, Function
или Object
).
Чистый пайп должен использовать чистую функцию, то есть функцию, которая обрабатывает входные данные и возвращает значения без побочных эффектов. Другими словами, при одинаковых входных данных чистая функция всегда должна возвращать одинаковые выходные данные.
При использовании чистого пайпа Angular игнорирует изменения внутри составных объектов, например, вновь добавленный элемент существующего массива, поскольку проверка примитивного значения или ссылки на объект выполняется гораздо быстрее, чем глубокая проверка различий внутри объектов. Angular может быстро определить, можно ли пропустить выполнение пайпа и обновление представления.
Однако чистый пайп с массивом в качестве входных данных может работать не так, как вы хотите. Чтобы продемонстрировать эту проблему, изменим предыдущий пример так, чтобы отфильтровать список героев только по тем героям, которые умеют летать. Используйте FlyingHeroesPipe
в ретрансляторе *ngFor
, как показано в следующем коде. Вкладки для примера выглядят следующим образом:
- Шаблон (
flying-heroes.component.html (flyers)
) с новым пайпом - Реализация пользовательского пайпа
FlyingHeroesPipe
(flying-heroes.pipe.ts
)
1 2 3 |
|
1 2 3 4 5 6 7 8 9 10 |
|
Теперь приложение демонстрирует неожиданное поведение: Когда пользователь добавляет летающих героев, ни один из них не появляется в разделе "Heroes who fly". Это происходит потому, что код, добавляющий героя, заталкивает его в массив heroes
:
1 |
|
Детектор изменений игнорирует изменения элементов массива, поэтому пайп не выполняется.
Причина, по которой Angular игнорирует измененный элемент массива, заключается в том, что ссылка на массив не изменилась. Поскольку массив остался прежним, Angular не обновляет отображение.
Один из способов добиться желаемого поведения — изменить саму ссылку на объект. Замените массив новым массивом, содержащим новые измененные элементы, а затем введите новый массив в пайп. В предыдущем примере создайте массив с добавлением нового героя и присвойте его heroes
. Angular обнаружит изменение в ссылке на массив и выполнит пайп.
Подведем итог: если вы мутируете входной массив, то чистый пайп не выполняется. Если же входной массив заменить, то пайп будет выполнен, и отображение будет обновлено.
Приведенный пример демонстрирует изменение кода компонента для реализации пайпа.
Чтобы сохранить независимость компонента от HTML-шаблонов, использующих пайпы, в качестве альтернативы можно использовать нечистый пайп для обнаружения изменений в составных объектах, таких как массивы, как описано в следующем разделе.
Обнаружение нечистых изменений внутри составных объектов¶
Чтобы выполнить пользовательский пайп после изменения внутри составного объекта, например, изменения элемента массива, необходимо определить пайп как impure
для обнаружения нечистых изменений. Angular выполняет нечистый пайп каждый раз, когда обнаруживает изменение при каждом нажатии клавиши или движении мыши.
Хотя нечистый пайп может быть полезен, использовать его следует с осторожностью. Долго работающий нечистый пайп может значительно замедлить работу приложения.
Сделать пайп нечистым, установив его флаг pure
в значение false
:
1 2 3 4 |
|
В приведенном ниже коде показана полная реализация FlyingHeroesImpurePipe
, которая расширяет FlyingHeroesPipe
для наследования его характеристик. Из примера видно, что больше ничего менять не нужно — единственное отличие — установка флага pure
как false
в метаданных пайпа.
1 2 3 4 5 |
|
1 2 3 4 5 6 7 8 9 10 |
|
FlyingHeroesImpurePipe
является хорошим кандидатом на роль нечистого пайпа, поскольку функция transform
является тривиальной и быстрой:
1 |
|
От FlyingHeroesImpureComponent
можно получить FlyingHeroesComponent
. Как показано в следующем коде, изменяется только пайп в шаблоне.
1 2 3 |
|
Чтобы убедиться в том, что при добавлении героев на экране происходит обновление, посмотрите живой пример.
Развертывание данных из наблюдаемого объекта¶
Observables позволяют передавать сообщения между частями приложения. Обсерватории рекомендуются для обработки событий, асинхронного программирования и работы с несколькими значениями. Наблюдаемые могут передавать одиночные или множественные значения любого типа как синхронно (как функция передает значение вызывающему ее пользователю), так и асинхронно по расписанию.
Подробности и примеры использования observables см. в Observables Overview.
Используйте встроенный AsyncPipe
для приема наблюдаемого объекта в качестве входного и автоматической подписки на него. Без этого пайпа коду компонента пришлось бы подписываться на наблюдаемую, чтобы потреблять ее значения, извлекать разрешенные значения, выставлять их для связывания и отписываться от них при уничтожении наблюдаемой, чтобы избежать утечек памяти. AsyncPipe
— это нечистый пайп, который избавляет ваш компонент от необходимости поддерживать подписку и продолжать передавать значения из наблюдаемой таблицы по мере их поступления.
Следующий пример кода привязывает наблюдаемое хранилище строк сообщений (message$
) к представлению с помощью пайпа async
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
Кэширование HTTP-запросов¶
Для [взаимодействия с внутренними сервисами по протоколу HTTP] (understanding-communicating-with-http.md 'Communicating with backend services using HTTP') сервис HttpClient
использует наблюдаемые и предлагает метод HttpClient.get()
для получения данных с сервера. Асинхронный метод посылает HTTP-запрос и возвращает наблюдаемую, которая выдает в ответ запрошенные данные.
Как было показано в предыдущем разделе, используйте нечистую трубу AsyncPipe
для приема наблюдаемого объекта в качестве входного и автоматической подписки на него. Вы также можете создать нечистый пайп для выполнения и кэширования HTTP-запроса.
Нечистые пайпы вызываются каждый раз, когда для компонента выполняется обнаружение изменений, что может происходить как раз в несколько миллисекунд. Чтобы избежать проблем с производительностью, вызывайте сервер только при изменении запрашиваемого URL, как показано в следующем примере, и используйте пайп для кэширования ответа сервера. На вкладках показано следующее:
- Пайп
fetch
(fetch-json.pipe.ts
). -
Компонент harness (
hero-list.component.ts
) для демонстрации запроса, использующий шаблон, определяющий две привязки к пайпу, запрашивающему героев из файлаheroes.json
.Вторая привязка связывает пайп
fetch
со встроеннымJsonPipe
для отображения тех же данных о героях в формате JSON.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
В предыдущем примере точка останова на запросе пайпа на получение данных показывает следующее:
- Каждое связывание получает свой собственный экземпляр пайпа.
- Каждый экземпляр пайпа кэширует свой URL и данные и обращается к серверу только один раз.
Пайпы fetch
и fetch-json
отображают героев в браузере следующим образом:
1 2 3 4 5 6 7 8 |
|
Встроенная функция JsonPipe предоставляет возможность диагностировать загадочную неудачную привязку данных или проверить объект на предмет будущей привязки.
Пайпы и старшинство¶
Оператор пайп имеет более высокий приоритет, чем тернарный оператор (?:
), поэтому a ? b : c | x
будет разобран как a ? b : (c | x)
. Оператор пайп не может быть использован без круглых скобок в первом и втором операндах ?:
.
В силу старшинства, если вы хотите, чтобы пайп применялся к результату тернарного оператора, оберните все выражение круглыми скобками; например, (a ? b : c) | x
.
1 2 |
|