Кастомные элементы форм в 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.
Однако, использование кастомного элемента формы не ограничено только модель-ориентированными формами. Его также можно использовать и в шаблон-ориентированных формах — просто использовать синтаксис двухсторонней привязки данных:
На этом все. Надеюсь этот материал был полезен для Вас.