Angular.js: Resumen infinito sobre el cambio de ubicación en iOS 9 con UIWebView (no en Safari / WKWebView)

Creado en 30 jun. 2015  ·  154Comentarios  ·  Fuente: angular/angular.js

El siguiente HTML simple demuestra el problema:

<!DOCTYPE html>
<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.16/angular.js"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.16/angular-route.js"></script>
        <script>
            angular.module('fail', ['ngRoute'])
            .config(function($routeProvider) {
                $routeProvider
                .when('/a', {
                    template: '<a ng-href="#/b">a</a>'
                })
                .when('/b', {
                    template: '<a ng-href="#/a">b</a>'
                })
                .otherwise({
                    redirectTo: '/a'
                });
            });
        </script>
    </head>
    <body ng-app="fail">
        <div ng-view></div>
    </body>
</html>

Esto se ejecuta como se esperaba en la mayoría de los dispositivos, pero arroja una excepción de resumen infinito en iOS 9.
Puedo reproducir tanto en iPad Air 2 como en iPad de cuarta generación con iOS 9 beta 2.
Me doy cuenta de que probablemente sea un problema en iOS, pero aún así podría valer la pena investigarlo.

iOS $location bug

Comentario más útil

Llegué un poco más lejos en este caso. Hice un cambio en Angular con respecto a la ubicación.
En la parte "actualizar navegador", he cambiado $ rootScope. $ EvalAsync a $ rootScope. $ ApplyAsync.

Los dos métodos parecen hacer exactamente lo mismo. La diferencia no se hace evidente hasta que observa la ejecución real de $ digest. Cuando AngularJS ejecuta un resumen, recorre el árbol de alcance y ejecuta los enlaces $ watch () hasta que no se produzcan más datos sucios. Durante este ciclo de vida, tanto la cola $ applyAsync () como la cola $ evalAsync () se vacían; pero esto sucede en dos lugares muy diferentes.

La cola $ applyAsync () solo se vacía en la parte superior de $ digest antes de que AngularJS comience a buscar datos sucios. Como tal, la cola $ applyAsync () se vaciará, como máximo, una vez durante un $ digest y solo se vaciará si la cola ya estaba llena antes de que comenzara $ digest.

La cola $ evalAsync (), por otro lado, se vacía en la parte superior del bucle while que implementa la "verificación sucia" dentro del $ digest. Esto significa que cualquier expresión agregada a la cola $ evalAsync () durante un resumen se ejecutará en un punto posterior dentro del mismo resumen.

Para hacer esta diferencia más concreta, significa que las expresiones asincrónicas agregadas por $ evalAsync () desde dentro de un enlace $ watch () se ejecutarán en el mismo resumen. Las expresiones asincrónicas agregadas por $ applyAsync () desde dentro de un enlace $ watch () se ejecutarán en un momento posterior (~ 10ms).

Espero que esto ya les ayude a algunos de ustedes :-).


// update browser
    $rootScope.$watch(function $locationWatch() {
      var oldUrl = trimEmptyHash($browser.url());
      var newUrl = trimEmptyHash($location.absUrl());
      var oldState = $browser.state();
      var currentReplace = $location.$$replace;
      var urlOrStateChanged = oldUrl !== newUrl ||
        ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);

      if (initializing || urlOrStateChanged) {
        initializing = false;

        $rootScope.$applyAsync(function() {
          var newUrl = $location.absUrl();
          var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
              $location.$$state, oldState).defaultPrevented;

          // if the location was changed by a `$locationChangeStart` handler then stop
          // processing this location change
          if ($location.absUrl() !== newUrl) return;

          if (defaultPrevented) {
            $location.$$parse(oldUrl);
            $location.$$state = oldState;
          } else {
            if (urlOrStateChanged) {
              setBrowserUrlWithFallback(newUrl, currentReplace,
                                        oldState === $location.$$state ? null : $location.$$state);
            }
            afterLocationChange(oldUrl, oldState);
          }
        });
      }

      $location.$$replace = false;

      // we don't need to return anything because $evalAsync will make the digest loop dirty when
      // there is a change
    });

Todos 154 comentarios

Encontré un problema similar, que sucedió en ios 9, pero funcionó bien en otros dispositivos.

Reproduje este problema con el mismo código proporcionado por santaslow en 1.4.1 / ios 9:

<!DOCTYPE html>
<html>
<head>
    <script src="../static/js/angular/angular.1.4.1.js"></script>
    <script src="../static/js/angular-route/angular-route.1.4.1.js"></script>
    <script>
        angular.module('fail', ['ngRoute'])
                .config(function ($routeProvider) {
                    $routeProvider
                            .when('/a', {
                                template: '<a ng-href="#/b">a</a>'
                            })
                            .when('/b', {
                                template: '<a ng-href="#/a">b</a>'
                            })
                            .otherwise({
                                redirectTo: '/a'
                            });
                }).factory('$exceptionHandler', ['$log', function($log) {
                    return function(exception, cause) {
                        var message = 'angularjs exception: '+exception.message+': caused by "' + cause+ '\njs stack:\n'+exception.stack;
                        $log.error(message);
                    };
                }]);
    </script>
</head>
<body ng-app="fail">
<div ng-view></div>
</body>
</html>

El código anterior se ejecuta normalmente en el navegador de escritorio, android e ios 8 webview, pero en ios 9 arrojará una excepción cuando hago clic en el enlace:

2015-07-02 11:00:09 ... angularjs exception: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: []
http://errors.angularjs.org/1.4.1/$rootScope/infdig?p0=10&p1=%5B%5D: caused by "undefined
js stack:
file:///.../static/js/angular/angular.js:68:32
$digest<strong i="10">@file</strong>:///.../static/js/angular/angular.js:15705:35
$apply<strong i="11">@file</strong>:///.../static/js/angular/angular.js:15935:31
file:///.../static/js/angular/angular.js:12070:30
eventHandler<strong i="12">@file</strong>:///.../static/js/angular/angular.js:3264:25

Ya no puedo reproducir en iOS 9 Beta 3.

Recibo el mismo error con la beta pública de ios9 (13A4293g)

Verifiqué el código anterior en ios 9 beta 3 (13A4293g), ya no es una excepción. Pero la aplicación que usa ng-view todavía arroja excepciones de infdig en ios 9 beta 3.

Recibo el mismo error con la beta pública de ios9 (13A4293g)

Error: [$ rootScope: infdig ] Se
Vigilantes despedidos en las últimas 5 iteraciones: []
http://errors.angularjs.org/1.3.13/ $ rootScope / infdig? p0 = 10 & p1 =% 5B% 5D
file: ///Users/mac5/Library/Developer/CoreSimulator/Devices/749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE/data/Containers/Bundle/Application/9B5EE368-F2A0-4C99-807B-EA/17B24hops79.awww /lib/ionic/js/ionic.bundle.js:8762:32
$ digest @ file : /// Users / mac5 / Library / Developer / CoreSimulator / Devices / 749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE / data / Containers / Bundle / Application / 9B5EE368-F2A0-4C99-807B-EA17B2479E58 / BAShops. app / www / lib / ionic / js / ionic.bundle.js: 22980: 35
$ apply @ file : /// Users / mac5 / Library / Developer / CoreSimulator / Devices / 749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE / data / Containers / Bundle / Application / 9B5EE368-F2A0-4C99-807B-EA17B2479E58 / BAShops. app / www / lib / ionic / js / ionic.bundle.js: 23205: 31
file: ///Users/mac5/Library/Developer/CoreSimulator/Devices/749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE/data/Containers/Bundle/Application/9B5EE368-F2A0-4C99-807B-EA/17B24hops79.awww /lib/ionic/js/ionic.bundle.js:54879:24
eventHandler @ file :
dispatchEvent @ [código nativo]
triggerMouseEvent @ file :
tapClick @ file : ///Users/mac5/Library/Developer/CoreSimulator/Devices/749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE/data/Containers/Bundle/Application/9B5EE368-F2A0-4C99BAS2479.app-EA /www/lib/ionic/js/ionic.bundle.js:2852:20
tapTouchEnd @ file :

También estamos recibiendo este error en la Beta 3 pública de iOS 9 con nuestra propia aplicación Angular. No ocurre en iOS 8.

Como solución alternativa, escribí una directiva simple para reemplazar ng-view y angular-route.js. La solución funcionó bien en nuestra propia aplicación, todas las excepciones de infdig desaparecieron en ios 9 beta / beta 3. A continuación se muestra el código simplificado, que es solo para nuestra propia aplicación, no se consideran los casos de uso general. NO recomiendo a otras personas que utilicen esto:

(function (window) {
    'use strict';

    var myApp = angular.module("myApp");
    var $route = {};

    // replace $routeProvider.when with the function below:
    window.routeWhen = function(path, route) {
        $route[path] = route;
    };

    myApp.directive("myView", ['$compile', '$controller', '$http', '$rootScope', function ($compile, $controller, $http, $rootScope) {

        return {
            priority: -400,
            link: function (scope, element) {
                var parentScope = scope;
                scope = null;

                window.updateView = function (path) {
                    location.hash = '#'+url;
                    if (scope) scope.$destroy();
                    scope = parentScope.$new();

                    var route = $route[path];

                    var linkView = function(html) {
                        element.html(html);
                        var link = $compile(element.contents());
                        var controller = $controller(route.controller, {$scope: scope});
                        element.data('$ngControllerController', controller);
                        element.children().data('$ngControllerController', controller);
                        link(scope);
                        scope.$emit('$viewContentLoaded');
                        if (!$rootScope.$$phase && !scope.$$phase) scope.$apply();
                    };
                    if (route.templateCache) linkView(route.templateCache)
                    else if (route.template) {
                        route.templateCache = document.getElementById(route.template).innerHTML;
                        linkView(route.templateCache)
                    }
                    else $http.get(route.templateUrl;).success(function(html) {
                        route.templateCache = html;
                        linkView(html);
                    });
                };
                //updateView(initialPath);
                // call updateView(path) to set location at other places of the app
            }
        };
    }]);

})(window)

Acabo de instalar iOS 9 Beta 4 y todavía tengo el mismo problema. ¿Alguien mas?

Lo vi en iOS 9 Beta 3 y todavía lo veo en iOS 9 Beta 4.

+1

Sí, también estamos viendo el mismo problema incluso con el enrutador de interfaz de usuario angular. ¿Alguien tiene una solución válida para este problema mientras tanto?

Viendo el mismo problema en uiWebView en el último iOS9.

¿Alguien tiene alguna actualización sobre este tema?

Esto sigue siendo un problema. Reapertura

Parece un problema de iOS. ¿Se rastrea esto en algún sitio web?

+1

+1

Aquí igual. Nuestra aplicación Cordova funciona bien si se ejecuta como web en el iPad Safari, pero el resumen infinito ocurre si se ejecuta como una aplicación Cordova (UIWebView).

¡Exactamente los mismos problemas que @borrull ! Ya experimenté con WKWebView y luego el problema no existe. Pero no podemos usar WKWebView ya que necesitamos Servicio de archivos local (y no queremos ejecutar un servidor local en nuestra aplicación) y cookies. Entonces tiene que hacer algo con UIWebView en combinación con Cordova / Mobile Safari en iOS 9. Actualmente estoy depurando $ locationWatch en Angular porque veo que nuestra aplicación quiere hacer la transición a una ubicación diferente varias veces y luego (después de 10 veces ) se lanza el error de resumen.

mismo problema aquí en iOS9 béta4
Bucle $ digest infinito angular; (

+1, solo en UIWebView, no en WKWebView pero solo podemos usar UIWebView en nuestra aplicación cordova.

+1

Si se trata de un problema específico de iOS, abra un problema en el rastreador de problemas de webkit y proporcione una demostración. Es probable que +1 aquí no cambie nada, ya que realmente suena como un error del navegador.

¿Alguien informó este error a Apple para su investigación?

Informé este error a Apple. Pero tal vez todos deberían hacer lo mismo para llamar su atención sobre el error.

¿Puedes darnos un enlace para que podamos hacer +1?

Está en nuestra cuenta personal de Apple a través del informe de errores ... así que no hay un enlace público; (

¿Podrías publicarlo en openradar y compartir la identificación del rdar para que podamos engañarlo?

lo mismo en iOS9 béta 5:
funciona en safari móvil
funciona en WKWebview que no podemos usar porque no puede servir archivos locales y no es compatible con NSProtocol
NO funciona en UIWebView

lo mismo aquí en IOS 9 Beta 5

También presenté un error a Apple. El enlace de Open Radar es: https://openradar.appspot.com/22186109 (Esto debería ayudar a los perezosos a presentar un error). Deje comentarios si puede mejorar la redacción / explicación del error ;-) Puede descargar el proyecto Xcode para adjuntarlo con la presentación del error en el ticket de Open Radar (Gracias a @santaslow por el JS en el OP)

Creé una versión del mismo proyecto Xcode (de @borrull) pero con ui-router en lugar de ng-route. Exactamente el mismo problema. Para las personas interesadas, pueden encontrar el proyecto aquí: http://s000.tinyupload.com/index.php?file_id=87281871603760127355

También estamos viendo este problema. Pude resolver el problema de que las propiedades de location. * No se actualizan de inmediato cuando angular está en la mezcla. Si intenta asignar un valor a location.hash (como lo que se está haciendo detrás del servicio de ubicación), luego vuelva a leerlo inmediatamente, el valor no ha cambiado. Parece que se producen algunos efectos secundarios como resultado de los controladores jqlite adjuntos a los eventos popstate y hashchanged.

Intentaré subir una muestra cuando esté en una computadora.

+1

@CleverCoder ¿ Alguna actualización sobre la muestra?

Tendré que terminar el caso de reproducción por la mañana, como el código, ya que he estado atado todo el fin de semana. ¡Gracias por el empujón! Mientras iOS 9 cuenta regresivamente, tenemos un gran interés en que esto se resuelva. Subiré algo tan pronto como pueda.

+1

He reproducido lo que creo que es la causa raíz, donde la configuración del hash de ubicación o las propiedades href no se "aplican" de inmediato.
Aquí hay un enlace al proyecto XCode:
https://www.dropbox.com/s/2jkwv2thhm86nly/iOS%209%20Location%20Bug.zip?dl=0
Avísame si no puedes acceder al archivo.

Observe el valor resultante en location.hash, además de adjuntar Safari para depurar. Parece que hay algo que difiere el cambio como resultado de algunos eventos de plomería basados ​​en los eventos 'popstate' y 'hashchange'.

Espero que esto sea útil.

  • Sean

Estamos usando window.location.href en lugar de state.go y parece estar funcionando por ahora. Menos buggy.

El valor de location.hash será correcto después de un giro del runloop. Angular puede solucionar fácilmente este problema retrasando location.hash get en un setTimeout (..., 0). Creo que esto sería ~ 2 cambios en angular.js / src / ng / location.js.

@hober Probó el tiempo de espera con angular como este:

// update $location when $browser url changes
    $browser.onUrlChange(function(newUrl, newState) {
      $rootScope.$evalAsync(function() {
        var oldUrl = $location.absUrl();
        var oldState = $location.$$state;
        var defaultPrevented;

        $location.$$parse(newUrl);
        $location.$$state = newState;

        defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
            newState, oldState).defaultPrevented;

        // if the location was changed by a `$locationChangeStart` handler then stop
        // processing this location change
        if ($location.absUrl() !== newUrl) return;

        if (defaultPrevented) {
          $location.$$parse(oldUrl);
          $location.$$state = oldState;
          setTimeout(function(){ setBrowserUrlWithFallback(oldUrl, false, oldState) }, 0);
        } else {
          initializing = false;
          afterLocationChange(oldUrl, oldState);
        }
      });
      if (!$rootScope.$$phase) $rootScope.$digest();
    });

y

// update browser
    $rootScope.$watch(function $locationWatch() {
      var oldUrl = trimEmptyHash($browser.url());
      var newUrl = trimEmptyHash($location.absUrl());
      var oldState = $browser.state();
      var currentReplace = $location.$$replace;
      var urlOrStateChanged = oldUrl !== newUrl ||
        ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);

      if (initializing || urlOrStateChanged) {
        initializing = false;

        $rootScope.$evalAsync(function() {
          var newUrl = $location.absUrl();
          var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
              $location.$$state, oldState).defaultPrevented;

          // if the location was changed by a `$locationChangeStart` handler then stop
          // processing this location change
          if ($location.absUrl() !== newUrl) return;

          if (defaultPrevented) {
            $location.$$parse(oldUrl);
            $location.$$state = oldState;
          } else {
            if (urlOrStateChanged) {
              setTimeout(function(){ setBrowserUrlWithFallback(newUrl, currentReplace,
                  oldState === $location.$$state ? null : $location.$$state) }, 0);
            }
            afterLocationChange(oldUrl, oldState);
          }
        });
      }

      $location.$$replace = false;

      // we don't need to return anything because $evalAsync will make the digest loop dirty when
      // there is a change
    });

Así que agregué un setTimout alrededor del método setBrowserUrlWithFallback pero no resuelve el problema.

Aquí hay un caso de prueba reducido que no depende de Angular y que demuestra la solución. No me queda claro cómo implementar realmente la solución alternativa en Angular. https://gist.github.com/hober/a29b6c28ac1744c800dd

Llegué un poco más lejos en este caso. Hice un cambio en Angular con respecto a la ubicación.
En la parte "actualizar navegador", he cambiado $ rootScope. $ EvalAsync a $ rootScope. $ ApplyAsync.

Los dos métodos parecen hacer exactamente lo mismo. La diferencia no se hace evidente hasta que observa la ejecución real de $ digest. Cuando AngularJS ejecuta un resumen, recorre el árbol de alcance y ejecuta los enlaces $ watch () hasta que no se produzcan más datos sucios. Durante este ciclo de vida, tanto la cola $ applyAsync () como la cola $ evalAsync () se vacían; pero esto sucede en dos lugares muy diferentes.

La cola $ applyAsync () solo se vacía en la parte superior de $ digest antes de que AngularJS comience a buscar datos sucios. Como tal, la cola $ applyAsync () se vaciará, como máximo, una vez durante un $ digest y solo se vaciará si la cola ya estaba llena antes de que comenzara $ digest.

La cola $ evalAsync (), por otro lado, se vacía en la parte superior del bucle while que implementa la "verificación sucia" dentro del $ digest. Esto significa que cualquier expresión agregada a la cola $ evalAsync () durante un resumen se ejecutará en un punto posterior dentro del mismo resumen.

Para hacer esta diferencia más concreta, significa que las expresiones asincrónicas agregadas por $ evalAsync () desde dentro de un enlace $ watch () se ejecutarán en el mismo resumen. Las expresiones asincrónicas agregadas por $ applyAsync () desde dentro de un enlace $ watch () se ejecutarán en un momento posterior (~ 10ms).

Espero que esto ya les ayude a algunos de ustedes :-).


// update browser
    $rootScope.$watch(function $locationWatch() {
      var oldUrl = trimEmptyHash($browser.url());
      var newUrl = trimEmptyHash($location.absUrl());
      var oldState = $browser.state();
      var currentReplace = $location.$$replace;
      var urlOrStateChanged = oldUrl !== newUrl ||
        ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);

      if (initializing || urlOrStateChanged) {
        initializing = false;

        $rootScope.$applyAsync(function() {
          var newUrl = $location.absUrl();
          var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
              $location.$$state, oldState).defaultPrevented;

          // if the location was changed by a `$locationChangeStart` handler then stop
          // processing this location change
          if ($location.absUrl() !== newUrl) return;

          if (defaultPrevented) {
            $location.$$parse(oldUrl);
            $location.$$state = oldState;
          } else {
            if (urlOrStateChanged) {
              setBrowserUrlWithFallback(newUrl, currentReplace,
                                        oldState === $location.$$state ? null : $location.$$state);
            }
            afterLocationChange(oldUrl, oldState);
          }
        });
      }

      $location.$$replace = false;

      // we don't need to return anything because $evalAsync will make the digest loop dirty when
      // there is a change
    });

Aquí hay otro enfoque. No estoy tan familiarizado con el código base de Angular, pero la lógica parece racional. La función de la URL del navegador (...) depende actualmente de que location.href devuelva inmediatamente la URL correcta. Debido a que este método se llama en el mismo ciclo de ejecución, dentro del ciclo de estabilización $ digest, continúa obteniendo la URL anterior. Este parche aprovecha un 'pendienteHref' para rastrear la asignación, devolviendo ese valor en su lugar, si está configurado. Una vez que el valor se alinea con location.href, se borra el valor pendiente. Durante un conjunto de la URL, se establece un temporizador con 0 ms para detectar el caso en el que no se llama a una URL (). No es perfecto, pero la lógica parece funcionar. Esto es principalmente para considerar un enfoque alternativo que no cree retrasos en el rendimiento. Esto se basa en la etiqueta Angular v1.4.3.

diff --git a/src/ng/browser.js b/src/ng/browser.js
index 928de95..3b9957e 100644
--- a/src/ng/browser.js
+++ b/src/ng/browser.js
@@ -87,7 +87,9 @@ function Browser(window, document, $log, $sniffer) {
   var cachedState, lastHistoryState,
       lastBrowserUrl = location.href,
       baseElement = document.find('base'),
-      reloadLocation = null;
+      reloadLocation = null,
+      pendingHref = null,
+      pendingHrefTimer = null;

   cacheState();
   lastHistoryState = cachedState;
@@ -124,6 +126,18 @@ function Browser(window, document, $log, $sniffer) {
     if (location !== window.location) location = window.location;
     if (history !== window.history) history = window.history;

+    // Schedule cleaning up pendingHref on the next run loop for setting URL. This is to handle
+    // the case where the browser doesn't update the location.* properties immediately
+    if (!pendingHrefTimer && pendingHref && url) {
+      pendingHrefTimer = setTimeout(function () {
+        if (location.href == pendingHref) {
+          console.log('Actual href updated... setting pendingHref to null from setTimeout');
+          pendingHref = null;
+        }
+        pendingHrefTimer = null;
+      }, 0);
+    }
+
     // setter
     if (url) {
       var sameState = lastHistoryState === state;
@@ -147,6 +161,7 @@ function Browser(window, document, $log, $sniffer) {
         // Do the assignment again so that those two variables are referentially identical.
         lastHistoryState = cachedState;
       } else {
+        pendingHref = url;
         if (!sameBase || reloadLocation) {
           reloadLocation = url;
         }
@@ -161,10 +176,22 @@ function Browser(window, document, $log, $sniffer) {
       return self;
     // getter
     } else {
+      var href = location.href.replace(/%27/g, "'");
+      if (pendingHref) {
+        //console.log('.. using pendingHref for url() return value');
+        href = pendingHref;
+      }
+
+      if (location.href == pendingHref) {
+        console.log('Actual href updated... setting pendingHref to null in getter');
+        pendingHref = null;
+      }
+
+      //var href = location.href.replace(/%27/g,"'");
       // - reloadLocation is needed as browsers don't allow to read out
       //   the new location.href if a reload happened.
       // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
-      return reloadLocation || location.href.replace(/%27/g,"'");
+      return reloadLocation || href;
     }
   };

¡Gracias @CleverCoder por la solución! ¡Parece funcionar a las mil maravillas! : +1:

@CleverCoder
Sería genial si realiza una solicitud de extracción con esto para el equipo angular.

@viattik Me sorprendería un poco si el equipo angular adoptara una solución alternativa para UIWebView en iOS9, ya que el error está en UIWebView (Apple). Pero siempre puedes intentar ...

@ raftheunis87
Hay muchos errores en diferentes navegadores y muchas soluciones para esos errores en el código angular.
Aunque no son oficialmente compatibles con UIWebView, muchas aplicaciones híbridas se romperán y el uso de angular será imposible en las aplicaciones híbridas en el último iOS hasta que Apple corrija ese error. Y ese es un problema bastante importante, diría yo.
Así que lo probaría.

@viattik Estoy totalmente de acuerdo. Y por cierto: Apple nos comunicó que es poco probable que solucionen el error de UIWebView. Entonces, de hecho: pruébalo ;-)

Amigos, si pudieran publicar esto como un error de webkit, idealmente con un caso de reproducción, haré un seguimiento con algunos de nuestros contactos en el lado de WebKit. https://bugs.webkit.org/

@naomiblack
No estoy seguro si es un error de webkit. Porque solo sucede en UIWebView en iOS9. Safari en iOS9 funciona bien.

@ raftheunis87 gracias por sus sugerencias de código, funcionó perfectamente

@ raftheunis87 @CleverCoder es una forma de trabajar con ionic-angular? ¿Puedes ser mas específico?

@abrahamrkj No tengo ninguna experiencia con ionic. ¿Pero sus personalizaciones son angulares cuando se usa iónico? De lo contrario, diría que la misma solución funcionaría con ionic-angular ...

@ raftheunis87 https://github.com/driftyco/ionic/tree/master/js este es el angular que están usando.

@CleverCoder +1 para la solicitud de extracción. Estoy de acuerdo con @viattik en que este es un tema importante, ya que romperá muchas aplicaciones híbridas.

Una solicitud de extracción puede ser en un futuro cercano, aunque dudo ya que no estoy tan cerca de la base de código Angular como otros. Revisaré la solución pronto y trataré de que sea a prueba de balas. Parece extraño que una propiedad en el objeto window.location no cambie 'inmediatamente'. En las pruebas que hice, noté que el cambio se mantuvo siempre que los ganchos de eventos 'popstate' y 'hashchange' no estuvieran en su lugar, lo que me llevó a pensar que la causa del cambio diferido en realidad puede ser hacer algo. más ... Quizás el momento de esos eventos ha cambiado (creo que eso es lo que estaba observando).
Voy a ver esto durante los próximos días, y si no surge nada mejor, profundizaré un poco más para confirmar lo que sé hasta ahora y que no hay un mejor lugar para abordar el cambio de comportamiento. Perdón si esto es confuso. Están sucediendo muchas cosas bajo el capó que todavía no entiendo completamente con respecto a esos eventos.
¡Salud!

... y sí, @borrull , estoy de acuerdo. Si Apple no hace más cambios, entonces esta es una bomba de tiempo seria que realmente conducirá a una mala prensa y a señalar con el dedo. No soy un fanático de los temporizadores como solución alternativa (preferiría haber mejorado el flujo lógico en torno a estas propiedades integradas), pero si no podemos depender de que el valor cambie una vez que esté configurado, ¿dónde dibujamos el ¿línea? ¿En qué otras propiedades no podemos confiar? Es extraño.

@CleverCoder Solo quería

@CleverCoder Gracias por la solución proporcionada.

Derivé una solución que usa la función decoradora de angular y viene sin parchear la fuente angular.

Con cssua, esta configuración se puede configurar para usarse solo en entornos específicos.

    app.config(['$provide', ($provide) => {
        $provide.decorator('$browser', ['$delegate', ($delegate) => {
            var origUrl = $delegate.url;

            var pendingHref = null;
            var pendingHrefTimer = null;

            var newUrl = function (url, replace, state) {

                if (url) {
                    // setter
                    var result = origUrl(url, replace, state);

                    if (window.location.href != url) {

                        if (pendingHref != url) {
                            pendingHref = url;

                            if (pendingHrefTimer) clearTimeout(pendingHrefTimer);

                            pendingHrefTimer = setTimeout(function () {
                                if (window.location.href == pendingHref) {
                                    pendingHref = null;
                                }
                                pendingHrefTimer = null;
                            }, 0);
                        }
                    }

                    return result;
                } else {
                    // getter
                    if (pendingHref == window.location.href) {
                        pendingHref = null;
                    }

                    return pendingHref || origUrl(url, replace, state);
                }
            };

            $delegate.url = newUrl;

            return $delegate;
        }]);
    }]);

@CleverCoder Ver # 12635

@ jd-carroll: Eso es realmente interesante. Podría volver a visitar esto un poco más tarde hoy, cuando tenga tiempo. Bastante inundado de cosas. Eso solo crea más misterio, ya que parece un problema separado que no debería introducir un retraso en la actualización de los valores de location. *.

@realityfilter : Es curioso que menciones al decorador ... Acabo de terminar de implementar algo usando la funcionalidad del decorador angular. ¡Lindo!

Oigan todos,
Solo quería agregar que esta corrección introdujo un error en nuestro código que fue fácil de corregir.

Nuestras plantillas tenían etiquetas de anclaje que usaban href = "#" y ng-click = "someCall ()". El href estaba provocando que el sitio fuera a index.html usando esta corrección. Eliminar el href solucionó el problema.

Nuestra aplicación se rompe durante la navegación del botón Atrás en Ionic, primero va a la nueva vista, luego regresa parcialmente a la vista anterior y luego vuelve a la nueva vista en IOS9 beta cualquier resolución para Ionic

El mismo problema con iOS 9 beta 5 13A4325c, Angular 1.4.0 (con cordova-ios 3.9.1). ¡Con suerte, se solucionará el error subyacente de UIWebView!

El mismo problema ocurre en Angular v1.2.27

Lo he rastreado hasta la versión 1.2.27, el error no está en la versión 1.2.26 anterior.

Específicamente, este compromiso es el culpable.

@damrbaby Parece que podría tener algo que ver con eso.

Pero el hecho de que esté trabajando con la última versión de angular en Mobile Safari aclara que tiene que ver algo con uiWebView en iOS 9. Por lo tanto, el cambio que hicieron en el código fuente de Angular no es necesariamente algo malo.

@CleverCoder @realityfilter @ jyc66 Solo quería darte las gracias, acabas de salvarme el día.

El problema sigue presente en iOS9 GM Seed, ¡así que actualice sus aplicaciones!

Puedo confirmar que el problema sigue presente en iOS9 GM (13A340)

Entonces, esto significa que Apple rompió algo y tenemos que actualizar nuestras aplicaciones nuevamente (algunas de las cuales no han cambiado durante meses o incluso más de un año) para evitar que se bloqueen. Tiene sentido :(. Preferiría que Apple lo arreglara para los lanzamientos de iOS9. Ir a la última versión de Angular en una aplicación antigua seguramente romperá algunas otras cosas también.

Dudo mucho que Angular sea el único marco que tiene problemas con iOS9.

Entonces, @adamdbradley , @perrygovier y @mhartington del equipo Ionic han estado trabajando todo el día en una solución que funcionará para Ionic y también para aplicaciones simples de Angular. El objetivo es ser una solución inmediata que no requiera modificar Angular y (con suerte) funcionará en la mayoría de las versiones 1.2+ Angular.

Aquí está nuestra solución empaquetada actual que decora y corrige $browser aplicando este parche . Nota: esto se basa en Angular 1.4.3 y es una especie de "clon con correcciones" de browser.js de Angular propiamente dicho: https://github.com/driftyco/ionic/blob/ios9-patch/js /angular/service/decorators/ios9-browser-fix.js

También colocamos el parche en nuestro CDN. No recomiendo usar el archivo CDN para la producción, solo está allí, por lo que es más fácil de probar en este momento.

Para probarlo, coloque esta etiqueta de secuencia de comandos debajo de su archivo angular o ionic.bundle.js:

<script src="https://code.ionicframework.com/patch/ios9-$browser-patch.js"></script>

Además, ahora mismo aplica el parche ya sea que esté ejecutando iOS9 o no. Eso pronto se solucionará de modo que solo se ejecute en iOS 9 UIWebView.

Siga el problema Ionic correspondiente aquí: https://github.com/driftyco/ionic/issues/4082#issuecomment -139079725

Hola todos,

UI-sref funciona a la perfección, pero $ state.go rompe la animación del botón de retroceso
y la página parpadea mucho incluso después de aplicar esta corrección.

Saludos,
Ajay Singh

El jueves 10 de septiembre de 2015 a las 6:23 a. M., Max Lynch [email protected] escribió:

Entonces, @adamdbradley https://github.com/adamdbradley , @perrygovier
https://github.com/perrygovier y @mhartington
https://github.com/mhartington del equipo Ionic han estado trabajando
todo el día en una solución que funcionará para Ionic y para aplicaciones simples de Angular como
bien. El objetivo es ser una solución inmediata que no requiera modificaciones.
Angular, y (con suerte) funcionará en la mayoría de las versiones 1.2+ Angular.

Aquí está nuestra solución empaquetada actual que decora y corrige $ browser por
aplicando este parche
https://github.com/angular/angular.js/issues/12241#issuecomment -130744518.
Nota: esto se basa en Angular 1.4.3 y es una especie de "clon con correcciones" de
browser.js de Angular propiamente dicho:
https://github.com/driftyco/ionic/blob/ios9-patch/js/angular/service/decorators/ios9-browser-fix.js

También colocamos el parche en nuestro CDN. No recomiendo usar el archivo CDN
para la producción, solo está ahí, por lo que es más fácil probarlo ahora mismo.

Pruébelo y háganos saber cómo va, gracias.

Para probarlo, coloque esta etiqueta de secuencia de comandos debajo de su angular o
Archivo ionic.bundle.js: