Angular.js: Unendliche Übersicht bei Standortwechsel unter iOS 9 mit UIWebView (nicht in Safari/ WKWebView)

Erstellt am 30. Juni 2015  ·  154Kommentare  ·  Quelle: angular/angular.js

Der folgende einfache HTML-Code veranschaulicht das Problem:

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

Dies wird auf den meisten Geräten wie erwartet ausgeführt, löst jedoch unter iOS 9 eine unendliche Digest-Ausnahme aus.
Ich kann sowohl auf dem iPad Air 2 als auch auf dem iPad der 4. Generation mit iOS 9 Beta 2 reproduzieren.
Mir ist klar, dass es wahrscheinlich ein Problem in iOS ist, aber es könnte sich dennoch lohnen, es zu untersuchen.

iOS $location bug

Hilfreichster Kommentar

Bin in dieser Sache ein bisschen weiter gekommen. Ich habe in Angular eine Änderung bezüglich des Standorts vorgenommen.
Im Teil "Browser aktualisieren" habe ich $rootScope.$evalAsync in $rootScope.$applyAsync geändert.

Die beiden Methoden scheinen genau das gleiche zu tun. Der Unterschied wird erst deutlich, wenn Sie sich die tatsächliche Ausführung von $digest ansehen. Wenn AngularJS einen Digest ausführt, durchläuft es den Scope-Baum und führt $watch()-Bindungen aus, bis keine schmutzigen Daten mehr erzeugt werden. Während dieses Lebenszyklus werden sowohl die Warteschlange $applyAsync() als auch die Warteschlange $evalAsync() geleert; Dies geschieht jedoch an zwei sehr unterschiedlichen Orten.

Die $applyAsync()-Warteschlange wird nur oben im $digest geleert, bevor AngularJS mit der Überprüfung auf schmutzige Daten beginnt. Daher wird die $applyAsync()-Warteschlange höchstens einmal während eines $digest geleert und wird nur geleert, wenn die Warteschlange bereits gefüllt war, bevor der $digest gestartet wurde.

Die $evalAsync()-Warteschlange hingegen wird am Anfang der while-Schleife geleert, die den "dirty check" in $digest implementiert. Dies bedeutet, dass jeder Ausdruck, der während eines Digests zur $evalAsync()-Warteschlange hinzugefügt wird, zu einem späteren Zeitpunkt innerhalb desselben Digests ausgeführt wird.

Um diesen Unterschied konkreter zu machen, bedeutet dies, dass asynchrone Ausdrücke, die von $evalAsync() aus einer $watch()-Bindung hinzugefügt werden, im selben Digest ausgeführt werden. Asynchrone Ausdrücke, die von $applyAsync() innerhalb einer $watch()-Bindung hinzugefügt werden, werden zu einem späteren Zeitpunkt (~10 ms) ausgeführt.

Hoffe das hilft schon einigen von euch :-).


// 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
    });

Alle 154 Kommentare

Ich habe ein ähnliches Problem festgestellt, das unter iOS 9 aufgetreten ist, aber auf anderen Geräten funktioniert.

Ich habe dieses Problem mit dem gleichen Code reproduziert, der von santaslow auf 1.4.1 / ios 9 bereitgestellt wurde:

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

Der obige Code wird normalerweise im Desktop-Browser, Android und iOS 8 Webview ausgeführt, aber unter iOS 9 wird eine Ausnahme ausgelöst, wenn ich auf den Link klicke

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

Ich kann in iOS 9 Beta 3 nicht mehr reproduzieren.

Ich erhalte den gleichen Fehler mit der öffentlichen Beta von ios9 (13A4293g)

Ich habe den obigen Code auf iOS 9 Beta 3 (13A4293g) überprüft, keine Ausnahme mehr. Aber die App mit der Verwendung von ng-view löst immer noch infdig-Ausnahmen auf iOS 9 Beta 3 aus.

Ich erhalte den gleichen Fehler mit der öffentlichen Beta von ios9 (13A4293g)

Fehler: [$ rootScope:infdig ] 10 $digest() Iterationen erreicht. Abbruch!
Beobachter, die in den letzten 5 Iterationen gefeuert wurden: []
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-EA17B2479E58/BAShops.app/www /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/BA 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/BA 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-EA17B2479E58/BAShops.app/www /lib/ionic/js/ionic.bundle.js:54879:24
eventHandler@file :/// 47F9- A1FC- EA17B2479E58 /BA /www/lib/ionic/js/ionic.bundle.js:11713:25
DispatchEvent@[nativer Code]
triggerMouseEvent@file :/// 47F9- A1FC- F2A0-4C99-807B- EA17B2479E58/ /www/lib/ionic/js/ionic.bundle.js:2863:20
tapClick@file :/// 47F9- A1FC- EA17B2479E58 /BA /www/lib/ionic/js/ionic.bundle.js:2852:20
tapTouchEnd@file :/// 47F9- A1FC- EA17B2479E58 /BA /www/lib/ionic/js/ionic.bundle.js:2975:13

Wir erhalten diesen Fehler auch in der öffentlichen Beta 3 von iOS 9 mit unserer eigenen Angular-Anwendung. Es tritt nicht in iOS 8 auf.

Als Workaround habe ich eine einfache Direktive geschrieben, um ng-view und angle-route.js zu ersetzen. Die Lösung hat in unserer eigenen Anwendung gut funktioniert, alle Infdig-Ausnahmen sind in iOS 9 Beta/Beta 3 verschwunden. Unten ist der vereinfachte Code, der nur für unsere eigene Anwendung gedacht ist, allgemeine Anwendungsfälle werden nicht berücksichtigt. Ich empfehle anderen Leuten NICHT, dies zu verwenden:

(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)

Habe gerade iOS 9 Beta 4 installiert und habe immer noch das gleiche Problem. Irgendjemand anderes?

Ich habe es in iOS 9 Beta 3 gesehen und sehe es immer noch in iOS 9 Beta 4.

+1

Ja, wir sehen das gleiche Problem auch bei einem eckigen UI-Router. Hat in der Zwischenzeit jemand eine gültige Lösung für dieses Problem?

Das gleiche Problem wird in uiWebView auf dem neuesten iOS9 angezeigt.

Hat jemand Updates zu diesem Thema?

Dies ist immer noch ein Thema. Wiedereröffnung

Es sieht nach einem iOS-Problem aus. Wird das irgendwo im Webkit nachverfolgt?

+1

+1

Hier gilt das gleiche. Unsere Cordova-App läuft gut, wenn sie als Web auf dem iPad Safari ausgeführt wird, aber der unendliche Digest tritt auf, wenn sie als Cordova-App (UIWebView) ausgeführt wird.

Genau die gleichen Probleme wie bei @borrull ! Habe schon mit WKWebView experimentiert und dann ist das Problem nicht mehr vorhanden. Aber wir können WKWebView nicht verwenden, da wir Local File Serving (und wir keinen lokalen Server in unserer Anwendung betreiben wollen) und Cookies benötigen. Es hat also etwas mit UIWebView in Kombination mit Cordova/Mobile Safari auf iOS 9 zu tun. Ich debugge derzeit die $locationWatch in Angular, weil ich sehe, dass unsere Anwendung mehrmals an einen anderen Ort wechseln möchte und dann (nach 10 Malen .) ) wird der Digest-Fehler geworfen.

gleiches Problem hier auf iOS9 beta4
Angular unendliche $digest-Schleife ;(

+1, nur in UIWebView, nicht in WKWebView, aber wir können UIWebView nur in unserer Cordova-App verwenden.

+1

Wenn es sich um ein iOS-spezifisches Problem handelt, öffnen Sie bitte ein Problem im Webkit-Problem-Tracker und stellen Sie eine Demo bereit! +1 hier wird wahrscheinlich nichts ändern, da es wirklich nach einem Browser-Bug klingt.

Hat jemand Apple diesen Fehler zur Untersuchung gemeldet?

Ich habe diesen Fehler an Apple gemeldet. Aber vielleicht sollten Sie alle dasselbe tun, um ihre Aufmerksamkeit auf den Fehler zu lenken.

Können Sie uns einen Link geben, damit wir +1 können?

Es ist in unserem persönlichen Apple-Konto über den Bug-Reporter .. also kein öffentlicher Link ;(

Könnten Sie es auf openradar posten und die rdar-ID teilen, damit wir es täuschen können!

Gleiches auf iOS9 Beta 5:
funktioniert auf mobiler Safari
funktioniert mit WKWebview, das wir nicht verwenden können, da es keine lokalen Dateien bereitstellen kann und NSProtocol nicht unterstützt
funktioniert NICHT auf UIWebView

das gleiche hier auf IOS 9 Beta 5

Ich habe auch einen Fehler bei Apple gemeldet. Der Open Radar-Link lautet: https://openradar.appspot.com/22186109 (Dies sollte denjenigen helfen, die faul sind, einen Fehler zu melden). Bitte hinterlassen Sie Kommentare, wenn Sie die Fehlerbeschreibung/Erklärung verbessern können ;-) Sie können das Xcode-Projekt herunterladen, um es mit der Fehlerdatei an das Open Radar-Ticket @santaslow für das JS im OP)

Ich habe eine Version desselben Xcode-Projekts (von @borrull) erstellt, jedoch mit ui-router anstelle von ng-route. Genau das gleiche Problem. Für Interessierte findet ihr das Projekt hier: http://s000.tinyupload.com/index.php?file_id=87281871603760127355

Wir sehen auch dieses Problem. Ich konnte dem Problem nachgehen, dass die location.*-Eigenschaften nicht sofort aktualisiert werden, wenn eckig in der Mischung ist. Wenn Sie versuchen, location.hash einen Wert zuzuweisen (wie das, was hinter dem Ortungsdienst getan wird), dann sofort wieder einlesen, der Wert hat sich nicht geändert. Es scheint einige Nebenwirkungen zu geben, die als Ergebnis der jqlite-Handler auftreten, die an popstate- und hashchanged-Ereignisse angehängt sind.

Ich werde versuchen, ein Beispiel hochzuladen, wenn ich an einem Computer bin.

+1

@CleverCoder Irgendwelche Updates zum Beispiel?

Ich muss den Repro-Fall morgen früh fertigstellen, da der Code da ich das ganze Wochenende gefesselt habe. Danke für den Anstoß! Da iOS 9 herunterzählt, haben wir ein berechtigtes Interesse daran, dass dies behoben wird. Ich werde so schnell wie möglich etwas hochladen.

+1

Ich habe reproduziert, was meiner Meinung nach die Hauptursache ist, bei der das Festlegen der Hash- oder Href-Eigenschaften für den Standort nicht sofort "zutreffen".
Hier ist ein Link zum XCode-Projekt:
https://www.dropbox.com/s/2jkwv2thhm86nly/iOS%209%20Location%20Bug.zip?dl=0
Lassen Sie es mich wissen, wenn Sie nicht auf die Datei zugreifen können.

Beobachten Sie den resultierenden Wert in location.hash und hängen Sie Safari an Debug an. Es scheint etwas zu geben, das die Änderung aufgrund einiger Ereignisinstallationen auf der Grundlage der Ereignisse "Popstate" und "Hashchange" verzögert.

Ich hoffe, das ist hilfreich.

  • Sean

Wir verwenden window.location.href anstelle von state.go und es scheint vorerst zu funktionieren. Weniger Buggy.

Der Wert von location.hash ist nach einer Drehung des Runloop korrekt. Angular kann dieses Problem leicht umgehen, indem das Abrufen von location.hash in einem setTimeout(..., 0) verzögert wird. Ich denke, das wären ~ 2 Änderungen an angle.js/src/ng/location.js.

@hober Habe das Timeout mit

// 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();
    });

und

// 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
    });

Also habe ich ein setTimout um die setBrowserUrlWithFallback-Methode hinzugefügt, aber es behebt das Problem nicht.

Hier ist ein reduzierter Testfall, der nicht auf Angular basiert und die Problemumgehung demonstriert. Wie man den Workaround in Angular tatsächlich umsetzt, ist mir unklar. https://gist.github.com/hober/a29b6c28ac1744c800dd

Bin in dieser Sache ein bisschen weiter gekommen. Ich habe in Angular eine Änderung bezüglich des Standorts vorgenommen.
Im Teil "Browser aktualisieren" habe ich $rootScope.$evalAsync in $rootScope.$applyAsync geändert.

Die beiden Methoden scheinen genau das gleiche zu tun. Der Unterschied wird erst deutlich, wenn Sie sich die tatsächliche Ausführung von $digest ansehen. Wenn AngularJS einen Digest ausführt, durchläuft es den Scope-Baum und führt $watch()-Bindungen aus, bis keine schmutzigen Daten mehr erzeugt werden. Während dieses Lebenszyklus werden sowohl die Warteschlange $applyAsync() als auch die Warteschlange $evalAsync() geleert; Dies geschieht jedoch an zwei sehr unterschiedlichen Orten.

Die $applyAsync()-Warteschlange wird nur oben im $digest geleert, bevor AngularJS mit der Überprüfung auf schmutzige Daten beginnt. Daher wird die $applyAsync()-Warteschlange höchstens einmal während eines $digest geleert und wird nur geleert, wenn die Warteschlange bereits gefüllt war, bevor der $digest gestartet wurde.

Die $evalAsync()-Warteschlange hingegen wird am Anfang der while-Schleife geleert, die den "dirty check" in $digest implementiert. Dies bedeutet, dass jeder Ausdruck, der während eines Digests zur $evalAsync()-Warteschlange hinzugefügt wird, zu einem späteren Zeitpunkt innerhalb desselben Digests ausgeführt wird.

Um diesen Unterschied konkreter zu machen, bedeutet dies, dass asynchrone Ausdrücke, die von $evalAsync() aus einer $watch()-Bindung hinzugefügt werden, im selben Digest ausgeführt werden. Asynchrone Ausdrücke, die von $applyAsync() innerhalb einer $watch()-Bindung hinzugefügt werden, werden zu einem späteren Zeitpunkt (~10 ms) ausgeführt.

Hoffe das hilft schon einigen von euch :-).


// 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
    });

Hier ist ein anderer Ansatz. Ich bin mit der Angular-Codebasis nicht so vertraut, aber die Logik scheint rational zu sein. Die Browser-URL(...)-Funktion hängt derzeit davon ab, dass location.href sofort die richtige URL zurückgibt. Da diese Methode innerhalb des Stabilisierungszyklus $digest in derselben Ausführungsschleife aufgerufen wird, wird weiterhin die alte URL abgerufen. Dieser Patch nutzt ein 'pendingHref', um die Zuweisung zu verfolgen, und gibt stattdessen diesen Wert zurück, falls er gesetzt ist. Sobald der Wert mit location.href abgeglichen ist, wird der ausstehende Wert gelöscht. Während eines Satzes der URL wird ein Timer mit 0ms gesetzt, um den Fall abzufangen, in dem ein url() get nicht aufgerufen wird. Es ist nicht perfekt, aber die Logik scheint zu funktionieren. Dies dient hauptsächlich der Betrachtung eines alternativen Ansatzes, der keine Leistungsverzögerungen verursacht. Dies basiert auf dem Angular-Tag 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;
     }
   };

Danke @CleverCoder für die Lösung! Scheint wie ein Zauber zu funktionieren! :+1:

@CleverCoder
Wäre toll, wenn du mit diesem einen Pull-Request an das eckige Team machst.

@viattik Ich würde mich irgendwie überraschen, wenn das Angular-Team eine Problemumgehung für UIWebView unter iOS9 übernehmen würde, da der Fehler in UIWebView (Apple) selbst liegt. Aber du kannst es immer versuchen...

@raftheunis87
Es gibt viele Fehler in verschiedenen Browsern und viele Problemumgehungen für diese Fehler in eckigem Code.
Obwohl sie UIWebView nicht offiziell unterstützen, werden viele Hybrid-Apps kaputt gehen und die Verwendung von Angular wird in Hybrid-Apps im neuesten iOS unmöglich sein, bis Apple diesen Fehler behoben hat. Und das ist ein ziemlich großes Problem, würde ich sagen.
Also ich würde es versuchen.

@viattik Ich stimme

Leute, wenn Sie dies als Webkit-Bug posten könnten, idealerweise mit einem Repro-Fall, werde ich einige unserer Kontakte auf der WebKit-Seite nachfassen. https://bugs.webkit.org/

@naomiblack
Ich bin mir nicht sicher, ob es sich um einen Webkit-Fehler handelt. Weil es nur in UIWebView auf iOS9 passiert. Safari unter iOS9 funktioniert einwandfrei.

@raftheunis87 danke für deine

@raftheunis87 @CleverCoder ist eine Möglichkeit, mit

@abrahamrkj Ich habe keine Erfahrung mit ionisch. Aber sind ihre Anpassungen bei der Verwendung von ionisch eckig? Ansonsten würde ich sagen, dass der gleiche Fix mit Ionenwinkel funktionieren würde ...

@raftheunis87 https://github.com/driftyco/ionic/tree/master/js Dies ist der Winkel, den sie verwenden.

@CleverCoder +1 für den Pull-Request. Ich stimme @viattik zu, dass dies ein wichtiges Thema ist, da es viele Hybrid-Apps

Eine Pull-Anfrage könnte in naher Zukunft folgen, obwohl ich zögere, da ich nicht so nah an der Angular-Codebasis bin wie andere. Ich werde die Lösung bald wieder aufgreifen und versuchen, sie kugelsicher zu machen. Es erscheint seltsam, dass sich eine Eigenschaft des window.location-Objekts nicht „sofort“ ändert. In den Tests, die ich durchgeführt habe, habe ich festgestellt, dass die Änderung bestehen blieb, solange die Ereignis-Hooks 'popstate' und 'hashchange' nicht vorhanden waren, was mich zu der Annahme veranlasste, dass die Ursache für die verzögerte Änderung tatsächlich darin besteht, etwas zu tun sonst... Vielleicht hat sich der Zeitpunkt dieser Ereignisse geändert (ich glaube, das habe ich beobachtet).
Ich werde mir das in den nächsten Tagen ansehen, und wenn nichts Besseres auftaucht, werde ich ein wenig tiefer graben, um zu bestätigen, was ich bisher weiß und dass es keinen besseren Ort gibt, um die Verhaltensänderung anzugehen. Tut mir leid, wenn das verwirrend ist. Es passiert viel unter der Haube, was ich in Bezug auf diese Ereignisse immer noch nicht ganz verstehe.
Prost!

... und ja, @borrull , da stimme ich zu. Wenn Apple keine Änderungen mehr vornimmt, dann ist dies eine ernsthafte tickende Zeitbombe, die wirklich zu schlechter Presse und Fingerzeigen führen wird. Ich bin kein Fan von Timern als Problemumgehung (ich hätte lieber einen verbesserten Logikfluss um diese integrierten Eigenschaften), aber wenn wir uns nicht darauf verlassen können, dass sich der Wert ändert, sobald er festgelegt ist, wo ziehen wir dann die? Linie? Welchen anderen Eigenschaften können wir nicht vertrauen? Es ist ein seltsames.

@CleverCoder Ich wollte bedanken , Ihr Patch hat wirklich den Tag gerettet!

@CleverCoder Danke für die gegebene Problemumgehung.

Ich habe eine Lösung abgeleitet, die das Dekorator-Feature von Angular verwendet und ohne Patchen der Angular-Quelle kommt.

Mit cssua kann dieses Setup so konfiguriert werden, dass es nur in bestimmten Umgebungen verwendet wird.

    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 Siehe #12635

@jd-carroll: Das ist wirklich interessant. Ich werde das vielleicht heute etwas später wiederholen, wenn ich Zeit habe. Ziemlich vollgestopft mit Sachen. Das schafft nur noch mehr Mysterium, da dies wie ein separates Problem zu sein scheint, das keine Verzögerung bei der Ortung verursachen sollte.

@realityfilter : Lustig, dass Sie den Dekorateur erwähnen ... Ich habe gerade etwas mit der Angular-Dekoratorfunktion implementiert. Nett!

Hallo zusammen,
Ich wollte nur hinzufügen, dass dieser Fix einen Fehler in unserem Code einführte, der einfach zu beheben war.

Unsere Vorlagen hatten Anker-Tags, die href="#" und ng-click="someCall()" verwendeten. Das href führte dazu, dass die Site mit diesem Fix zu index.html wechselte. Das Entfernen der href hat das Problem behoben.

Unsere App bricht während der Navigation mit der Zurück-Taste in Ionic ab. Sie wechselt zuerst zur neuen Ansicht, dann teilweise zur alten Ansicht und wechselt dann wieder zur neuen Ansicht auf IOS9 Beta alle Auflösungen für Ionic

Gleiches Problem mit iOS 9 Beta 5 13A4325c, Angular 1.4.0 (mit Cordova-ios 3.9.1). Hoffentlich wird der zugrunde liegende UIWebView-Fehler behoben!

Das gleiche Problem tritt bei Angular v1.2.27 auf

Ich habe es bis zur Version 1.2.27 zurückverfolgt, der Fehler ist nicht in der vorherigen Version 1.2.26 enthalten.

Insbesondere dieser Commit ist der Schuldige.

@damrbaby Sieht so aus, als könnte ich etwas damit zu tun haben.

Aber die Tatsache, dass es mit der neuesten Version von Angular in Mobile Safari arbeitet, macht deutlich, dass es etwas mit uiWebView auf iOS 9 zu tun hat. Die Änderung, die sie im Angular-Quellcode vorgenommen haben, ist also nicht unbedingt schlecht.

@CleverCoder @realityfilter @jyc66 Ich wollte mich nur bedanken, du hast mir gerade den Tag gerettet.

Das Problem ist immer noch auf iOS9 GM Seed vorhanden, also aktualisieren Sie Ihre Apps!

Ich kann bestätigen, dass das Problem in iOS9 GM (13A340) immer noch vorhanden ist.

Das bedeutet also, dass Apple etwas kaputt gemacht hat und wir unsere Apps (von denen sich einige seit Monaten oder sogar über einem Jahr nicht geändert haben) erneut aktualisieren müssen, damit sie nicht abstürzen. Macht Sinn :( . Ich möchte lieber, dass Apple es für iOS9-Starts repariert. Wenn Sie in einer alten App zur neuesten Angular-Version wechseln, werden auch andere Dinge kaputt gehen.

Ich bezweifle stark, dass Angular das einzige Framework ist, das Probleme mit iOS9 hat?

Also haben @adamdbradley , @perrygovier und @mhartington vom Ionic Team den ganzen Tag an einem Fix gearbeitet, der sowohl für Ionic als auch für einfache Angular-Apps funktioniert. Das Ziel ist ein Drop-In-Fix, der keine Änderung von Angular erfordert und (hoffentlich) in den meisten Angular-Versionen ab 1.2 funktioniert.

Hier ist unsere aktuelle gebündelte Lösung, die $browser durch Anwenden dieses Patches dekoriert und repariert. Hinweis: Dies basiert auf Angular 1.4.3 und ist eine Art "Klon mit Fixes" von browser.js von Angular: https://github.com/driftyco/ionic/blob/ios9-patch/js /angular/service/decorators/ios9-browser-fix.js

Wir haben den Patch auch auf unserem CDN platziert. Ich empfehle nicht, die CDN-Datei für die Produktion zu verwenden, sie ist nur vorhanden, damit sie jetzt einfacher getestet werden kann.

Um es zu testen, platzieren Sie dieses Skript-Tag unter Ihrer Datei angle oder ionic.bundle.js:

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

Außerdem wird der Patch im Moment angewendet, unabhängig davon, ob Sie iOS9 ausführen oder nicht. Das wird bald behoben, so dass es nur auf dem iOS 9 UIWebView läuft.

Folgen Sie dem entsprechenden Ionic-Problem hier: https://github.com/driftyco/ionic/issues/4082#issuecomment -139079725

Hi an alle,

UI-sref funktioniert wie ein Zauber, aber $state.go unterbricht die Animation der Zurück-Schaltfläche
und die Seite flackert stark, selbst nachdem dieser Fix angewendet wurde.

Grüße,
Ajay Singh

Am Do, 10. September 2015 um 6:23 Uhr schrieb Max Lynch [email protected] :

Also, @adamdbradley https://github.com/adamdbradley , @perrygovier
https://github.com/perrygovier und @mhartington
https://github.com/mhartington vom Ionic Team haben gearbeitet
den ganzen Tag an einem Fix, der für Ionic und für einfache Angular-Apps funktioniert wie
Gut. Das Ziel ist ein Drop-In-Fix, der nicht geändert werden muss
Angular und wird (hoffentlich) mit den meisten 1.2+ Angular-Versionen funktionieren.

Hier ist unsere aktuelle gebündelte Lösung, die $browser dekoriert und repariert von
diesen Patch anwenden
https://github.com/angular/angular.js/issues/12241#issuecomment -130744518.
Hinweis: Dies basiert auf Angular 1.4.3 und ist eine Art "Klon mit Korrekturen" von
browser.js von Angular selbst:
https://github.com/driftyco/ionic/blob/ios9-patch/js/angular/service/decorators/ios9-browser-fix.js

Wir haben den Patch auch auf unserem CDN platziert. Ich empfehle nicht, die CDN-Datei zu verwenden
für die Produktion ist es nur da, damit es jetzt einfacher zu testen ist.

Bitte testen Sie es und lassen Sie uns wissen, wie es gelaufen ist, danke.

Um es auszuprobieren, platzieren Sie dieses Skript-Tag unter Ihrem eckigen oder
ionic.bundle.js-Datei: