web-dev-qa-db-fra.com

Définition de l'élément sélectionné dans la directive angularJS select

J'ai un problème avec le réglage de l'élément sélectionné dans la directive select de angular. Je ne sais pas s'il s'agit d'un bug ou d'une conception consciente de la part des concepteurs de angular. Cela rend cependant la directive select beaucoup moins utile.

La description:

Mon application communique avec une API REST pour recevoir une entité de la base de données. L'API indique que les relations de l'objet sont envoyées avec une propriété ID uniquement afin que vous puissiez les récupérer dans les demandes suivantes, si nécessaire. 

Exemple:

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

où city est une autre entité pouvant être extraite via un autre noeud final REST à l'aide de l'identifiant de la ville et se présentant ainsi:

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

Je dois créer un formulaire pour modifier l'entité client avec un menu déroulant avec toutes les villes possibles afin que l'utilisateur puisse sélectionner la ville appropriée dans la liste. La liste doit initialement afficher la ville du client telle qu'elle a été extraite de l'objet JSON. 

Le formulaire ressemble à ceci: 

 <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> 

Et le contrôleur ressemble à ceci:

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];
            }
        }
    }
});

Que devrait-il se passer: Une fois que tous les détails de l'objet City ont été chargés, la directive de sélection doit définir la ville chargée comme élément sélectionné dans la liste. 

Que se passe-t-il: La liste affiche un élément vide et il n’ya aucun moyen d’initialiser l’élément sélectionné si cet élément est sélectionné à partir d’objets en dehors du tableau d’éléments. 

La DEMO du problème ici: http://plnkr.co/edit/NavukDb34mjjnnOPPHEHE?p=preview

Y a-t-il des solutions pour cela? L'élément sélectionné peut-il être défini par programme de manière générique afin que les appels AJAX et la logique de sélection soient remaniés dans un widget de sélection basé sur AJAX réutilisable?

19
javito

C'est aussi simple que ça

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

Puis à l'intérieur de votre contrôleur:

// 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]

Consultez le http://docs.angularjs.org/api/ng.directive:select À partir de là:

trackexpr: utilisé lors de l'utilisation d'un tableau d'objets. Le résultat de cette expression sera utilisé pour identifier les objets du tableau. Trackexpr fera très probablement référence à la variable de valeur (par exemple, valeur.nom_propriété).

Le reste consiste simplement à attribuer une valeur à la variable $scope.item et angular déterminera quel élément doit être défini comme actif en vérifiant la propriété name de l'élément.

41
simo

Cela ne fonctionne pas parce qu'angular s'attend à ce que les références des objets soient égales. Dans votre cas (le 'select from object' dans votre plnkr) crée un nouvel objet, mais avec les mêmes propriétés. Cependant, Angular ne peut pas savoir que deux objets différents représentent le même objet. Vous avez au moins deux approches:

Trouver la bonne instance d'objet de ville

Au lieu de définir $scope.customer.city sur un nouvel objet, recherchez l'objet City actuel dans le tableau $scope.cities. Si vous utilisez UnderscoreJs vous pouvez faire quelque chose comme:

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

Lier l'id de la ville à la place de l'objet de la ville

Une autre approche, qui pourrait être plus simple, consiste à modifier les directives ng-model et ng-options afin qu'elles correspondent à id et non à object. Voir exemple de travail ici .

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

Je suis tombé sur le même problème. Mes options et les données modélisées provenaient d'appels d'API distincts.

Au lieu d’ajouter une couche d’indirection en utilisant ng-model sur les clés d’objet, j’ai fini par écrire une directive simple qui utilise une variable "proxy".

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

devient

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

En utilisant $ watch sur customer.city et customer_cityProxy, j'obtiens le comportement attendu.

Il y a encore quelques inconvénients, à savoir que cela ne fonctionne que si les clés sont disjointes.

Le code est disponible ici: https://github.com/gosusnp/options-proxy

2
user1498724

Jetez un oeil à http://configit.github.io/ngyn/#select_extensions cette solution a fonctionné pour moi

0
danielgatis