Angular.js: Onchange wird vor der Modellaktualisierung für den isolierten Bereich ausgelöst

Erstellt am 21. Okt. 2013  ·  11Kommentare  ·  Quelle: angular/angular.js

Ein Änderungsereignis für die Elemente mit isoliertem Gültigkeitsbereich wird vor der Modellaktualisierung ausgelöst.

Beispiel:

<!doctype html>
<html ng-app="App">
    <head>

        <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />

        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.js"></script>
        <script src="http://code.angularjs.org/1.0.6/angular.js"></script>
        <script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>

        <script type="text/javascript">
            "use strict"

            var App = angular.module('App', []);
            App.controller('Ctrl', function ($scope,$timeout) {

                $scope.change1 = function () {
                    console.warn("inside change", $scope.y);
                }

                $scope.change2 = function () {
                    console.warn("inside change", $scope.x);    
                }

            });

            App.directive("wminput", function () {
                return {
                    restrict: "E",
                    replace: true,
                    scope: {
                        "onChange":"&",
                        "value": "="
                    },
                    template: '<input type="text" data-ng-model="value" data-ng-change="onChange();">'
                }
            });

        </script>

    </head>
    <body>
        <div data-ng-controller="Ctrl">
            "Onchange will be triggered after the model update"
            <input data-ng-change="change1()" data-ng-model="y">
            <br><br>
            "Onchange will be triggered before the model update"
            <wminput on-change="change2()" value="x"></wminput>
        </div>

    </body>
</html>
$compile forms low works as expected confusing bug

Hilfreichster Kommentar

Narretz, ich bin absolut anderer Meinung. Mit dem Entwickler wird ein Vertrag geschlossen, in dem der Code verwendet wird, wonach die bidirektionale Bindung Vorrang hat. Wenn eine Änderung an einer beliebigen Stelle entlang der Bindungskette vorgenommen wird, sollte die gesamte Kette aktualisiert werden, bevor Sie fortfahren. Selbst wenn eine andere Direktive von dieser Direktive erbt und etwas weiter unten an eine Bindung einer Bindung gebunden ist ... Wenn dieses Modell aktualisiert wird, sollte das ursprüngliche Controller-Scope-Modell am unteren Rand des Stapels im selben Digest-Zyklus zuvor aktualisiert werden Weiter zu anderen Funktionen. Es ist das, was vom Verbraucher erwartet wird.

Alle 11 Kommentare

Interessanter Eckfall ... http://plnkr.co/edit/lsrga8qMcbgNWATrVht3?p=preview

Dies geschieht nur, wenn die Funktion onChange an die übergeordnete Bereichsbreite der Bindung '&' delegiert wird.

Wir haben an einem RAD-Tool gearbeitet, das auf Angularjs basiert.
Dies war ein schwerwiegender Fehler, da jedes Widget in der Anwendung isolateScope hat.
Wir haben das Problem behoben, indem wir eine Proxy-Methode für onChange erstellt haben.

Schauen Sie sich Folgendes an: https://studio.wavemaker.com/

Grüße, Vinay

Könnten Sie ein Beispiel für diesen Proxy veröffentlichen und wie er verwendet wird? Könnte beim Debuggen helfen.

Wird es bald behoben sein? Es ist sooooo verwirrend.

Nach weiteren Überlegungen muss ich sagen, dass dies das erwartete Verhalten ist. Im plnkr-Beispiel ist der Wert des ngModel eine zwei gebundene Zeichenfolge zwischen dem Controller und dem Direktivenbereich. Wenn Sie nun den Wert in der Direktive ändern, aktualisiert ngModel zuerst den Bereich _isolate_ und löst ngChange im selben Digest aus. Da die bidirektionale Bindung noch keine Änderung am Direktivenbereich vorgenommen hat, erhält ngChange im übergeordneten Bereich den "alten" Wert von x . Jetzt benötigt der Zwei-Wege-Bindungsbeobachter mindestens zwei Digest-Zyklen, um den Wert zu stabilisieren, sodass der Controller-Bereich erst aktualisiert wird, nachdem ngChange aufgerufen wurde.
Ich stimme zu, dass dies verwirrend ist, aber so funktioniert es. Sie können dies umgehen, indem Sie ein Objekt an die Direktive binden, die den Wert enthält, den Sie an ngModel binden. Auf diese Weise wird der Wert sowohl im Isolat- als auch im Controller-Bereich gleichzeitig als Referenz festgelegt und ist verfügbar, wenn ngChange ausgeführt wird: http://plnkr.co/edit/fs7S6yX1a5aeo1Ese522?p=preview

Narretz, ich bin absolut anderer Meinung. Mit dem Entwickler wird ein Vertrag geschlossen, in dem der Code verwendet wird, wonach die bidirektionale Bindung Vorrang hat. Wenn eine Änderung an einer beliebigen Stelle entlang der Bindungskette vorgenommen wird, sollte die gesamte Kette aktualisiert werden, bevor Sie fortfahren. Selbst wenn eine andere Direktive von dieser Direktive erbt und etwas weiter unten an eine Bindung einer Bindung gebunden ist ... Wenn dieses Modell aktualisiert wird, sollte das ursprüngliche Controller-Scope-Modell am unteren Rand des Stapels im selben Digest-Zyklus zuvor aktualisiert werden Weiter zu anderen Funktionen. Es ist das, was vom Verbraucher erwartet wird.

Leicht verwandt und ebenfalls verwirrend. Wenn Sie mit ngChange eine Validierung für ein anderes Feld auslösen, das ein ngModel teilt, wird die Änderung ausgelöst, bevor $modelValue aktualisiert wird. Die Validierung wird kompliziert.

Verwenden von $timeout als Problemumgehung. Immer noch verwirrt, warum das nötig ist.

Beispiel:
http://codepen.io/jakobadam/pen/qmBdrJ?editors=1010

Grüße, Jakob

Gibt es Pläne, dieses Problem erneut zu behandeln? Vielleicht wieder öffnen?

Ich bin auf dieses Problem gestoßen und habe die Kommentare durchgelesen, während die Art und Weise, wie es derzeit aufgrund des isolierten Bereichs funktioniert, Sinn macht. Das Ergebnis, das sich aus der Erwartung eines aktualisierten Modells ergibt, wenn der On-Change-Aufruf ausgelöst wird, und dem Erhalt einer vorherigen Iteration des Modells ist definitiv nicht das Verhalten, das Sie als Entwickler erwarten.

Ich halte mich an die Frage, wird dies noch einmal überprüft?

Das glaube ich nicht. Das aktuelle Verhalten wird erwartet und obwohl es in einigen Fällen nicht wünschenswert ist, gibt es mindestens ebenso viele Fälle, in denen dies der Fall ist. Eine Änderung jetzt wäre (a) eine bahnbrechende Änderung und (b) würde viele Anwendungsfälle nicht unterstützen (z. B. wenn ngChange den Wert ändern soll, bevor das übergeordnete Element aktualisiert wird). Es ist einer dieser Fälle, in denen nicht alle glücklich sein können, also müssen wir uns an die Version halten, die:

  • Macht Sinn.
  • Bricht vorhandene Benutzer nicht ab.
  • Unterstützt spezifischere / fortgeschrittenere Anwendungsfälle, auch wenn dies nur wenig zusätzlichen Code kostet (siehe unten).

Die aktuelle Implementierung von ngChange ist eng an das Konzept von ngModel gebunden. Richtlinienbindungen sind ein völlig eigenständiges Konzept. Die beiden können gut zusammenarbeiten, sollten sich aber nicht auf die Umsetzung / das Innenleben der anderen auswirken.

#

Wie bereits erwähnt, gibt es einige Optionen, wenn Ihr spezifischer Rückruf Zugriff auf den aktualisierten Wert benötigt:

  1. Übergeben Sie den aktualisierten Wert als Argument an den Rückruf.

  2. Wenn es asynchron ausgeführt werden muss (dh nachdem der Digest beendet ist und alle Bindungen weitergegeben wurden), können Sie im Rückruf immer $timeout .

  3. Wenn Sie eine "asynchrone" Version von ngChange möchten, ist die Implementierung als benutzerdefinierte Direktive ebenfalls trivial. (Sie könnten auch die eingebaute Direktive überschreiben, aber das würde ich definitiv nicht empfehlen.) ZB:

.directive('myAsyncChange', function($timeout) {
  return {
    restrict: 'A',
    require: 'ngModel',
    link: function(scope, element, attr, ctrl) {
      ctrl.$viewChangeListeners.push(function() {
        $timeout(function() {
          scope.$eval(attr.myAsyncChange);
        });
      });
    }
  };
});
<input ng-model="foo" my-async-change="onChange()" />
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen