web-dev-qa-db-fra.com

objet d'option de sélection angular2 ngModel/ngValue - égalité entre différentes instances

Des versions de cette question ont été posées à de nombreuses reprises dans les différentes versions d’Angular2 avant leur publication. Cependant, je n'ai encore rien trouvé qui produirait le comportement souhaité (à part les solutions de contournement que je souhaite éviter) dans le dossier ici: Sélectionner des problèmes d'objet

Ma sélection de formulaire a une liaison bidirectionnelle via [(ngModel)] à un objet, puis "option" est générée via * ngFor pour une liste d'objets similaires (tous différenciés par id). Dans mes recherches, il a été mentionné à plusieurs reprises qu'Angular2 utilise l'équivalence d'objet JavaScript (par instance), de sorte qu'un objet du modèle lié qui ne possède pas la même instance ne correspondra pas à la liste. Ainsi, il n'est pas présenté comme l'élément "sélectionné", ce qui rompt la liaison de données "bidirectionnelle".

Cependant, j'aimerais définir un moyen de faire correspondre ces instances. Certaines solutions qui semblent flotter sur Internet ont été essayées, mais il me manque un petit morceau ou je les ai mal implémentées. 

Options que je veux éviter:

  • Liaison à autre chose que l'objet (c'est-à-dire l'id) dans ngModel. ngValue est une aide précieuse que je souhaite utiliser
  • Solutions de contournement via les gestionnaires de changement
  • Forcer la correspondance des instances (je reçois des objets d'un service de données et je ne veux pas les redéfinir pour qu'ils correspondent ... cela ressemble à un gaspillage inutile de ressources)

Idéalement (et cela semble avoir été discuté comme possible à plusieurs endroits avec des solutions - solutions insuffisantes dans mon cas), il serait possible de définir un standard d'égalité que ngModel utilisera à la place de l'égalité d'instance d'objet. 

c'est-à-dire la dernière tentative ci-dessous, où h.id == a.id définit l'attribut "sélectionné". Ce que je ne comprends pas, c'est pourquoi cet attribut "sélectionné" ne soit pas rendu - est-il bloqué en quelque sorte par ngModel? La définition de selected = 'true' manuellement dans le code HTML semble résoudre, mais générer avec [attr.selected] ou l'une des autres variantes qui construisent un attribut ng-reflect-selected = 'true' ne semble pas faire l'affaire.

<div *ngFor='let a of activePerson.hobbyList ; let i=index; trackBy:a?.id'>

    <label for='personHobbies'>Hobby:</label>
    <select id='personHobbies' class='form-control'
        name='personHobbies' [(ngModel)]='activePerson.hobbyList[i]' #name='ngModel'>

        <option *ngFor='let h of hobbyListSelect; trackBy:h?.id' 
            [ngValue]='h' 
            [attr.selected]='h.id == a.id ? true : null'
        >
        {{h.name}}
        </option>
    </select>
</div>

Certaines choses que j'ai essayées:

  • trackBy
  • Liaison à [sélectionné] =, sélectionné = {{}} et [attr.selected] (Cela semble proche)

J'ai réussi à obtenir un rendu HTML ressemblant à ceci:

<select ...>
    <option selected='true'>Selected</option>
    <option selected='false'>Not Selected</option>
    <!-- and variants, excluding with selected=null-->
</select>

Mais toujours pas de valeur sélectionnée lorsque l'instance de l'objet est différente. J'ai également essayé de trouver quel élément HTML ou CSS enregistre la valeur sélectionnée lorsque l'utilisateur sélectionne une valeur (comment ngModel gère-t-il les autres options possibles?). 

Toute aide serait grandement appréciée. Le but est d’obtenir le bouton "Changer" pour changer le modèle sous-jacent et mettre à jour les cases de sélection en conséquence - j’ai concentré mes tentatives sur la "hobbyList". J'ai essayé sur Firefox et Chrome. Merci!

17
cole

Per @Klinki

Actuellement, il n'y a pas de solution simple dans angular 2, mais dans angular 4 ceci est déjà traité depuis la beta 6 en utilisant compareWith - voir https://github.com/angular/angular/pull/13349

Pour illustrer l’utilisation du cas proposé ( voir plunk ):

<div *ngFor='let a of activePerson.hobbyList ; let i=index;'>

        <label for='personHobbies'>Hobby:</label> 
        <select id='personHobbies' class='form-control'
          name='personHobbies' [(ngModel)]='activePerson.hobbyList[i]'
          [compareWith]='customCompareHobby'>
          <option *ngFor='let h of hobbyListSelect;' [ngValue]='h'>{{h.name}}</option>
        </select>

</div>
...
customCompareHobby(o1: Hobby, o2: Hobby) {
    return o1.id == o2.id;
}
10
cole

Actuellement, il n'y a pas de solution simple dans angular 2, mais dans angular 4, ceci est déjà traité depuis la beta 6 - voir https://github.com/angular/angular/pull/13349

4
Klinki

Lorsque vous faites passer un objet quelque part que vous voulez créer, sélectionnez simplement le tableau hobbyListSelect pour l'élément correspondant et affectez l'élément trouvé à activePerson.hobbyList[i]

0

Il n’existe pas d’approche simple pour utiliser la liaison ngModel de cette façon . Cependant, comme vous ne souhaitez pas enregistrer les gestionnaires d’événements séparément, il peut être nécessaire de modifier la façon dont l’objet est affecté. attribue l'identifiant pour la balise select car elle ne sert pas l'objectif . Pour le cas d'utilisation mentionné, je l'ai modifiée pour afficher la liaison bidirectionnelle.

Recherchez le constructeur du composant avec model et printVal () est inclus uniquement pour tester le changement. Le modèle n'inclut pas ngValue et ngModel, mais il peut néanmoins fonctionner comme prévu.

constructor() {
    this.activePerson = {id: 'a1',
        hobbyList : [
           {
             id: 'h4'
           },
           {
             id : 'h2'
           }
          ]
      }
      
    this.hobbyListSelect = [
        {
         id: 'h1'
        },
        {
         id : 'h2'
        },
        {
         id: 'h3'
        },
        {
         id : 'h4'
        }
      ];
  }

printVal($event,i) {
    console.log(JSON.stringify(this.activePerson.hobbyList))
}
<div *ngFor='let a of activePerson.hobbyList ; let i=index; trackBy:a?.id'>
        <label for='personHobbies'>Hobby:</label>
        <select name='personHobbies' 
        (change)=" activePerson.hobbyList[i] = hobbyListSelect[$event.target.selectedIndex];
        printVal($event,i)">
            <option *ngFor='let h of hobbyListSelect; trackBy:h?.id' 
                [selected]='(h.id == a.id) ? "selected" : null'
            >
            {{h.id}}
            </option>
        </select>
    </div>

0
Ashwin Kumar