В этой статье мы узнаем о двух способах реализации реактивных и многоразовых форм, которые можно использовать как подформы, а также как отдельные формы.
Я предполагаю, что вы знаете, что такое формы, и вы работали с реактивными формами Angular.
Мы рассмотрим два подхода к достижению этого:
- Использование ControlContainer, который позволяет нам передавать родительскую форму ее подформам, которые реализованы как компоненты.
- И @ViewChild, который помогает нам получить экземпляр класса компонента, который в данном случае будет экземпляром компонента формы.
Angular предоставляет два подхода к настройке и реализации форм: это реактивные формы и управляемые шаблонами. В этой статье мы будем иметь в виду реактивные формы.
Демо пользовательского интерфейса форм
Как показано на видео выше, у нас есть одна большая форма с двумя подформами:
- HeroComponent (родительский)
- PowersComponent (подформа, запрошенная с помощью декоратора @ViewChild)
- HobbiesComponent (подформа, реализованная с использованием класса ControlContainer)
Реализация подформы с использованием декоратора @ViewChild
Давайте начнем с наилучшего подхода, который использует декоратор, который позволяет родительской форме (HeroComponent) запрашивать экземпляр класса компонента нашей дочерней / подформы, которая является PowersComponent, см. код ниже:
Как вы можете видеть в шаблоне HeroComponent, компонент PowersComponent не требует дополнительных входных данных.
<nb-card> <nb-card-header>Super Power</nb-card-header> <nb-card-body class="col"> <app-powers></app-powers> // here </nb-card-body> </nb-card>
Но если вы можете проверить класс HeroComponent, вы заметите, как мы получаем экземпляр класса компонента подформы Powers и вызываем метод createFormGroup общедоступная функция-член, чтобы вернуть нам экземпляр конфигурации PowersComponent FormGroup.
@ViewChild(PowersComponent, { static: true }) public powersComponent: PowersComponent;
Обратите внимание, как параметры @ViewChild используют static: true для разрешения экземпляра компонента как можно скорее, чтобы у нас есть экземпляр подформы класса PowersComponent.
И проверьте строку 21 файла HeroComponent.ts, который создает форму PowersComponent.
powers: this.powersComponent.createFormGroup(),
Речь идет о том, как создать повторно используемую и реактивную подформу с помощью @ViewChild decorator, к которому я склонен, потому что считаю его простым в реализации и более легким в обслуживании в качестве подформы и родительские формы отделены друг от друга и обеспечивают следующие преимущества:
- Родительская форма вообще не должна знать об экземпляре подчиненной формы. Все, что ему нужно, - это чтобы у класса компонента подформы была открытая функция-член createFormGroup, которая возвращает экземпляр FormGroup.
- Изменение конфигурации формы подчиненной формы никоим образом не влияет на конфигурацию родительской формы или ее шаблон.
Круто, а как насчет настройки модульного теста для такой настройки?
Просто проверьте код ниже
Модульный тест для подхода @ViewChild
Если вы проверите строки 11 и 25 приведенного выше кода, вы заметите, как мы создаем поддельный компонент PowersComponent с поддельной функцией, которая имитирует функцию createFormGroup с помощью createSpyObj функция.
const powersComponent = jasmine.createSpyObj('PowersComponent', ['createFormGroup']); // line 11 ... component.powersComponent = powersComponent; // line 25
Без указанного выше в файле спецификации HeroComponent вы получите сообщение об ошибке при выполнении тестов, которые:
TypeError: Cannot read property 'createFormGroup' of undefined
Реализация подформы с помощью ControlContainer
Этот подход сбивает с толку и требует много работы, но он также жизнеспособен.
В шаблоне HeroComponent у нас есть разметка другой нашей подформы, HobbiesComponent
<app-hobbies [parentForm]="heroForm [formGroup]="heroForm.get('hobbies')" ></app-hobbies>
Как видите, эта разметка формы использует директиву [formGroup], которая поставляется с ControlContainer, позволяя родительской форме передавать подчиненную форму самой подчиненной форме. , при необходимости.
Также обратите внимание, что подчиненная форма ожидает ввода, который должен быть родительской формой, это также позволяет нам иметь доступ к родительской форме внутри подчиненной формы, если необходимо, см. источник этой вложенной формы -форма ниже.
Модульный тест для этой настройки выглядит следующим образом.
Создайте базовую заглушку HobbiesComponent, которая будет записью в объявлениях TestBed родительской формы.
Это все, что нам нужно в родительской форме, чтобы настроить тест для подчиненной формы с помощью ControlContainer.
А ниже приведены спецификации компонента HobbiesComponent.
Если вы посмотрите на исходный код выше, то увидите, что мы создаем фиктивную FormGroup, используя фиктивный FormBuilder, определенный в строке 9. Затем мы используем фиктивный FormGroup как токен для ControlContainer в строке 18.
Нет очевидных преимуществ использования последнего подхода, но он работает с некоторыми недостатками в отношении ремонтопригодности.
- Если подчиненная форма изменяется, возможно, имя свойства было переименовано, родительская форма должна иметь конфигурацию подчиненной формы, которая совпадает с этим изменением для подчиненной формы.
- Если родительская форма изменяется в конфигурации подчиненной формы, подчиненной форме необходимо знать об этом, поэтому для обновления шаблона, например, значение formControlName или любой другой код в подчиненной форме, которая использует экземпляр подчиненной формы FromGroup.
- Необходимость писать заглушки для всех компонентов вашей подформы, чтобы избежать каких-либо предупреждений в тестах.
- Если вы хотите повторно использовать эту форму в любой другой родительской форме, эта форма должна иметь конфигурацию для этой подформы.
hobbies: this.formBuilder.group({ favoriteHobby: ['', Validators.required] })
Резюме
Первый подход, в котором используется декоратор @ViewChild, является лучшим, он полностью многоразовый, инкапсулированный и простой в обслуживании.
Последний подход работает, но имеет множество недостатков.
Но, в конце концов, это зависит от вас, потому что это ваше мышление, ваш код.
Надеюсь, вам понравилось читать и вы кое-что узнали.
Вы можете ознакомиться с полным исходным кодом для этого на GitHub.