Кастомные элементы форм в Angular
Реактивные формы — это одна из лучших вещей в Angular. Поскольку они упрощают работу с большими и действительно динамичными формами. В этой мы рассмотрим создание кастомного элемента формы.
Могут быть разные причины для создания собственных элементов управления формой.
- можно сделать кастомный селект, который будет использоваться для выбора типа контакта и страны;
- можно сделать собственный элемент для ввода пароля и его подтверждения, который будет содержать собственный валидатор и возвращать в форму всего одно значение;
- или же, если бы у нас была необходимость в элементе 'agree terms & conditions' — мы бы могли создать для этой цели кастомный
formControl
, который использовали бы во многих компонентах приложения.
Но, прежде чем мы начнем создавать пользовательский элемент управления формой, давайте определим какими свойствами и поведением он должен обладать:
- корректно отображать значение в UI;
- корректно передавать введенное пользователем значение из UI в модель;
- передавать состояние валидации в DOM, что бы к компоненту можно было применить необходимые стили;
- в случае необходимости — иметь собственные валидаторы;
- работать с модель-ориентированными формами;
- работать с шаблон-ориентированными формами;
Создание собственного select'a
В этом примере мы рассмотрим создание достаточно простого компонента для выбора одного значения из списка возможных.
Для этих целей мы могли бы использовать элемент select
. И, в большинстве случаев, я советовал бы Вам использовать именно его. Но мы хотим рассмотреть как можно создавать собственные элементы форм и такой компонент отлично подходит для этой тривиальной задачи. Кроме того, может быть достаточно много причин, почему нам необходим кастомный элемент для выбора значения.
Итак, давайте начнем с создания компонента CustomSelectComponent
, который принимает из родительского компонента массив options
и отображает его в шаблоне. Также у нас есть метод OptionsSelect
, который сохранят выбранное пользователем значение в свойство innerValue
.
Этот компонент работает хорошо и мы уже можем использовать его в шаблоне формы, как только задекларируем его в модуле нашего приложения.
Однако, этот компонент никак не связан с нашей формой, а нам необходимо, чтобы он умел работать с реактивными (и не только) формами, передавал выбранное значение в модель и т.д.
Иными словами — нам нужна возможность использовать form api директивы, например formControl
:
Хорошо, но как мы можем сделать из обычного компонента компонент типа formControl
? Для этого нам нужно узнать что такое ControlValueAccessor
, потому что это именно то, что использует Angular для построения коммуникаций между моделью формы и DOM элементом.
Реализация ControlValueAccessor в пользовательском элементе
Компонент может опционально реализовывать интерфейс ControlValueAccessor
, который позволяет записывать значение в компонент и прослушивать его изменения. Этот интерфейс используется в директивах NgModel
и FormControlName
.
В кастомных элементах формы мы должны добавить собственную реализацию этого интерфейса.
Интерфейс ControlValueAccessor
выглядит следующим образом:
Итак, давайте перепишем наш компонент и реализуем в нем методы описанные выше:
Здесь мы регистрируем обработчики событий, а также в методе writeValue
:
- проверяем что новое значение является необходимым для нас типом данных;
- удостоверяемся что в нашем списке опций есть элемент с необходимым значением;
- записываем новое значение в локальную модель
selectedOption;
- вызываем метод onChange, который информирует другие компоненты о том, что значение было изменено.
Вызов же метода writeValue
происходит в методе optionSelect
, который используется в качестве обработчика клика на элементе списка. Также в этом методе вызывается функция onTouched и изменяется свойство open для того, чтобы убрать выпадающий список.
CustomSelectComponent
почти готов к использованию. Но хотя мы и реализовали все необходимые методы интерфейса ControlValueAccessor
, Angular все еще не воспринимает его как таковой, потому что его необходимо зарегистрировать.
Регистрация ControlValueAccessor
Для регистрации аксессора используется мультипровайдер с токеном NG_VALUE_ACCESSOR
. Его следует добавить в список провайдеров нашего компонента:
Здесь мы сообщаем этому токену о нашем компоненте. Также, следует обратить внимание на использование forwardRef
. Это необходимо, поскольку класс нашего компонента еще не будет определен, когда провайдер будет зарегистрирован и нам нужно сообщить конструктору провайдера о том, что необходимо дождаться определения класса.
Использование кастомного элемента в реактивной форме
После того, как мы создали нашу модель формы, мы можем связать наш кастомный form Control с моделью с помощью директивы formControlName
или formControl
.
Мы же хотим модифицировать форму из предыдущей статьи и изменить элемент для выбора типа контакта, поэтому в нашем случае мы просто заменили тег select
на custom-select
.
Однако, использование кастомного элемента формы не ограничено только модель-ориентированными формами. Его также можно использовать и в шаблон-ориентированных формах — просто использовать синтаксис двухсторонней привязки данных:
На этом все. Надеюсь этот материал был полезен для Вас.