Angular.js: Onchange se activa antes de la actualización del modelo para aislar el alcance

Creado en 21 oct. 2013  ·  11Comentarios  ·  Fuente: angular/angular.js

El evento de cambio para los elementos que tienen un alcance aislado se activa antes de la actualización del modelo.

Ejemplo:

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

Comentario más útil

Narretz, estoy totalmente en desacuerdo. Existe un contrato con el desarrollador que consume el código en el que el enlace bidireccional tiene prioridad. Cuando se realiza un cambio en cualquier parte de la cadena de enlace, se debe actualizar toda la cadena antes de continuar. Entonces, incluso si otra directiva hereda de esta directiva, y algo más abajo se une a un enlace de un enlace ... si ese modelo se actualiza, el modelo de alcance del controlador original en la parte inferior de la pila debe actualizarse en el mismo ciclo de resumen antes continuando con otras funciones. Es lo que se espera del consumidor.

Todos 11 comentarios

Caso de esquina interesante ... http://plnkr.co/edit/lsrga8qMcbgNWATrVht3?p=preview

Esto sucede solo cuando la función onChange se delega al ancho del alcance principal del enlace '&'.

Hemos estado trabajando en una herramienta RAD basada en angularjs.
Este fue un error importante ya que todos los widgets de la aplicación tienen isolateScope.
Hemos resuelto el problema creando un método proxy para onChange.

Eche un vistazo a: https://studio.wavemaker.com/

Saludos, Vinay

¿Podrías publicar un ejemplo de este proxy y cómo se usa? Podría ayudar a depurar esto.

¿Se solucionará pronto? Es taaaan confuso.

Después de una mayor consideración, debo decir que este es el comportamiento esperado. En el ejemplo de plnkr, el valor de ngModel es una cadena de dos enlaces entre el controlador y el alcance de la directiva. Ahora, cuando cambie el valor en la directiva, ngModel actualizará primero el alcance _isolate_ y activará ngChange en el mismo resumen. Dado que el enlace bidireccional aún no ha detectado un cambio en el alcance de la directiva, ngChange en el alcance principal obtiene el valor "antiguo" de x . Ahora, el observador de enlace bidireccional necesita al menos dos ciclos de resumen para estabilizar el valor, por lo que el alcance del controlador solo se actualiza después de que se haya llamado ngChange.
Estoy de acuerdo en que esto es confuso, pero así es como funciona. Puede solucionar esto vinculando un objeto a la directiva que contiene el valor que vincula a ngModel. De esa manera, el valor se establece por referencia tanto en el ámbito aislado como en el del controlador al mismo tiempo y está disponible cuando se ejecuta ngChange: http://plnkr.co/edit/fs7S6yX1a5aeo1Ese522?p=preview

Narretz, estoy totalmente en desacuerdo. Existe un contrato con el desarrollador que consume el código en el que el enlace bidireccional tiene prioridad. Cuando se realiza un cambio en cualquier parte de la cadena de enlace, se debe actualizar toda la cadena antes de continuar. Entonces, incluso si otra directiva hereda de esta directiva, y algo más abajo se une a un enlace de un enlace ... si ese modelo se actualiza, el modelo de alcance del controlador original en la parte inferior de la pila debe actualizarse en el mismo ciclo de resumen antes continuando con otras funciones. Es lo que se espera del consumidor.

Ligeramente relacionado y también confuso. Cuando se usa ngChange para activar la validación en otro campo que comparte un ngModel , el cambio se activa antes de que se actualice $modelValue . Haciendo la validación complicada.

Usando $timeout como solución. Todavía confundido por qué es necesario.

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

saludos, Jakob

¿Hay planes para volver a examinar este tema? ¿Quizás reabrir?

Me encontré con este problema y leí los comentarios, mientras que la forma en que funciona actualmente debido al alcance aislado tiene sentido; el resultado que proviene de esperar un modelo actualizado cuando se activa la llamada de cambio y obtener el modelo una iteración anterior definitivamente no es el comportamiento que espera como desarrollador.

Me adhiero a la pregunta, ¿se volverá a revisar esto?

No lo creo. El comportamiento actual es el esperado y aunque no es deseable en algunos casos, hay al menos tantos casos en los que lo es. Cambiarlo ahora sería (a) un cambio rotundo y (b) haría que muchos casos de uso no fueran compatibles (por ejemplo, donde desea que ngChange modifique el valor _antes_ de que actualice el padre). Es uno de esos casos en los que no se puede hacer feliz a todo el mundo, así que tenemos que ceñirnos a la versión que:

  • Tiene sentido.
  • No rompe a los usuarios existentes.
  • Admite casos de uso más específicos / avanzados, incluso a costa de un pequeño código adicional (ver más abajo).

La implementación actual de ngChange está estrechamente ligada al concepto de ngModel . Las vinculaciones directivas son un concepto totalmente independiente. Los dos pueden trabajar juntos muy bien, pero no deberían afectar la implementación / funcionamiento interno del otro.

#

Como ya se mencionó, si su devolución de llamada específica necesita acceso al valor actualizado, existen algunas opciones:

  1. Pase el valor actualizado como argumento a la devolución de llamada.

  2. Si necesita que se ejecute de forma asincrónica (es decir, después de que finalice el resumen y se hayan propagado todos los enlaces), siempre puede usar $timeout dentro de la devolución de llamada.

  3. Si desea una versión "asíncrona" de ngChange , también es trivial implementarla como una directiva personalizada. (También puede sobrescribir la directiva incorporada, pero definitivamente no lo recomendaría). Por ejemplo:

.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()" />
¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

brijesh1ec picture brijesh1ec  ·  3Comentarios

jtorbicki picture jtorbicki  ·  3Comentarios

WesleyKapow picture WesleyKapow  ·  3Comentarios

visnup picture visnup  ·  3Comentarios

ashish91 picture ashish91  ·  3Comentarios