7

入力フィールドの値など、ユーザーが何かを変更したときに変更を自動的に保存するアプリケーションに取り組んでいます。autosave保存イベントを自動的にトリガーする必要があるすべてのフォーム フィールドに追加されるディレクティブを作成しました。

テンプレート:

   <input ng-model="fooCtrl.name" autosave>
   <input ng-model="fooCtrl.email" autosave>

指令:

  .directive('autosave', ['$parse', function  ($parse) {

    return {
      restrict: 'A',
      require: 'ngModel',
      link: function (scope, element, attrs, ngModel) {

        function saveIfModelChanged () {
          // save object containing name and email to server ...
        }

        ngModel.$viewChangeListeners.push(function () {
          saveIfModelChanged();
        });
      }
    };
  }]);

これまでのところ、これはすべてうまくいきます。ただし、入力フィールドが有効な電子メール アドレスであることを検証するなど、検証をミックスに追加すると、undefinedviewValue が無効な電子メール アドレスに変更されるとすぐに modelValue が設定されます。

私がやりたいことはこれです: 最後の有効なモデル値を記憶し、自動保存時にこれを使用します。ユーザーが電子メール アドレスを無効に変更した場合でもname、 とを含むオブジェクトemailはサーバーに保存する必要があります。現在の validnameと最後の valid を使用しますemail

次のように、最後の有効な modelValue を保存することから始めました。

検証が追加されたテンプレート:

   <input type="email" ng-model="fooCtrl.name" autosave required>
   <input ng-model="fooCtrl.email" autosave required>

lastModelValue を保存するディレクティブ:

  .directive('autosave', ['$parse', function  ($parse) {

    return {
      restrict: 'A',
      require: 'ngModel',
      link: function (scope, element, attrs, ngModel) {

        var lastModelValue;

        function saveIfModelChanged () {

          // remeber last valid modelValue
          if (ngModel.$valid) {
             lastModelValue = ngModel.$modelValue;
          }

          // save object containing current or last valid
          // name and email to server ...
        }

        ngModel.$viewChangeListeners.push(function () {
          saveIfModelChanged();
        });
      }
    };
  }]);

私の質問は、lastModelValue保存中に使用する方法ですが、ビューに無効な値を保持していますか?

編集:

以下のJugnuが示唆する別の可能性は、バリデーターでビルドをラップして操作することです。

私は次のことを試みました:既存のすべてのバリデーターをラップし、最後の有効な値を記憶して、検証が失敗した場合に復元します:

Object.keys(ngModel.$validators).forEach(function(validatorName, index) {
    var validator = ngModel.$validators[validatorName];
    ngModel.$validators[validatorName] = createWrapper(validatorName, validator, ngModel);
});

function createWrapper(validatorName, validator, ngModel){

  var lastValid;

  return function (modelValue){

    var result = validator(modelValue);

    if(result) {
      lastValid = modelValue;
    }else{
        // what to do here? maybe asign the value like this:
      // $parse(attrs.ngModel).assign(scope, lastValid);
    }

    return result;
  };
}

しかし、このアプローチを継続する方法もわかりません。AngularJS を起動せずにモデル値を設定し、新しく設定された値を検証することはできますか?

4

3 に答える 3

3

フィールドの変更ごとにオブジェクト全体を送信するため、そのオブジェクト全体の最後の有効な状態をどこかに保持する必要があります。私が念頭に置いているユースケース:

  1. 有効なオブジェクトがあります{ name: 'Valid', email: 'Valid' }
  2. 名前を無効に変更します。名前入力に配置されたautosaveディレクティブは、それ自体の最後の有効な値を認識しているため、正しいオブジェクトが送信されます。
  3. メールも無効に変更します。電子メール入力に配置されたautosaveディレクティブは、それ自身の最後の有効な値を知っていますが、名前のそれは知りません。最後の既知の適切な値が集中化されていない場合、次のようなオブジェクト{ name: 'inalid', email: 'Valid' }が送信されます。

だから提案:

  1. 編集中のオブジェクトのサニタイズ済みコピーを保管してください。サニタイズとは、無効な初期値を有効な元の値 (ゼロ、ヌルなど) に置き換える必要があることを意味します。そのコピーをコントローラーメンバーとして公開しますfooCtrl.lastKnowngood
  2. たとえばautosave、次のように、最後の既知の良好な状態を知らせます。

    <input ng-model="fooCtrl.email" autosave="fooCtrl.lastKnowngood" required />
    
  3. そのオブジェクトの最後の既知の適切なローカル値を保持します。ng-modelたとえば、次のように表現を使用します。

    var lastKnownGoodExpr = $parse(attrs.autosave);
    var modelExpr = $parse(attrs.ngModel);
    
    function saveIfModelChanged () {
        var lastKnownGood = lastKnownGoodExpr(scope);
    
        if (ngModel.$valid) {
            // trick here; explanation later
            modelExpr.assign({fooCtrl: lastKnownGood}, ngModel.$modelValue);
        }
    
        // send the lastKnownGood object to the server!!!
    }
    
  4. オブジェクトを送信しlastKnownGoodます。

トリック、その欠点、および改善方法:ローカル モデルの値をlastKnownGoodオブジェクトに設定する場合、現在のスコープとは異なるコンテキスト オブジェクトを使用します。このオブジェクトは、コントローラーが呼び出されることを前提としていますfooCtrl(行を参照modelExpr.assign({fooCtrl: lastKnownGood}, ...))。より一般的なディレクティブが必要な場合は、ルートを別の属性として渡すことができます。次に例を示します。

<input ng-model="fooCtrl.email" autosave="fooCtrl.lastKnowngood" required
    autosave-fake-root="fooCtrl" />

また、最初のコンポーネントを決定するために、式を自分で解析することもできng-modelます。たとえば、部分文字列 0 → ドットの最初の出現 (これも単純化されています)。

もう 1 つの欠点は、より複雑なパス (一般的なケース) を処理する方法です。たとえばfooCtrl.persons[13].address['home'].street、それはユースケースではないようです。


ちなみにこれ:

ngModel.$viewChangeListeners.push(function () {
    saveIfModelChanged();
});

次のように簡略化できます。

ngModel.$viewChangeListeners.push(saveIfModelChanged);
于 2015-09-17T12:03:15.633 に答える