AngularJSで双方向フィルタリングを行う方法は?


124

AngularJSで実行できる興味深いことの1つは、特定のデータバインディング式にフィルターを適用することです。これは、たとえば、カルチャ固有の通貨やモデルのプロパティの日付形式を適用するのに便利な方法です。スコープに計算されたプロパティがあると便利です。問題は、これらの機能がどちらも双方向データバインディングシナリオでは機能しないことです。スコープからビューへの一方向データバインディングのみです。これは、他の点では優れたライブラリでの明らかな省略のようです-または何かが欠けていますか?

KnockoutJS、私は私は、プロパティの値を取得するために呼び出され1、およびプロパティが設定されたときに呼び出されるものを関数のペアを指定することができ、読み取り/書き込み計算プロパティを、作成することができます。これにより、たとえば、カルチャ対応の入力を実装できます。ユーザーが「$ 1.24」と入力して、それをViewModelのフロートに解析し、ViewModelの変更を入力に反映させます。

これに似ていると私が見つけることができる最も近いものは、$scope.$watch(propertyName, functionOrNGExpression);これを使用することです。これにより、プロパティの$scope変更時に関数を呼び出すことができます。しかし、これは、たとえば、文化を意識した入力の問題を解決しません。メソッド自体の$watched中でプロパティを変更しようとすると、問題に注意してください$watch

$scope.$watch("property", function (newValue, oldValue) {
    $scope.outputMessage = "oldValue: " + oldValue + " newValue: " + newValue;
    $scope.property = Globalize.parseFloat(newValue);
});

http://jsfiddle.net/gyZH8/2/

ユーザーが入力を開始すると、入力要素は非常に混乱します。プロパティを2つのプロパティに分割し、1つは解析されていない値用、もう1つは解析された値用に改善しました。

$scope.visibleProperty= 0.0;
$scope.hiddenProperty = 0.0;
$scope.$watch("visibleProperty", function (newValue, oldValue) {
    $scope.outputMessage = "oldValue: " + oldValue + " newValue: " + newValue;
    $scope.hiddenProperty = Globalize.parseFloat(newValue);
});

http://jsfiddle.net/XkPNv/1/

これは最初のバージョンからの改善でしたが、もう少し冗長でありparsedValue、スコープの変更のプロパティの問題がまだあることに注意してください(2番目の入力に何かを入力すると、parsedValue直接変更されます。一番上の入力は変更されないことに注意してください。更新)。これは、コントローラーアクションまたはデータサービスからのデータの読み込みから発生する可能性があります。

AngularJSを使用してこのシナリオを実装する簡単な方法はありますか?ドキュメントにいくつかの機能がありませんか?

回答:


231

これには非常に洗練された解決策があることが判明しましたが、十分に文書化されていません。

表示用のモデル値のフォーマットは、|オペレーターと角度によって処理できますformatter。ngModelはフォーマッターのリストだけでなくパーサーのリストも持っていることがわかります。

1. ng-model双方向データバインディングの作成に使用します

<input type="text" ng-model="foo.bar"></input>

2.同じ要素に適用され、ngModelコントローラーに依存する角度モジュールにディレクティブを作成します。

module.directive('lowercase', function() {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function(scope, element, attr, ngModel) {
            ...
        }
    };
});

3. linkメソッド内で、カスタムコンバーターをngModelコントローラーに追加します。

function fromUser(text) {
    return (text || '').toUpperCase();
}

function toUser(text) {
    return (text || '').toLowerCase();
}
ngModel.$parsers.push(fromUser);
ngModel.$formatters.push(toUser);

4.新しいディレクティブをすでに同じ要素に追加します ngModel

<input type="text" lowercase ng-model="foo.bar"></input>

これは、テキストをで小文字に変換し、モデルで大文字に戻す実際の例ですinput

モデルコントローラーAPIドキュメントにも、簡単な説明とその他の使用可能なメソッドの概要があります。


リンク関数の4番目のパラメーターの名前として「ngModel」を使用した理由はありますか?これは、基本的にはngModel属性とは何の関係もない、ディレクティブの単なる汎用コントローラではありませんか?(私は完全に間違っている可能性があるので、ここで角度を学習します。)
Drew Miller

7
「require: 'ngModel'」のため、リンク関数の4番目のパラメーターはngModelディレクティブのコントローラー、つまりng.Controllerのインスタンスであるfoo.barのコントローラーになります。4番目のパラメータには任意の名前を付けることができます。(私はそれに名前を付けngModelCtrlます。)
Mark Rajcok

8
この手法は、docs.angularjs.org/guide/formsの「カスタム検証」セクションに記載されています。
Nikhil Dabas

1
提供されたフィドルの@Mark Rajcokは、[データの読み込み]をクリックしながら(すべて小文字)、モデル値はすべて大文字であると予想しましたが、モデル値は小さかったです。pls。理由を説明し、モデルを常にIN CAPSで作成する
Rajkamal Subramanian 2013

1
@ rajkamal、loadData2()は$scope直接変更するため、モデルが設定されるのは、ユーザーがテキストボックスを操作するまでです。その時点で、すべてのパーサーがモデル値に影響を与える可能性があります。パーサーに加えて、コントローラーに$ watchを追加してモデル値を変換できます。
Mark Rajcok 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.