Передача формы в компонент AngularJS для проверки

Я перевожу свою устаревшую кодовую базу на новую компонентную архитектуру, продвигаемую с AngularJS 1.5. Я столкнулся с проблемой при выполнении этого для больших форм. Традиционно я бы приложил проверку формы следующим образом:

<form name="myForm">
  <input type="text" name="input1" ng-model="vm.input1" required />
  <div ng-messages="myForm.input1.$error">
    <div ng-message="required">Please fill out this field.</div>
  </div>
  <!-- many more inputs -->
</form>

При переходе к архитектуре компонента я должен явно передать форму компоненту:

<form name="vm.myForm">
  <my-awesome-input-component model="vm.input1" form="vm.myForm"><my-awesome-input-component>
  <!-- many more inputs -->
</form>

Я хотел бы избежать загрязнения vm с моей формой. Есть ли лучший способ достичь желаемой архитектуры компонентов для форм?

2 ответа

Решение

Обновить - изменил имя формы на ссылку формы, поскольку было неясно, что мы передаем фактическую ссылку на форму, а не просто имя формы. Это можно назвать как угодно, просто будь уверен, что это на самом деле.

Как говорится в комментарии Iain Reid, вам не нужно использовать vm для этого. Вы просто называете форму как хотите, а затем передаете это имя вашему компоненту, чтобы оно выглядело так:

<form name="myForm" ng-submit="ctrl.someFunction()" novalidate>
   <my-input form-reference="myForm"></my-input>
   <button type="submit">Some button</button>
</form>

Удостоверьтесь, что вы пишете "novalidate" в своей форме, чтобы отключить проверки браузера по умолчанию, если вы хотите обрабатывать проверки самостоятельно (что, я думаю, вы используете ng-сообщения).

Затем оттуда, на моем компоненте я бы написал что-то вроде:

angular.module("myApp")
  .component("myInput",{
     templateUrl:'path/to/template.html'
     bindings:{
       formReference:'<',
       myInputModel:'<',
       onUpdate:'&'
     },
     controller: MyInputController
  }

А затем во входном шаблоне:

<input type="text" name="myInput" ng-model="$ctrl.myInputModel" ng-change="$ctrl.update($ctrl.myInputModel)" required />
<div ng-messages="$ctrl.formReference.myInput.$error">
  <div ng-message="required">Please fill out this field.</div>
</div>

Несколько дополнительных заметок о привязках и о том, как передавать и обновлять модели:

  • '<': означает одностороннюю привязку, которую Angular говорит использовать для всех компонентов с этого момента. Чтобы обновить значение и иметь двустороннюю привязку, нам нужно включить функцию "onUpdate".
  • onUpdate: '&' я говорю здесь о том, что я передам функцию для обновления модели (обратный вызов для событий компонента).

Так что в контроллере ввода я бы написал что-то вроде:

function MyInputController(){
    var ctrl = this;
    ctrl.update = function(value){
        ctrl.onUpdate({value: value});
    };
}

И, наконец, когда я использую свой компонент внутри формы:

<form name="myForm" ng-submit="ctrl.someFunction()" novalidate>
   <my-input form-reference="myForm" my-input-model="ctrl.anyModelIWant" on-update="ctrl.updateMyInput(value)"></my-input>
   <button type="submit">Some button</button>
</form>

И контроллер формы будет иметь функцию:

...
ctrl.updateMyInput = function(value){
   ctrl.anyModelIWant = value;
}
...

Официальные документы: https://docs.angularjs.org/guide/component

Я надеюсь, что все это помогает кому-то там:-)

Вот еще один подход, который некоторые могут найти полезным. использование require включить родителя form в вашем $ctrl:

angular.module("myApp")
    .component("myInput",{
        templateUrl:'path/to/template.html'
        bindings:{
            myInputModel:'<',
            onUpdate:'&'
        },
        controller: MyInputController,
        require: {
            form: '^form'
        }
}

В шаблоне ввода:

<input type="text" name="myInput" ng-model="$ctrl.myInputModel" ng-change="$ctrl.update($ctrl.myInputModel)" required />
    <div ng-messages="$ctrl.form.myInput.$error">
    <div ng-message="required">Please fill out this field.</div>
</div>

Нет необходимости явно передавать форму в ваш компонент как form автоматически добавляется в ваш $ctrl:

<form name="myForm" ng-submit="ctrl.someFunction()" novalidate>
    <my-input my-input-model="ctrl.anyModelIWant" on-update="ctrl.updateMyInput(value)"></my-input>
    <button type="submit">Some button</button>
</form>

Полагаю, технически вы все еще загрязняете свой виртуальный компьютер, но, по крайней мере, вам не нужно явно передавать его по всей иерархии.

Добавление текста, удовлетворяющего требованиям редактирования 6 символов, изменило точку на запятую, чтобы исправить пример. Период сбивает с толку для новичков.

На самом деле вам не нужно передавать родительскую форму для этого. В шаблоне вашего awesome-компонента добавьте тег ng-form и используйте его:

Шаблон для вашего компонента:

<ng-form name="myComponentForm">
    <input type="number" ng-model="$ctrl.myModel" name="myField"/>
    <span ng-show="myComponentForm.myField.$invalid">There's an error</span>
</ng-form>

В этом смысл директивы ngForm - иметь дочерние формы в директивах и компонентах для проверки подгруппы полей формы.

Другие вопросы по тегам