Angular.js: Résumé infini sur le changement d'emplacement sur iOS 9 avec UIWebView (pas dans Safari/ WKWebView)

Créé le 30 juin 2015  ·  154Commentaires  ·  Source: angular/angular.js

Le code HTML simple suivant illustre le problème :

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

Cela fonctionne comme prévu sur la plupart des appareils, mais il lève une exception de résumé infinie sur iOS 9.
Je suis capable de reproduire sur iPad Air 2 et iPad 4ème génération avec iOS 9 beta 2.
Je me rends compte que c'est probablement un problème dans iOS, mais cela vaut peut-être encore la peine d'être étudié.

iOS $location bug

Commentaire le plus utile

Je suis allé un peu plus loin sur celui-ci. J'ai fait un changement dans Angular concernant l'emplacement.
Dans la partie "navigateur de mise à jour", j'ai changé le $rootScope.$evalAsync en $rootScope.$applyAsync.

Les deux méthodes semblent faire exactement la même chose. La différence ne devient évidente que lorsque vous regardez l'exécution réelle de $digest. Lorsque AngularJS exécute un condensé, il parcourt l'arborescence Scope et exécute les liaisons $watch() jusqu'à ce qu'aucune donnée sale ne soit produite. Au cours de ce cycle de vie, la file d'attente $applyAsync() et la file d'attente $evalAsync() sont vidées ; mais, cela se produit dans deux endroits très différents.

La file d'attente $applyAsync() n'est vidée qu'en haut de $digest avant qu'AngularJS ne commence à rechercher les données modifiées. En tant que telle, la file d'attente $applyAsync() sera vidée, au plus, une fois au cours d'un $digest et ne sera vidée que si la file d'attente était déjà remplie avant le début de $digest.

La file d'attente $evalAsync(), d'autre part, est vidée en haut de la boucle while qui implémente le "dirty check" à l'intérieur du $digest. Cela signifie que toute expression ajoutée à la file d'attente $evalAsync() lors d'un condensé sera exécutée ultérieurement dans le même condensé.

Pour rendre cette différence plus concrète, cela signifie que les expressions asynchrones ajoutées par $evalAsync() à partir d'une liaison $watch() s'exécuteront dans le même condensé. Les expressions asynchrones ajoutées par $applyAsync() à partir d'une liaison $watch() s'exécuteront à un moment ultérieur (~10ms).

J'espère que cela aidera déjà certains d'entre vous :-).


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

Tous les 154 commentaires

J'ai rencontré un problème similaire, qui s'est produit sur ios 9, mais fonctionne correctement sur d'autres appareils.

J'ai reproduit ce problème avec le même code fourni par santaslow sur 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>

Le code ci-dessus s'exécute normalement sur le navigateur de bureau, Android et ios 8 webview, mais sur ios 9, il lèvera une exception lorsque je cliquerai sur le lien :

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

Je ne peux plus reproduire dans iOS 9 Beta 3.

Je reçois la même erreur avec la bêta publique ios9 (13A4293g)

J'ai vérifié le code ci-dessus sur ios 9 beta 3 (13A4293g), plus aucune exception. Mais l'application utilisant ng-view lance toujours des exceptions infdig sur ios 9 beta 3.

Je reçois la même erreur avec la bêta publique ios9 (13A4293g)

Erreur : [$ rootScope:infdig ] 10 itérations $digest() ont été atteintes. Abandonner !
Observateurs tirés au cours des 5 dernières itérations : []
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 :///
dispatchEvent@[code natif]
triggerMouseEvent@file :///
tapClick@file :///
tapTouchEnd@file :///

Nous recevons également cette erreur sur la bêta publique 3 d'iOS 9 avec notre propre application Angular. Cela ne se produit pas dans iOS 8.

Pour contourner le problème, j'ai écrit une directive simple pour remplacer ng-view et angular-route.js. La solution a bien fonctionné dans notre propre application, toutes les exceptions infdig ont disparu sur ios 9 beta/beta 3. Ci-dessous se trouve le code simplifié, qui est juste pour notre propre application, les cas d'utilisation générale ne sont pas pris en compte. Je ne recommande pas à d'autres personnes d'utiliser ceci :

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

Je viens d'installer iOS 9 Beta 4 et j'ai toujours le même problème. Quelqu'un d'autre?

Je l'ai vu dans iOS 9 Beta 3 et je le vois toujours dans iOS 9 Beta 4.

+1

Oui, nous constatons également le même problème, même avec un routeur ui angulaire. Est-ce que quelqu'un a une solution valable autour de ce problème en attendant?

Voir le même problème dans uiWebView sur le dernier iOS9.

Quelqu'un a-t-il des mises à jour sur ce problème?

C'est toujours un problème. Réouverture

Cela ressemble à un problème ios. Est-ce que c'est suivi sur webkit quelque part ?

+1

+1

Pareil ici. Notre application Cordova fonctionne bien si elle s'exécute en tant que Web sur l'iPad Safari, mais le résumé infini se produit si elle s'exécute en tant qu'application Cordova (UIWebView).

Exactement les mêmes problèmes que @borrull ! Déjà expérimenté avec WKWebView et le problème n'existe pas. Mais nous ne pouvons pas utiliser WKWebView car nous avons besoin d'un service de fichiers local (et nous ne voulons pas exécuter un serveur local dans notre application) et de cookies. Il doit donc faire quelque chose avec UIWebView en combinaison avec Cordova/Mobile Safari sur iOS 9. Je débogue actuellement $locationWatch dans Angular car je vois que notre application veut passer plusieurs fois à un autre emplacement puis (après 10 fois ) l'erreur de résumé est levée.

même problème ici sur iOS9 béta4
Boucle $digest infinie angulaire ;(

+1, uniquement dans UIWebView, pas dans WKWebView mais nous ne pouvons utiliser UIWebView que dans notre application cordova.

+1

S'il s'agit d'un problème spécifique à iOS, veuillez ouvrir un problème sur l'outil de suivi des problèmes du kit Web et fournir une démo ! +1 ici ne changera probablement rien, car cela ressemble vraiment à un bogue de navigateur.

Quelqu'un a-t-il signalé ce bogue à Apple pour enquête ?

J'ai signalé ce bug à Apple. Mais peut-être devriez-vous tous faire de même pour attirer leur attention sur le bogue.

Pouvez-vous nous donner un lien pour que nous puissions +1 ?

C'est dans notre compte Apple personnel via le bug reporter .. donc pas de lien public ;(

Pourriez-vous le publier sur openradar et partager l'identifiant rdar afin que nous puissions le duper !

idem sur iOS9 béta 5 :
fonctionne sur safari mobile
fonctionne sur WKWebview que nous ne pouvons pas utiliser car il ne peut pas servir les fichiers locaux et ne prend pas en charge NSProtocol
fonctionne PAS sur UIWebView

idem ici sur IOS 9 Beta 5

J'ai également signalé un bug à Apple. Le lien Open Radar est : https://openradar.appspot.com/22186109 (Cela devrait aider les paresseux à signaler un bogue). Veuillez laisser des commentaires si vous pouvez améliorer la formulation/l'explication du bogue ;-) Vous pouvez télécharger le projet Xcode à joindre avec le dossier de bogue sur le ticket Open Radar (Merci à @santaslow pour le JS dans l'OP)

J'ai créé une version du même projet Xcode (de @borrull) mais avec ui-router au lieu de ng-route. Exactement le même problème. Pour les personnes intéressées, vous pouvez retrouver le projet ici : http://s000.tinyupload.com/index.php?file_id=87281871603760127355

Nous voyons également ce problème. J'ai pu résoudre le problème en ce sens que les propriétés location.* ne sont pas mises à jour immédiatement lorsque angulaire est dans le mélange. Si vous essayez d'attribuer une valeur à location.hash (comme ce qui est fait derrière le service de localisation), puis relisez-la immédiatement, la valeur n'a pas changé. Il semble y avoir des effets secondaires résultant des gestionnaires jqlite attachés aux événements popstate et hashchanged.

J'essaierai de télécharger un échantillon lorsque je serai sur un ordinateur.

+1

@CleverCoder Des mises à jour sur l'échantillon ?

Je devrai finir le cas de repro dans la matinée, car le code car j'ai été ligoté tout le week-end. Merci pour le coup de pouce ! Alors que iOS 9 compte à rebours, nous avons tout intérêt à ce que cela soit résolu. Je téléchargerai quelque chose dès que possible.

+1

J'ai reproduit ce que je pense être la cause première, où la définition des propriétés de hachage ou href d'emplacement ne "s'applique" pas immédiatement.
Voici un lien vers le projet XCode :
https://www.dropbox.com/s/2jkwv2thhm86nly/iOS%209%20Location%20Bug.zip?dl=0
Faites-moi savoir si vous ne pouvez pas accéder au fichier.

Observez la valeur résultante dans location.hash et attachez Safari au débogage. Il semble y avoir quelque chose qui retarde le changement à la suite d'une certaine plomberie d'événements basée sur les événements « popstate » et « hashchange ».

J'espère que ceci est utile.

  • Sean

Nous utilisons window.location.href au lieu d'utiliser state.go et cela semble fonctionner pour le moment. Moins de buggy.

La valeur de location.hash sera correcte après un tour de la boucle d'exécution. Angular peut facilement contourner ce problème en retardant l'obtention de location.hash dans un setTimeout(..., 0). Je pense que ce serait ~ 2 changements à angular.js/src/ng/location.js.

@hober Essayé le délai d'attente avec angulaire comme ceci:

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

et

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

J'ai donc ajouté un setTimout autour de la méthode setBrowserUrlWithFallback mais cela ne résout pas le problème.

Voici un cas de test réduit qui ne repose pas sur Angular et qui illustre la solution de contournement. Comment implémenter réellement la solution de contournement dans Angular n'est pas clair pour moi. https://gist.github.com/hober/a29b6c28ac1744c800dd

Je suis allé un peu plus loin sur celui-ci. J'ai fait un changement dans Angular concernant l'emplacement.
Dans la partie "navigateur de mise à jour", j'ai changé le $rootScope.$evalAsync en $rootScope.$applyAsync.

Les deux méthodes semblent faire exactement la même chose. La différence ne devient évidente que lorsque vous regardez l'exécution réelle de $digest. Lorsque AngularJS exécute un condensé, il parcourt l'arborescence Scope et exécute les liaisons $watch() jusqu'à ce qu'aucune donnée sale ne soit produite. Au cours de ce cycle de vie, la file d'attente $applyAsync() et la file d'attente $evalAsync() sont vidées ; mais, cela se produit dans deux endroits très différents.

La file d'attente $applyAsync() n'est vidée qu'en haut de $digest avant qu'AngularJS ne commence à rechercher les données modifiées. En tant que telle, la file d'attente $applyAsync() sera vidée, au plus, une fois au cours d'un $digest et ne sera vidée que si la file d'attente était déjà remplie avant le début de $digest.

La file d'attente $evalAsync(), d'autre part, est vidée en haut de la boucle while qui implémente le "dirty check" à l'intérieur du $digest. Cela signifie que toute expression ajoutée à la file d'attente $evalAsync() lors d'un condensé sera exécutée ultérieurement dans le même condensé.

Pour rendre cette différence plus concrète, cela signifie que les expressions asynchrones ajoutées par $evalAsync() à partir d'une liaison $watch() s'exécuteront dans le même condensé. Les expressions asynchrones ajoutées par $applyAsync() à partir d'une liaison $watch() s'exécuteront à un moment ultérieur (~10ms).

J'espère que cela aidera déjà certains d'entre vous :-).


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

Voici une autre approche. Je ne connais pas très bien la base de code Angular, mais la logique semble rationnelle. La fonction url(...) du navigateur dépend actuellement de location.href renvoyant immédiatement l'URL correcte. Étant donné que cette méthode est appelée dans la même boucle d'exécution, dans le cycle de stabilisation $digest, elle continue d'obtenir l'ancienne URL. Ce correctif exploite un 'pendingHref' pour suivre l'affectation, renvoyant cette valeur à la place, si elle est définie. Une fois la valeur alignée avec location.href, la valeur en attente est effacée. Pendant un ensemble de l'url, une minuterie est définie avec 0ms pour attraper le cas où une url() get n'est pas appelée. Ce n'est pas parfait, mais la logique semble fonctionner. Il s'agit principalement d'envisager une approche alternative qui ne crée pas de retards de performance. Ceci est basé sur la balise 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;
     }
   };

Merci @CleverCoder pour la solution ! Semble fonctionner comme un charme! :+1:

@CleverCoder
Ce serait formidable si vous faisiez une demande de tirage avec cela à l'équipe angulaire.

@viattik Je serais en quelque sorte surpris si l'équipe angulaire adoptait une solution de contournement pour UIWebView sur iOS9, car le bogue se trouve dans UIWebView (Apple) lui-même. Mais tu peux toujours essayer...

@raftheunis87
Il existe de nombreux bogues dans différents navigateurs et de nombreuses solutions de contournement pour ces bogues dans le code angulaire.
Bien qu'elles ne prennent pas officiellement en charge UIWebView, de nombreuses applications hybrides seront cassées et l'utilisation d'angular sera impossible dans les applications hybrides dans le dernier iOS jusqu'à ce qu'Apple corrige ce bogue. Et c'est un gros problème je dirais.
Alors je ferais un essai.

@viattik Je suis tout à fait d'accord. Et d'ailleurs : Apple nous a fait savoir qu'il était peu probable qu'ils corrigent le bogue UIWebView. Alors en effet : essayez-le ;-)

Les amis, si vous pouviez publier ceci en tant que bug du kit Web, idéalement avec un cas de repro, je ferais un suivi avec certains de nos contacts du côté WebKit. https://bugs.webkit.org/

@naomiblack
Je ne sais pas si c'est un bug du webkit. Parce que cela ne se produit que dans UIWebView sur iOS9. Safari sur iOS9 fonctionne très bien.

@ raftheunis87 merci pour vos suggestions de code, a parfaitement fonctionné

@raftheunis87 @CleverCoder est un moyen de travailler avec ionic-angular ? Peux-tu être plus précis?

@abrahamrkj Je n'ai aucune expérience avec ionic. Mais leurs personnalisations sont-elles angulaires lors de l'utilisation d'ionic ? Sinon, je dirais que le même correctif fonctionnerait avec ionic-angular...

@ raftheunis87 https://github.com/driftyco/ionic/tree/master/js c'est l'angle qu'ils utilisent.

@CleverCoder +1 pour la demande de tirage. Je suis d'accord avec @viattik pour

Une pull request peut être dans un avenir proche, bien que j'hésite car je ne suis pas aussi proche de la base de code Angular que les autres. Je reviendrai bientôt sur la solution et essaierai de la rendre à l'épreuve des balles. Il semble étrange qu'une propriété de l'objet window.location ne change pas « immédiatement ». Dans les tests que j'ai effectués, j'ai remarqué que le changement persistait tant que les crochets d'événement « popstate » et « hashchange » n'étaient pas en place, ce qui m'amène à penser que la cause du changement différé peut en fait être faire quelque chose d'autre .. Peut-être que le moment de ces événements a changé (je pense que c'est ce que j'observais).
Je vais regarder cela au cours des prochains jours, et si rien de mieux ne fait surface, je creuserai un peu plus pour confirmer ce que je sais jusqu'à présent, et qu'il n'y a pas de meilleur endroit pour aborder le changement de comportement. Désolé si cela prête à confusion. Il se passe beaucoup de choses sous le capot que je ne comprends toujours pas complètement en ce qui concerne ces événements.
Acclamations!

... et oui, @borrull , je suis d'accord. Si Apple n'apporte plus de changements, alors il s'agit d'une bombe à retardement sérieuse qui conduira vraiment à une mauvaise presse et à un pointage du doigt. Je ne suis pas un fan des minuteries comme solution de contournement (je préférerais avoir un flux logique amélioré autour de ces propriétés intégrées), mais si nous ne pouvons pas dépendre de la valeur qui change une fois qu'elle est définie, alors où dessinons-nous le ligne? À quelles autres propriétés ne pouvons-nous pas faire confiance ? C'est étrange.

@CleverCoder Je voulais juste dire merci, votre patch a vraiment sauvé la journée !

@CleverCoder Merci pour la solution de contournement donnée.

J'ai dérivé une solution qui utilise la fonction de décorateur d'angular et vient sans patcher la source angulaire.

Avec cssua, cette configuration peut être configurée pour être utilisée uniquement dans des environnements spécifiques.

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

@jd-carroll : C'est vraiment intéressant. J'y reviendrai peut-être un peu plus tard dans la journée, quand j'aurai le temps. Assez inondé de trucs. Cela crée juste plus de mystère, car cela semble être un problème distinct qui ne devrait pas introduire de retard dans la mise à jour des valeurs de location.*.

@realityfilter : C'est marrant que tu mentionnes le décorateur... Je viens juste de finir d'implémenter quelque chose en utilisant la fonctionnalité du décorateur angulaire. Agréable!

Salut à tous,
Je voulais juste ajouter que ce correctif a introduit un bogue dans notre code qui était facile à corriger.

Nos modèles avaient des balises d'ancrage qui utilisaient href="#" et ng-click="someCall()". Le href provoquait le passage du site à index.html à l'aide de ce correctif. La suppression du href a résolu le problème.

Notre application se brise pendant la navigation du bouton de retour dans Ionic, elle passe d'abord à la nouvelle vue, puis revient partiellement à l'ancienne vue, puis revient à la nouvelle vue sur IOS9 bêta toutes les résolutions pour Ionic

Même problème avec iOS 9 beta 5 13A4325c, Angular 1.4.0 (avec cordova-ios 3.9.1). Espérons que le bogue UIWebView sous-jacent sera corrigé !

Même problème sur Angular v1.2.27

Je l'ai retracé jusqu'à la version 1.2.27, le bogue n'est pas dans la version 1.2.26 précédente.

Plus précisément, ce commit est le coupable.

@damrbaby On dirait que je pourrais avoir quelque chose à voir avec ça.

Mais le fait qu'il fonctionne avec la dernière version d'angular dans Mobile Safari clarifie qu'il doit faire quelque chose avec uiWebView sur iOS 9. Ainsi, le changement qu'ils ont apporté au code source d'Angular n'est pas nécessairement une mauvaise chose.

@CleverCoder @realityfilter @jyc66 Je voulais juste te dire merci, tu viens de me sauver la journée.

Le problème est toujours présent sur iOS9 GM Seed, alors mettez vos applications à jour !

Je peux confirmer que le problème est toujours présent dans iOS9 GM (13A340)

Cela signifie donc qu'Apple a cassé quelque chose et que nous devons à nouveau mettre à jour nos applications (dont certaines n'ont pas changé depuis des mois, voire plus d'un an) pour les empêcher de planter. Cela a du sens :( . Je préférerais qu'Apple le répare pour les lancements d'iOS9. Passer à la dernière version angulaire dans une ancienne application risque de casser d'autres choses également.

Je doute fortement qu'Angular soit le seul framework à avoir des problèmes avec iOS9 ?

Ainsi, @adamdbradley , @perrygovier et @mhartington de l'équipe Ionic ont travaillé toute la journée sur un correctif qui fonctionnera également pour Ionic et pour les applications angulaires simples. L'objectif est d'être un correctif instantané qui ne nécessite pas de modification d'Angular et qui fonctionnera (espérons-le) sur la plupart des versions 1.2+ d'Angular.

Voici notre solution groupée actuelle qui décore et corrige $browser en appliquant ce correctif . Remarque: ceci est basé sur Angular 1.4.3 et est en quelque sorte un "clone avec correctifs" de browser.js d'Angular proprement dit : https://github.com/driftyco/ionic/blob/ios9-patch/js /angular/service/decorators/ios9-browser-fix.js

Nous avons également placé le patch sur notre CDN. Je ne recommande pas d'utiliser le fichier CDN pour la production, il est uniquement là, il est donc plus facile de tester avec maintenant.

Pour le tester, placez cette balise de script sous votre fichier angulaire ou ionic.bundle.js :

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

De plus, il applique actuellement le correctif, que vous utilisiez ou non iOS9. Cela sera bientôt corrigé de sorte qu'il ne fonctionne que sur iOS 9 UIWebView.

Suivez le numéro Ionic correspondant ici : https://github.com/driftyco/ionic/issues/4082#issuecomment -139079725

Salut tout le monde,

UI-sref fonctionne comme un charme mais $state.go casse l'animation du bouton de retour
et la page scintille beaucoup même après avoir appliqué ce correctif.

Salutations,
Ajay Singh

Le jeudi 10 septembre 2015 à 6h23, Max Lynch [email protected] a écrit :

Alors, @adamdbradley https://github.com/adamdbradley , @perrygovier
https://github.com/perrygovier et @mhartington
https://github.com/mhartington de l'équipe Ionic a travaillé
toute la journée sur un correctif qui fonctionnera pour Ionic et pour les applications angulaires simples comme
bien. L'objectif est d'être un correctif instantané qui ne nécessite pas de modification
Angular, et fonctionnera (espérons-le) sur la plupart des versions 1.2+ Angular.

Voici notre solution groupée actuelle qui décore et corrige $browser en
appliquer ce patch
https://github.com/angular/angular.js/issues/12241#issuecomment -130744518.
Remarque: ceci est basé sur Angular 1.4.3 et est en quelque sorte un "clone avec correctifs" de
browser.js à partir d'Angular proprement dit :
https://github.com/driftyco/ionic/blob/ios9-patch/js/angular/service/decorators/ios9-browser-fix.js

Nous avons également placé le patch sur notre CDN. Je ne recommande pas d'utiliser le fichier CDN
pour la production, c'est seulement là, donc c'est plus facile à tester pour le moment.

S'il vous plaît, testez-le et dites-nous comment cela se passe, merci.

Pour le tester, placez cette balise de script sous votre angle ou
fichier ionic.bundle.js :