web-dev-qa-db-ja.com

検証のためにフォームを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を汚染しないようにしたいと思います。フォームに必要なコンポーネントアーキテクチャを実現するより良い方法はありますか?

19

更新-form-nameform-referenceに変更されました。フォームの名前だけでなく、実際のフォーム参照を渡していました。これは好きなように呼び出すことができますが、実際には何であるかを明確にしてください。

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-messagesを使用することで実現すると思います)。

その後、私のコンポーネントで次のように記述します。

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

このすべてが誰かの役に立てば幸いです:-)

22
RGonzalez

これは、一部のユーザーが役立つと思われる別のアプローチです。 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>

技術的にはまだvmを汚染していると思いますが、少なくとも階層全体に明示的に渡す必要はありません。

6文字の編集要件を満たすテキストを追加し、ピリオドをカンマに変更して例を修正しました。期間は新人を混乱させます。

3
John Barton