116 votes

Difficulté avec ng-model, ng-repeat et les entrées

J'essaie de permettre à l'utilisateur de modifier une liste d'éléments en utilisant la fonction ngRepeat y ngModel . ( Voir ce violon .) Cependant, les deux approches que j'ai essayées conduisent à un comportement bizarre : l'une ne met pas à jour le modèle, et l'autre rend le formulaire flou à chaque pression sur une touche.

Est-ce que je fais quelque chose de mal ici ? Est-ce que ce cas d'utilisation n'est pas pris en charge ?

Voici le code du violon, copié pour plus de commodité :

<html ng-app>
    <head>
        <link href="stackoverflow.com//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/css/bootstrap-combined.min.css" rel="stylesheet">
    </head>
    <body ng-init="names = ['Sam', 'Harry', 'Sally']">
        <h1>Fun with Fields and ngModel</h1>
        <p>names: {{names}}</p>
        <h3>Binding to each element directly:</h3>
        <div ng-repeat="name in names">
            Value: {{name}}
            <input ng-model="name">                         
        </div>
        <p class="muted">The binding does not appear to be working: the value in the model is not changed.</p>
        <h3>Indexing into the array:</h3>
        <div ng-repeat="name in names">
            Value: {{names[$index]}}
            <input ng-model="names[$index]">                         
        </div>
        <p class="muted">Type one character, and the input field loses focus. However, the binding appears to be working correctly.</p>
    </body>
</html>

120voto

jaime Points 15538

Cela semble être une question contraignante.

Le conseil est le suivant ne pas se lier aux primitives .

Votre ngRepeat itère sur des chaînes de caractères dans une collection, alors qu'il devrait itérer sur des objets. Pour résoudre votre problème

<body ng-init="models = [{name:'Sam'},{name:'Harry'},{name:'Sally'}]">
    <h1>Fun with Fields and ngModel</h1>
    <p>names: {{models}}</p>
    <h3>Binding to each element directly:</h3>
    <div ng-repeat="model in models">
        Value: {{model.name}}
        <input ng-model="model.name">                         
    </div>

jsfiddle : http://jsfiddle.net/jaimem/rnw3u/5/

70voto

Thilaga Points 188

En utilisant la dernière version d'Angular (1.2.1) et le suivi par $index . Ce problème est résolu

http://jsfiddle.net/rnw3u/53/

<div ng-repeat="(i, name) in names track by $index">
    Value: {{name}}
    <input ng-model="names[i]">                         
</div>

43voto

Artem Andreev Points 9057

Vous vous retrouvez dans une situation difficile lorsqu'il est nécessaire de comprendre comment scopes , ngRepeat y ngModel avec NgModelController travail. Essayez également d'utiliser la version 1.0.3. Votre exemple fonctionnera un peu différemment.

Vous pouvez simplement utiliser la solution fournie par jm-.

Mais si vous voulez aborder la situation plus en profondeur, vous devez comprendre :

  • comment fonctionne AngularJS ;
  • Les scopes ont une structure hiérarchique ;
  • ngRepeat crée une nouvelle portée pour chaque élément ;
  • ngRepeat construit un cache d'éléments avec des informations supplémentaires (hashKey) ; à chaque appel de veille, pour chaque nouvel élément (qui n'est pas dans le cache), ngRepeat construit une nouvelle portée, un nouvel élément DOM, etc. Description plus détaillée .
  • à partir de la version 1.0.3, ngModelController restitue les entrées avec les valeurs réelles du modèle.

Comment votre exemple "Binding to each element directly" fonctionne pour AngularJS 1.0.3 :

  • vous entrez la lettre 'f' en entrée ;
  • ngModelController change le modèle pour la portée de l'élément (le tableau des noms n'est pas changé) => name == 'Samf' , names == ['Sam', 'Harry', 'Sally'] ;
  • $digest la boucle est lancée ;
  • ngRepeat remplace la valeur du modèle à partir de la portée de l'élément ( 'Samf' ) par la valeur du tableau des noms inchangés ( 'Sam' ) ;
  • ngModelController restitue l'entrée avec la valeur réelle du modèle ( 'Sam' ).

Comment fonctionne votre exemple "Indexation dans le tableau" :

  • vous entrez la lettre 'f' en entrée ;
  • ngModelController change d'élément dans les noms array => `names == ['Samf', 'Harry', 'Sally'] ;
  • La boucle $digest est lancée ;
  • ngRepeat ne peut pas trouver 'Samf' dans le cache ;
  • ngRepeat crée une nouvelle portée, ajoute un nouvel élément div avec une nouvelle entrée (c'est pourquoi le champ de saisie perd le focus - l'ancienne div avec l'ancienne entrée est remplacée par la nouvelle div avec la nouvelle entrée) ;
  • les nouvelles valeurs des nouveaux éléments du DOM sont rendues.

Vous pouvez également essayer d'utiliser AngularJS Batarang et voir comment change $id de la portée de div avec l'entrée dans laquelle vous entrez.

6voto

Bill Heitstuman Points 48

Si vous n'avez pas besoin que le modèle se mette à jour à chaque frappe, il suffit de lier le modèle à l'adresse suivante name et ensuite mettre à jour l'élément du tableau sur l'événement de flou :

<div ng-repeat="name in names">
    Value: {{name}}
    <input ng-model="name" ng-blur="names[$index] = name" />
</div>

4voto

Anton Rodin Points 370

Je viens de mettre à jour AngularJs en 1.1.2 et je n'ai aucun problème avec. Je suppose que ce bug a été corrigé.

http://ci.angularjs.org/job/angular.js-pete/57/artifact/build/angular.js

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X