Schwierigkeiten mit ng-Modell, ng-Wiederholung und Eingaben

116

Ich versuche dem Benutzer zu erlauben, eine Liste von Elementen mit ngRepeatund zu bearbeiten ngModel. ( Siehe diese Geige .) Beide Ansätze, die ich versucht habe, führen jedoch zu bizarrem Verhalten: Einer aktualisiert das Modell nicht und der andere verwischt das Formular bei jedem Tastendruck.

Mache ich hier etwas falsch Ist dies kein unterstützter Anwendungsfall?

Hier ist der Code aus der Geige, der der Einfachheit halber kopiert wurde:

<html ng-app>
    <head>
        <link href="//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>

Wenn Sie

Nick Heiner
quelle
1
Dies ist sehr ähnlich zu stackoverflow.com/questions/12977894/… , aber der zweite Ansatz ist hier neu, sodass es sich nicht genau um ein Duplikat handelt. Ich gab dort eine detaillierte Antwort (ähnlich wie bei Artem) und erklärte, wie ng-repeat-Kinderbereiche funktionieren.
Mark Rajcok
Da es mich eine angemessene Menge an Googeln gekostet hat, bevor ich diesen Thread gefunden habe, kann ich ihn in etw umbenennen wie: "Übergeordnetes Modell nicht von ngRepeat-Eingaben aktualisiert" oder "Modell nicht aktualisiert, wenn ngRepeat verwendet wird" oder "ngRepeat-Eingaben nicht an (übergeordnetes Element gebunden") Modell"? Vielleicht hast du bessere Ideen für den Titel?
Vucalur

Antworten:

120

Dies scheint ein verbindliches Problem zu sein.

Der Rat ist , nicht an Primitive zu binden .

Sie ngRepeatiterieren über Zeichenfolgen innerhalb einer Sammlung, wenn sie über Objekte iterieren sollen. Um Ihr Problem zu beheben

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

jaime
quelle
3
Vielen Dank für den ersten Hyperlink, der das Problem Unschärfe / Fokusverlust / Flimmern erklärt. Ich habe mich immer gefragt, warum das passiert ist.
Mark Rajcok
2
Danke dafür, hat mir sehr geholfen. Dennoch sollte die Bindung an Primitive vorhanden sein ...
Daniel Gruszczyk
1
Alter Beitrag, aber danke, ich habe einige Zeit gebraucht, um das Problem zu finden, dass das Modell in ngRepeat nicht geändert wird, und es war Ihr Rat, sich nicht an
Grundelemente
Außerdem: Ändern Sie nicht die gesamte Liste während der Eingabe - eine Falle, in die ich geraten bin. Ich habe die Sammlung auf Änderungen überprüft und durch eine identische Kopie ersetzt. Obwohl ich nicht an Grundelemente gebunden war, wurden die Elemente neu erstellt.
z0r
71

Verwenden Sie die neueste Version von Angular (1.2.1) und verfolgen Sie nach $index. Dieses Problem wurde behoben

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

<div ng-repeat="(i, name) in names track by $index">
    Value: {{name}}
    <input ng-model="names[i]">                         
</div>
Thilaga
quelle
hat den Job gemacht; kein Referenzverlust mehr
Dan Ochiana
Ich habe so lange danach gesucht ... so einfach ... einfach so versteckt. Geben Sie dies als Antwort!
Andrea
oooooh, also behebt das Hinzufügen von "track by $ index" in ng-repeat auch das "flackernde" Problem
boi_echos
43

Sie geraten in eine schwierige Situation, wenn Sie verstehen müssen, wie Bereiche , ngRepeat und ngModel mit NgModelController funktionieren. Versuchen Sie auch, die Version 1.0.3 zu verwenden. Ihr Beispiel wird etwas anders funktionieren.

Sie können einfach die von jm- bereitgestellte Lösung verwenden.

Aber wenn Sie sich eingehender mit der Situation befassen möchten, müssen Sie verstehen:

  • wie AngularJS funktioniert ;
  • Bereiche haben eine hierarchische Struktur;
  • ngRepeat erstellt für jedes Element einen neuen Bereich.
  • ngRepeat Build-Cache von Elementen mit zusätzlichen Informationen (hashKey); Bei jedem Überwachungsaufruf für jedes neue Element (das sich nicht im Cache befindet) erstellt ngRepeat einen neuen Bereich, ein neues DOM-Element usw. Detailliertere Beschreibung .
  • ab 1.0.3 ngModelController rendert Eingaben mit tatsächlichen Modellwerten neu.

So funktioniert Ihr Beispiel "Direkt an jedes Element binden" für AngularJS 1.0.3:

  • Sie geben einen Buchstaben 'f'in die Eingabe ein.
  • ngModelControllerÄndert das Modell für den Elementbereich (Namensarray wird nicht geändert) => name == 'Samf', names == ['Sam', 'Harry', 'Sally'];
  • $digest Schleife wird gestartet;
  • ngRepeatErsetzt den Modellwert aus item scope ( 'Samf') durch den Wert aus array ( 'Sam') mit unveränderten Namen .
  • ngModelControllerEingabe mit dem tatsächlichen Modellwert erneut rendern ( 'Sam').

So funktioniert Ihr Beispiel "Indizieren in das Array":

  • Sie geben einen Buchstaben 'f'in die Eingabe ein.
  • ngModelControllerändert Element in Namen array=> `names == ['Samf', 'Harry', 'Sally'];
  • $ Digest-Schleife wird gestartet;
  • ngRepeatkann nicht 'Samf'im Cache finden;
  • ngRepeat erstellt einen neuen Bereich, fügt ein neues div-Element mit neuer Eingabe hinzu (aus diesem Grund verliert das Eingabefeld den Fokus - altes div mit alter Eingabe wird durch neues div mit neuer Eingabe ersetzt);
  • Neue Werte für neue DOM-Elemente werden gerendert.

Sie können auch versuchen, AngularJS Batarang zu verwenden und sehen, wie sich $ id des Bereichs von div mit der Eingabe ändert, in die Sie eingeben.

Artem Andreev
quelle
danke für die 1.0.3 erklärung. Es ist interessant, dass in 1.0.3, dem Fall "Direkt binden", das Eingabefeld in dem Sinne unterbrochen zu sein scheint, dass es keine Änderungen / eingegebenen Eingaben zu akzeptieren scheint (zumindest in Chrome). Ich bin mir sicher, dass wir in naher Zukunft einige SO-Posts darüber sehen werden :) Ich nehme an, dieser neue Weg ist besser, da es offensichtlicher sein wird, dass etwas nicht stimmt.
Mark Rajcok
6

Wenn Sie das Modell nicht bei jedem Tastendruck aktualisieren müssen, binden Sie es einfach an namedas Array-Element und aktualisieren Sie es bei einem Unschärfeereignis:

<div ng-repeat="name in names">
    Value: {{name}}
    <input ng-model="name" ng-blur="names[$index] = name" />
</div>
Bill Heitstuman
quelle
Warum nicht verwenden ng-model="names[$index]"... Ich weiß, es ist eine Problemumgehung, aber es funktioniert ;-)
Draško Kokić
2

Das Problem scheint in der Art und Weise zu liegen, wie ng-modelmit inputdem nameObjekt gearbeitet und es überschrieben wird, wodurch es verloren geht ng-repeat.

Als Problemumgehung kann der folgende Code verwendet werden:

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

Ich hoffe es hilft

Draško Kokić
quelle
1

Ich habe die obige Lösung für mein Problem ausprobiert, es hat wie ein Zauber funktioniert. Vielen Dank!

http://jsfiddle.net/leighboone/wn9Ym/7/

Hier ist meine Version davon:

var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
    $scope.models = [{
        name: 'Device1',
        checked: true
    }, {
        name: 'Device1',
        checked: true
    }, {
        name: 'Device1',
        checked: true
    }];

}

und mein HTML

<div ng-app="myApp">
    <div ng-controller="MyCtrl">
         <h1>Fun with Fields and ngModel</h1>
        <p>names: {{models}}</p>
        <table class="table table-striped">
            <thead>
                <tr>
                    <th></th>
                    <th>Feature 1</td>
                    <th>Feature 2</th>
                    <th>Feature 3</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>Device</td>
                   <td ng-repeat="modelCheck in models" class=""> <span>
                                    {{modelCheck.checked}}
                                </span>

                    </td>
                </tr>
                <tr>
                    <td>
                        <label class="control-label">Which devices?</label>
                    </td>
                    <td ng-repeat="model in models">{{model.name}}
                        <input type="checkbox" class="checkbox inline" ng-model="model.checked" />
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
</div>
Leigh Lawhon
quelle
-5

wie macht man so etwas wie:

<select ng-model="myModel($index+1)">

Und in meinem Inspektorelement sei:

<select ng-model="myModel1">
...
<select ng-model="myModel2">
Nicolás Oviedo
quelle