9

AngularJS では、$scope 変数を $watch し、それを使用して別の変数を更新する方法は簡単です。しかし、2 つのスコープ変数が互いに監視する必要がある場合のベスト プラクティスは何でしょうか?

例として、華氏を摂氏に、またはその逆に変換する双方向コンバーターがあります。華氏に「1」と入力しても問題ありませんが、「1.1」と入力すると、Angular は入力したばかりの華氏をわずかに異なる値 (1.1000000000000014) に上書きする前に、少し跳ね返ります。

function TemperatureConverterCtrl($scope) {
  $scope.$watch('fahrenheit', function(value) {
    $scope.celsius = (value - 32) * 5.0/9.0;
  });
  $scope.$watch('celsius', function(value) {
    $scope.fahrenheit = value * 9.0 / 5.0 + 32;
  });
}

ここにプランカーがあります: http://plnkr.co/edit/1fULXjx7MyAHjvjHfV1j?p=preview

Angularが「バウンス」するのを止め、入力した値をそのまま使用するように強制するさまざまな方法は何ですか?たとえば、フォーマッタまたはパーサー(またはその他のトリック)を使用しますか?

4

2 に答える 2

9

最も簡単で、最速で、最も正しい解決策は、どのフィールドが編集されているかを追跡するフラグを設定し、そのフィールドからの更新のみを許可することだと思います。

ディレクティブを使用して、ng-change編集中のフィールドにフラグを設定するだけです。

ワーキングプランク

コードの変更が必要:

コントローラーを次のように変更します。

function TemperatureConverterCtrl($scope) {
  // Keep track of who was last edited
  $scope.edited = null;
  $scope.markEdited = function(which) {
    $scope.edited = which;
  };

  // Only edit if the correct field is being modified
  $scope.$watch('fahrenheit', function(value) {
    if($scope.edited == 'F') {
      $scope.celsius = (value - 32) * 5.0/9.0;
    }
  });
  $scope.$watch('celsius', function(value) {
    if($scope.edited == 'C') {
      $scope.fahrenheit = value * 9.0 / 5.0 + 32;
    }
  });
}

次に、この単純なディレクティブを入力フィールドに追加します (必要に応じてFまたはを使用)。C

<input ... ng-change="markEdited('F')/>

現在、入力されているフィールドのみが他のフィールドを変更できます。

これらのフィールドを入力の外で変更する機能が必要な場合は、次のようなスコープまたはコントローラー関数を追加できます。

$scope.setFahrenheit = function(val) {
  $scope.edited = 'F';
  $scope.fahrenheit = val;
}

その後、次の $digest サイクルで摂氏フィールドが更新されます。

このソリューションには最小限の余分なコードがあり、サイクルごとに複数の更新が行われる可能性がなくなり、パフォーマンスの問題は発生しません。

于 2013-07-13T03:42:28.900 に答える
7

これはかなり良い質問です。すでに受け入れられていますが、私はそれに答えています:)

Angular では、$scope がモデルです。モデルは、保持したり、アプリの他の部分で使用したりするデータを格納する場所です。そのため、たとえばデータベースの場合と同様に、適切なスキーマで設計する必要があります。

モデルには温度の 2 つの冗長なフィールドがありますが、これは良い考えではありません。「本当の」温度はどれですか?アクセス効率のためだけにモデルを非正規化したい場合がありますが、それは値が冪等である場合にのみ実際にオプションです。これは、浮動小数点の精度が原因ではないことがわかりました。

モデルを引き続き使用する場合は、次のようになります。どちらか一方を「信頼できる情報源」の温度として選択し、もう一方をフォーマッターとパーサーを備えた便利な入力ボックスとして入力します。モデルに華氏が必要だとしましょう:

<input type="number" ng-model="temperatureF">
<input type="number" ng-model="temperatureF" fahrenheit-to-celcius>

そして変換ディレクティブ:

someModule.directive('fahrenheitToCelcius', function() {
  return {
    require: 'ngModel',
    link: function(scope, element, attrs, ngModel) {
      ngModel.$formatters.push(function(f) {
        return (value - 32) * 5.0 / 9.0;
      });
      ngModel.$parsers.push(function(c) {
        return c * 9.0 / 5.0 + 32;
      });
    }
  };
});

これにより、$parsers はモデルの変更ではなく、ユーザーのアクションでのみ実行されるため、「バウンス アラウンド」を回避できます。あなたはまだ長い小数を持っていますが、丸めることで解決できます。

ただし、これにはモデルを使用するべきではないように思えます。「各ボックスが他のボックスを更新する」だけが必要な場合は、まさにそれを行うことができ、モデルさえもありません。これは、入力ボックスが空白で始まることを前提としており、ユーザーが温度を変換するための単純なツールです。その場合は、モデルもコントローラーもなく、Angular もほとんど使用していません。その時点では純粋にビューベースのウィジェットであり、基本的には単なる jQuery または jQLite です。ただし、モデルがないと Angular の他の何かに影響を与えることができないため、有用性は限定的です。

temperatureConverterそのためには、いくつかの入力ボックスを持つテンプレートを持ち、両方のボックスを監視してそれらの値を設定するディレクティブを作成するだけです。何かのようなもの:

fahrenheitBox.bind('change', function() {
  celciusBox.val((Number(fahrenheitBox.val()) - 32) * 5.0 / 9.0);
});
于 2013-07-13T18:16:20.500 に答える