web-dev-qa-db-ja.com

angularJS selectディレクティブで選択したアイテムを設定する

Angularのselectディレクティブで選択したアイテムを設定する際に問題があります。これがバグなのか、Angularのデザイナーの意識的なデザインなのかはわかりません。ただし、selectディレクティブの有用性はかなり低くなります。

説明:

私のアプリはREST APIと通信し、データベースからエンティティを受信します。APIは、必要に応じて後続のリクエストで取得できるように、オブジェクトの関係はIDプロパティのみで送信されることを指示します。

例:

{ id : 1, customerName : "some name", city : {id : 12}} 

ここで、cityは、シティIDを使用して別のRESTエンドポイントを通じて取得できる別のエンティティであり、次のようになります。

{ id: 12, name : "New York"}

ユーザーがリストから適切な都市を選択できるように、すべての可能な都市を含むドロップダウンメニューで顧客エンティティを編集するフォームを作成する必要があります。リストには、最初にJSONオブジェクトから取得した顧客の都市を表示する必要があります。

フォームは次のようになります。

 <form>
  <input type="text" ng-model="customer.name"/>
  <select ng-model="customer.city" ng-options="i as i.name for i in cities"></select>
 </form> 

そして、コントローラーは次のようになります。

app.controller('MainCtrl', function ($scope, $http) {
    $http.get(serviceurl + 'admin/rest/customer/' + id + "/", {
        params: {"accept": "json"},
        withCredentials: true
    }).then(function (response) {
                $scope.customer = response.data.item;
            });
    $http.get(serviceurl + 'admin/rest/city/', {
        params: {"accept": "json"},
        withCredentials: true
    }).then(function (response) {
                $scope.cities = response.data.items;
                // THIS LINE LOADS THE ACTUAL DATA FROM JSON
                $scope.customer.city = $scope.findCity($scope.customer.city);
            });
    $scope.findCity = function (city) {
        for (var i = 0; i < $scope.cities.length; i++) {
            if ($scope.cities[i].id == city.id) {
                return $scope.cities[i];
            }
        }
    }
});

起こるべきこと:Cityオブジェクトの完全な詳細がロードされると、selectディレクティブは、リスト内の選択されたアイテムとしてロードされた都市を設定する必要があります。

何が起こるか:リストには空のアイテムが表示され、選択したアイテムがアイテムの配列外のオブジェクトから選択されている場合、選択したアイテムを初期化する方法はありません。

ここの問題のデモ: http://plnkr.co/edit/NavukDb34mjjnQOP4HE9?p=preview

これに対する解決策はありますか? AJAX呼び出しと選択ロジックが再利用可能なAJAXベースの選択ウィジェットにリファクタリングされるように、選択された項目を一般的な方法でプログラムで設定できますか?

20
javito

これと同じくらい簡単です

<select
    ng-model="item"
    ng-options="item.name for item in items track by item.name">

次に、コントローラー内で:

// all items
$scope.items = [{name: 'a'}, {name: 'b'}, {name: 'c'}];
// set the active one
$scope.item = {name: 'b'};
// or just
$scope.item = $scope.items[1]

http://docs.angularjs.org/api/ng.directive:select をご覧ください。

trackexpr:オブジェクトの配列を操作するときに使用されます。この式の結果は、配列内のオブジェクトを識別するために使用されます。 trackexprは、ほとんどの場合、値変数(value.propertyNameなど)を参照します。

残りは$scope.item変数に値を代入するだけで、angular=は、アイテムのnameプロパティをチェックすることにより、どの要素をアクティブとして設定する必要があるかを判断します。

42
simo

それが機能しない理由は、angularはオブジェクト参照が等しいことを期待していることです。あなたの場合(plnkrの 'select from object')は同じでただし、Angularは、2つの異なるオブジェクトが同じオブジェクトを表していることを知ることができません。少なくとも2つのアプローチがあります。

正しい都市オブジェクトインスタンスを見つける

$scope.customer.cityを新しいオブジェクトに設定する代わりに、$scope.cities配列から実際の都市オブジェクトを見つけます。 nderscoreJs を使用している場合、次のようなことができます。

$scope.customer.city = _.find($scope.cities, function (city) {
    return city.id === theCustomersCity.id;
});

都市オブジェクトの代わりに都市IDにバインドします

より簡単な別の方法は、ng-modelおよびng-optionsディレクティブを変更して、オブジェクトではなくidに一致させることです。作業例を参照してください here

<select ng-model="customer.cityId" ng-options="i.id as i.name for i in cities"></select>
21
Martin

私は同じ問題に遭遇しました。私のオプションとモデル化されたデータは両方とも別々のAPI呼び出しから来ました。

オブジェクトキーでng-modelを使用してインダイレクションのレイヤーを追加する代わりに、「プロキシ」変数を使用する単純なディレクティブを記述しました。

<select ng-model="customer.city" ng-options="i as i.name for i in cities"></select>

になる

<select ng-model="customer_cityProxy" ng-options="i.name as i.name for i in cities"></select>

Customer.cityとcustomer_cityProxyで$ watchを使用すると、期待どおりの動作が得られます。

キーが互いに素である場合にのみ機能するという点で、まだいくつかの欠点があります。

コードはこちらから入手できます: https://github.com/gosusnp/options-proxy

2
user1498724
0
danielgatis