Пишем декораторы c Angular Ivy
В восьмой версии Angular в экспериментальном режиме будет доступен новый движок рендеринга Ivy. Oдна из его возможностей — более гибкое создание собственных декораторов. Что такое декораторы и зачем их писать разберем в этой статье.
Декораторы
При написании кода большую часть функционала можно разделить на функции, компоненты или модули, но в некоторых случаях это невозможно.
Такую функциональность называют сквозной (от англ. scattered — разбросанный или англ. tangled — переплетённый), так как её реализация распределена по различным модулям программы. Сквозная функциональность приводит к рассредоточенному и запутанному коду, сложному для понимания и сопровождения.
Для этих целей идеально подходит патерн декоратор:
Декоратор (англ. Decorator) — структурный шаблон проектирования, предназначенный для динамического подключения дополнительного поведения к объекту.
Шаблон Декоратор предоставляет гибкую альтернативу практике создания подклассов с целью расширения функциональности.
Говоря простыми словами, декоратор может добавлять к классу новые свойства и методы и запускать под капотом невидимые механизмы. Это позволяет делать сами компоненты более чистыми и читабельными и использовать в разработке принцип don’t repeat yourself (DRY), а вся магия происходит под капотом декоратора.
В JavaScript декораторы появились в ES2016 (Exploring EcmaScript Decorators). Декоратор — это функция, которую надо добавить перед классом свойством или методом со знаком @ . В Angular примерами декораторов являются: Component, Input, Output и т.д.
Вот как будет выглядеть простейший декоратор класса
Добавим декоратор, написанный выше, к Angular компоненту
И в консоли мы получим ссылку на класс компонента
Как это работает в IVY
В текущем движке Renderer 2 после компиляции приложения метаданные и фабрика для создания компонента хранятся отдельно от самого класса компонента, поэтому большинство параметров компонента недоступны для изменения в рантайме. С новым движком IVY после компиляции все метаданные попадают в статическое свойство класса компонента ngComponentDef, и с ними можно производить манипуляции до создания компонента.
Доработаем декоратор Logger, который будет логировать метаданные компонента. Также добавим к нему входной параметр tag. Для этого надо обернуть декоратор в функцию и через её аргументы передать входные данные
Вот что получим в консоли
Тут можно найти селектор, фабрику для создания компонента, шаблон, стили, хуки жизненного цикла и многое другое.
Декораторы для работы с хуками компонента
Как было сказано выше, хуки жизненного цикла можно получить через ngComponentDef. Разберем как можно с ними работать так же на примере логирования.
В первую очередь необходимо получить экземпляр компонента. Он пригодится ниже для привязки контекста к методам жизненного цикла. Делается это через подмену фабрики ngComponentDef.factory
Все хуки хранятся в метаданных компонента в методах onInit, onDestroy и т. д. Так же, как и с фабрикой, сохраняем оригинальный хук в константу, после чего подменяем его, добавляя нужный функционал. И тут же необходимо использовать созданный экземпляр компонента, для того чтоб привязать контекст через call или apply.
Работа с экземпляром компонента
Для манипуляций с данными компонента, как и в примере выше, необходимо создать его экземпляр и подменить оригинальную фабрику, которую использует Angular при создании компонента.
В этом примере мы будем логировать обращение к свойствам и методам компонента, поэтому нам понадобится еще объект Proxy
Прокси (proxy) — особый объект, смысл которого — перехватывать обращения к другому объекту и, при необходимости, модифицировать их.
Синтаксис:
let proxy = new Proxy(target, handler)
Здесь:
target
– объект, обращения к которому надо перехватывать.
handler
– объект с «ловушками»: функциями-перехватчиками для операций кtarget
.
Проще говоря, прокси является прослойкой между конечным объектом и тем, кто к нему обращается, и при обращении может проверять и модернизировать данные.
Для того чтобы перехватить запись и чтение свойств и методов компонента, надо в декораторе:
- Создать экземпляр компонента;
- Сделать его proxy;
- Вернуть прокси в фабрике вместо оригинала.
Вот как выглядит перехват и логирование чтения свойств компонента
При логировании чтения методов принцип остается тот же. Только надо не забыть добавить проверку на то что это метод и через call или apply привязать к методу контекст this.
Вот что получим в консоли
Заключение
Декораторы действительно крутая фича. С их помощью можно красивее и элегантнее использовать такие вещи, как логирование, сторы для хранение данных, отписку при уничтожении компонента и другие вещи.
Будем ждать выхода Angular 8 и полноценной поддержки IVY.