15

<select>この質問は、 ng-options を使用したタグでの選択に関する問題と非常によく似ていると言うことから始めましょう。たとえば、AngularJS の ng-options を使用して select を操作します。特定の問題は、オブジェクトの 2 つの異なるインスタンスを比較することです。これらは参照が等しくありませんが、論理的には同じデータを表しています。

実証するために、モデル内に次のオプションの配列と選択されたオプション変数があるとします。

$scope.items = [
   {ID: 1, Label: 'Foo', Extra: 17},
   {ID: 2, Label: 'Bar', Extra: 18},
   {ID: 3, Label: 'Baz', Extra: 19}
];
$scope.selectedItem = {ID: 1, Label: 'Foo'};

上記のオブジェクトはデモ用であることに注意してください。selectedItemモデルオブジェクトの特定のプロパティが異なる場合があることを示すために、特に「追加」プロパティをオフにしました。重要なことは、ID プロパティで比較したいということです。equals()プロトタイプ「クラス」とIDの両方を比較する実際のオブジェクトの関数があります。

そして、ビューで:

<label class="radio inline" ng-repeat="item in items">
    <input type="radio" ng-model="selectedItem" ng-value="item"> {{item.Label}}
</label>

ここでの問題は、angular がオブジェクトの参照等価性を使用しているため、'Foo' のラジオ ボタンが選択されないことです。スコープの最後の行を以下に変更すると、すべてが期待どおりに機能します。

$scope.selectedItem = items[0];

しかし、私が抱えている問題は、私のアプリケーションでは、これら 2 つの単純な変数をスコープ内で単純に宣言していないことです。むしろ、オプション リストと、選択されたオプションがバインドされているデータ構造は、両方とも $http を使用してサーバーからクエリされる JSON データの大きなセットの一部です。一般的なケースでは、データ バインドされた選択されたプロパティを、データ クエリから同等のオプションに変更することは非常に困難です。

それで、私の質問: の ng-options では<select>、angular は、track by「object.ID」のようなものを言うことができる式を提供し、選択したモデル値を ID プロパティを介してオプションと比較する必要があることを angular に通知します。すべてが同じモデル プロパティにバインドされている一連の無線入力に使用できる同様のものはありますか? 理想的には、オブジェクト タイプと ID の両方をチェックする、これらのモデル オブジェクトに配置した独自のカスタム equals() メソッドを使用するように angular に指示できます。それができない場合は、ID 比較を指定できるようにすることもできます。

4

5 に答える 5

10

私は最も単純なディレクティブを書きます。一種の「トラックバイ」を使用して、2 つの異なるオブジェクトをマッピングします。http://jsfiddle.net/xWWwT/146/を参照してください。

HTML

<div ng-app="app">
<div ng-app ng-controller="ThingControl">    
    <ul >
        <li ng-repeat="color in colors">
            <input type="radio" name="color" ng-model="$parent.thing" ng-value="color" radio-track-by="name" />{{ color.name }}
        </li>
    </ul>
    Preview: {{ thing }}
</div>
</div>

JS

var app = angular.module('app', []);

app.controller('ThingControl', function($scope){
    $scope.colors = [
        { name: "White", hex: "#ffffff"},
        { name: "Black", hex: "#000000"},
        { name: "Red", hex: "#000000"},
        { name: "Green", hex: "#000000"}
    ];

    $scope.thing = { name: "White", hex: "#ffffff"};

});

app.directive('radioTrackBy', function(){
return {
        restrict: "A",
        scope: {
            ngModel: "=",
            ngValue: "=",
            radioTrackBy: "@"
        },
        link: function (ng) {   
            if (ng.ngValue[ng.radioTrackBy] === ng.ngModel[ng.radioTrackBy]) {                                
                ng.ngModel = ng.ngValue;
            }
        }
    };
});
于 2015-04-06T22:57:08.150 に答える
2

OK、さらに検討した結果、本質的に ng-model ディレクティブを独自のカスタム ディレクティブに置き換えるだけで、より多くの「ミックスイン」アプローチを採用することにしました。これは、 https ://stackoverflow.com/a/14519881/561604 の回答に基づいて「チェックボックス リスト」ディレクティブを作成するために使用したアプローチと非常によく似ています。

.directive('radioOptions', function() {
    // Apply this directive as an attribute to multiple radio inputs. The value of the attribute
    // should be the scope variable/expression which contains the available options for the
    // radio list. Typically, this will be the collection variable in an ng-repeat directive
    // that templates the individual radio inputs which have radio-options applied. In addition,
    // instead of the normal ng-model, use a selected-option attribute set to the same expression.
    // For example, you might use radio-options like this:
    // <label ... ng-repeat="item in collection">
    //     <input type="radio" ... ng-value="item" radio-options="collection" selected-option="myModel.myProperty">
    // </label>
    //
    // See https://stackoverflow.com/questions/19281404/object-equality-comparison-for-inputradio-with-ng-model-and-ng-value
    // for the SO question that inspired this directive.
    return {
        scope: {
            radioOptions: '=',
            selectedOption: '=',
            ngValue: '='
        },
        link: function( scope, elem, attrs ) {
            var modelChanged =  function() {
                if( jQuery.isArray(scope.radioOptions) ) {
                    jQuery.each( scope.radioOptions, function(idx, item) {
                        // This uses our models' custom 'equals' function for comparison, but another application could use
                        // ID propeties, etc.
                        if( typeof item.equals === 'function' && item.equals(scope.selectedOption) ) {
                            elem.prop( 'checked', item === scope.ngValue );
                        }
                    });
                }
            };
            scope.$watch( 'radioOptions', modelChanged );
            scope.$watch( 'selectedOption', modelChanged );
            var viewChanged = function() {
                var checked = elem.prop( 'checked' );
                if( checked ) {
                    scope.selectedOption = scope.ngValue;
                }
            };
            elem.bind( 'change', function() {
                scope.$apply( viewChanged );
            });
        }
    };
});
于 2013-10-10T14:29:53.273 に答える
1

OPが要求したように、複雑なオブジェクトで機能するラジオボタンディレクティブの例を次に示します。underscore.jsを使用して、オプションから選択された項目を見つけます。AJAX 呼び出しでオプションと選択した値の読み込みもサポートしているため、本来よりも少し複雑です。

于 2013-10-10T13:03:13.203 に答える
0

まだコメントを追加することができないため、ここで返信する必要があります。ダナの答えは私にとってはうまくいきました。彼のアプローチを使用するには指摘したいのですが、コレクション内のオブジェクトに「equals」関数を実装する必要があります。以下の例を参照してください。

.controller('ExampleController', ['$scope', function($scope) {
   var eq = function(obj) {
       return this.id === obj.id;
     };
   col = [{id: 1, name: 'pizza', equals: eq}, {id:2, name:'unicorns', equals: eq}, {id:3, name:'robots', equals: eq}];

   $scope.collection = col;
   $scope.my = { favorite : {id:2, name:'unicorns'} };

 }]);

プランカーのリンクを参照してください。

于 2014-12-05T13:24:34.123 に答える