Angular.js: Resumo infinito sobre mudança de localização no iOS 9 com UIWebView (não no Safari / WKWebView)

Criado em 30 jun. 2015  ·  154Comentários  ·  Fonte: angular/angular.js

O seguinte HTML simples demonstra o 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>

Isso funciona como esperado na maioria dos dispositivos, mas lança uma exceção de resumo infinito no iOS 9.
Consigo reproduzir no iPad Air 2 e no iPad de 4ª geração com iOS 9 beta 2.
Sei que provavelmente é um problema no iOS, mas ainda pode valer a pena investigar.

iOS $location bug

Comentários muito úteis

Aproximei-me um pouco mais deste. Fiz uma alteração no Angular em relação ao local.
Na parte "atualizar navegador", alterei $ rootScope. $ EvalAsync para $ rootScope. $ ApplyAsync.

Os dois métodos parecem fazer exatamente a mesma coisa. A diferença não se torna evidente até que você observe a execução real de $ digest. Quando o AngularJS executa um resumo, ele percorre a árvore do Escopo e executa ligações $ watch () até que nenhum dado mais sujo seja produzido. Durante esse ciclo de vida, a fila $ applyAsync () e a fila $ evalAsync () são liberadas; mas, isso acontece em dois lugares muito diferentes.

A fila $ applyAsync () só é liberada no início do $ digest antes que o AngularJS comece a verificar os dados sujos. Assim, a fila $ applyAsync () será liberada, no máximo, uma vez durante um $ digest e só será liberada se a fila já tiver sido preenchida antes do $ digest começar.

A fila $ evalAsync (), por outro lado, é liberada no topo do loop while que implementa a "verificação suja" dentro do $ digest. Isso significa que qualquer expressão adicionada à fila $ evalAsync () durante um resumo será executada posteriormente no mesmo resumo.

Para tornar essa diferença mais concreta, isso significa que as expressões assíncronas adicionadas por $ evalAsync () de dentro de uma ligação $ watch () serão executadas no mesmo resumo. Expressões assíncronas adicionadas por $ applyAsync () de dentro de uma associação $ watch () serão executadas em um momento posterior (~ 10 ms).

Espero que isso já ajude alguns de vocês :-).


// 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 comentários

Eu encontrei um problema semelhante, que aconteceu no ios 9, mas funcionou bem em outros dispositivos.

Reproduzi esse problema com o mesmo código fornecido por santaslow em 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>

O código acima é executado normalmente no navegador de desktop, android e ios 8 webview, mas no ios 9 ele lançará uma exceção quando eu clicar no link:

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

Não consigo mais reproduzir no iOS 9 Beta 3.

Recebo o mesmo erro com iOS9 público beta (13A4293g)

Eu verifiquei o código acima no ios 9 beta 3 (13A4293g), sem exceção mais. Mas o aplicativo com o uso do ng-view ainda lança exceções infdig no ios 9 beta 3.

Recebo o mesmo erro com iOS9 público beta (13A4293g)

Erro: [$ rootScope: infdig ] 10 $ digest () iterações alcançadas. Abortando!
Observadores acionados nas últimas 5 iterações: []
http://errors.angularjs.org/1.3.13/ $ rootScope / infdig? p0 = 10 & p1 =% 5B% 5D
arquivo: ///Users/mac5/Library/Developer/CoreSimulator/Devices/749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE/data/Containers/Bundle/Application/9B5EE368-F2A0-4C99-807B-8wops /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-8wops /lib/ionic/js/ionic.bundle.js:54879:24
eventHandler @ file : ///Users/mac5/Library/Developer/CoreSimulator/Devices/749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE/data/Containers/Bundle/Application/9B5EE368-F2A0-4Containers/Bundle/Application/9B5EE368-F2A0-4C579-807ops.17A7B5EE368-F2A0-4C579-8h7-807ops /www/lib/ionic/js/ionic.bundle.js:11713:25
dispatchEvent @ [código nativo]
triggerMouseEvent @ file : ///Users/mac5/Library/Developer/CoreSimulator/Devices/749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE/data/Containers/Bundle/Application/9B5EE368-F2A0-9-4C4C9B5EE368-F2A079-4C /www/lib/ionic/js/ionic.bundle.js:2863:20
tapClick @ file : ///Users/mac5/Library/Developer/CoreSimulator/Devices/749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE/data/Containers/Bundle/Application/9B5EE368-F2A0-4CE5B-80h7B7ops /www/lib/ionic/js/ionic.bundle.js:2852:20
tapTouchEnd @ file : ///Users/mac5/Library/Developer/CoreSimulator/Devices/749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE/data/Containers/Bundle/Application/9B5EE368-F2A0BE5-4C8998-F2A1FC-E3D54A1CCEEE/data/Containers/Bundle/Application/9B5EE368-F2A0BE579-99app780B780B7ops. /www/lib/ionic/js/ionic.bundle.js:2975:13

Também estamos recebendo esse erro no Beta 3 público do iOS 9 com nosso próprio aplicativo Angular. Isso não ocorre no iOS 8.

Como solução alternativa, escrevi uma diretiva simples para substituir ng-view e angular-route.js. A solução funcionou bem em nosso próprio aplicativo, todas as exceções infdig desapareceram no ios 9 beta / beta 3. Abaixo está o código simplificado, que é apenas para nosso próprio aplicativo, casos de uso gerais não são considerados. NÃO recomendo que outras pessoas usem isto:

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

Acabei de instalar o iOS 9 Beta 4 e ainda tenho o mesmo problema. Alguém mais?

Eu vi no iOS 9 Beta 3 e ainda estou vendo no iOS 9 Beta 4.

+1

Sim, estamos vendo o mesmo problema, mesmo com o roteador ui angular. Alguém tem uma solução alternativa válida para esse problema nesse meio tempo?

Observando o mesmo problema no uiWebView no iOS9 mais recente.

Alguém tem atualizações sobre esse problema?

Isso ainda é um problema. Reabertura

Parece um problema de iOS. Isso é rastreado no webkit em algum lugar?

+1

+1

Mesmo aqui. Nosso aplicativo Cordova funciona bem se for executado como web no iPad Safari, mas o resumo infinito acontece se for executado como um aplicativo Cordova (UIWebView).

Exatamente os mesmos problemas que @borrull ! Já experimentei com WKWebView e então o problema não existe. Mas não podemos usar WKWebView, pois precisamos do Local File Serving (e não queremos executar um servidor local em nosso aplicativo) e cookies. Portanto, ele tem que fazer algo com UIWebView em combinação com Cordova / Mobile Safari no iOS 9. No momento, estou depurando o $ locationWatch no Angular porque vejo que nosso aplicativo deseja fazer a transição para um local diferente várias vezes e então (após 10 vezes ) o erro de resumo é lançado.

mesmo problema aqui no iOS9 béta4
Loop infinito angular $ digest; (

+1, apenas em UIWebView, não em WKWebView, mas só podemos usar UIWebView em nosso aplicativo Cordova.

+1

Se este for um problema específico do iOS, abra um problema no rastreador de problemas do webkit e forneça uma demonstração! +1 aqui provavelmente não mudará nada, pois realmente parece um bug do navegador.

Alguém relatou esse bug à Apple para investigação?

Eu relatei esse bug para a apple. Mas talvez todos vocês devam fazer o mesmo para chamar a atenção deles para o bug.

Você pode nos dar um link para que possamos marcar com +1?

Ele está em nossa conta pessoal da apple por meio do relator de bug ... portanto, nenhum link público; (

Você poderia postá-lo no openradar e compartilhar o id do rdar para que possamos enganá-lo!

mesmo no iOS9 béta 5:
funciona em safari móvel
funciona em WKWebview que não podemos usar porque não pode servir arquivos locais e não suporta NSProtocol
NÃO funciona em UIWebView

mesmo aqui no IOS 9 Beta 5

Também registrei um bug na Apple. O link do Open Radar é: https://openradar.appspot.com/22186109 (Isso deve ajudar os preguiçosos a registrar um bug). Por favor, deixe comentários se você pode melhorar o texto / explicação do bug ;-) Você pode baixar o projeto Xcode para anexar com o arquivamento de bug no tíquete do Open Radar (Obrigado a @santaslow pelo JS no OP)

Eu criei uma versão do mesmo projeto Xcode (de @borrull), mas com ui-router em vez de ng-route. Exatamente o mesmo problema. Para os interessados, você pode encontrar o projeto aqui: http://s000.tinyupload.com/index.php?file_id=87281871603760127355

Também estamos vendo esse problema. Consegui identificar o problema de que as propriedades location. * Não são atualizadas imediatamente quando o angular está no mix. Se você tentar atribuir um valor a location.hash (como o que está sendo feito por trás do serviço de localização), então leia imediatamente, o valor não mudou. Parece haver alguns efeitos colaterais que ocorrem como resultado dos manipuladores jqlite anexados a eventos popstate e hashchanged.

Vou tentar fazer o upload de uma amostra quando estiver em um computador.

+1

@CleverCoder Alguma atualização na amostra?

Terei que terminar o caso de reprodução pela manhã, conforme o código, pois estive amarrado o fim de semana todo. Obrigado pelo cutucão! Na contagem regressiva do iOS 9, temos grande interesse em ver isso resolvido. Vou fazer o upload de algo assim que puder.

+1

Eu reproduzi o que acredito ser a causa raiz, onde definir as propriedades hash ou href do local não "se aplicam" imediatamente.
Aqui está um link para o projeto XCode:
https://www.dropbox.com/s/2jkwv2thhm86nly/iOS%209%20Location%20Bug.zip?dl=0
Avise-me se você não conseguir acessar o arquivo.

Observe o valor resultante em location.hash, bem como anexe o Safari para depuração. Parece haver algo adiando a mudança como resultado de algum encanamento de evento com base nos eventos 'popstate' e 'hashchange'.

Espero que isto seja útil.

  • Sean

Estamos usando window.location.href em vez de state.go e parece estar funcionando por enquanto. Menos bugado.

O valor de location.hash estará correto após um giro do runloop. O Angular pode facilmente contornar esse problema atrasando o get location.hash em um setTimeout (..., 0). Acho que seriam cerca de 2 mudanças para angular.js / src / ng / location.js.

@hober tentou o tempo limite com angular assim:

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

e

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

Portanto, adicionei um setTimout ao método setBrowserUrlWithFallback, mas isso não resolve o problema.

Aqui está um caso de teste reduzido que não depende do Angular e que demonstra a solução alternativa. Como realmente implementar a solução alternativa no Angular não está claro para mim. https://gist.github.com/hober/a29b6c28ac1744c800dd

Aproximei-me um pouco mais deste. Fiz uma alteração no Angular em relação ao local.
Na parte "atualizar navegador", alterei $ rootScope. $ EvalAsync para $ rootScope. $ ApplyAsync.

Os dois métodos parecem fazer exatamente a mesma coisa. A diferença não se torna evidente até que você observe a execução real de $ digest. Quando o AngularJS executa um resumo, ele percorre a árvore do Escopo e executa ligações $ watch () até que nenhum dado mais sujo seja produzido. Durante esse ciclo de vida, a fila $ applyAsync () e a fila $ evalAsync () são liberadas; mas, isso acontece em dois lugares muito diferentes.

A fila $ applyAsync () só é liberada no início do $ digest antes que o AngularJS comece a verificar os dados sujos. Assim, a fila $ applyAsync () será liberada, no máximo, uma vez durante um $ digest e só será liberada se a fila já tiver sido preenchida antes do $ digest começar.

A fila $ evalAsync (), por outro lado, é liberada no topo do loop while que implementa a "verificação suja" dentro do $ digest. Isso significa que qualquer expressão adicionada à fila $ evalAsync () durante um resumo será executada posteriormente no mesmo resumo.

Para tornar essa diferença mais concreta, isso significa que as expressões assíncronas adicionadas por $ evalAsync () de dentro de uma ligação $ watch () serão executadas no mesmo resumo. Expressões assíncronas adicionadas por $ applyAsync () de dentro de uma associação $ watch () serão executadas em um momento posterior (~ 10 ms).

Espero que isso já ajude alguns de vocês :-).


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

Aqui está outra abordagem. Não estou familiarizado com a base de código Angular, mas a lógica parece racional. A função url do navegador (...) atualmente depende de location.href retornar imediatamente o URL correto. Como esse método é chamado no mesmo loop de execução, dentro do ciclo de estabilização $ digest, ele continua obtendo a URL antiga. Este patch utiliza um 'pendingHref' para rastrear a atribuição, retornando esse valor, se estiver definido. Uma vez que o valor esteja alinhado com location.href, o valor pendente é apagado. Durante uma configuração do url, um cronômetro é definido com 0 ms para detectar o caso em que um url () get não é chamado. Não é perfeito, mas a lógica parece funcionar. Isso é principalmente para considerar uma abordagem alternativa que não crie atrasos no desempenho. Isso é baseado na tag 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;
     }
   };

Obrigado @CleverCoder pela solução! Parece funcionar como um encanto! : +1:

@CleverCoder
seria ótimo se você fizesse uma solicitação de pull com isso para a equipe angular.

@viattik Eu meio que me surpreenderia se a equipe angular adotasse uma solução alternativa para o UIWebView no iOS9, já que o bug está no próprio UIWebView (Apple). Mas você sempre pode tentar ...

@ raftheunis87
Existem muitos bugs em navegadores diferentes e muitas soluções alternativas para esses bugs no código angular.
Embora não sejam oficialmente compatíveis com UIWebView, muitos aplicativos híbridos serão quebrados e o uso do angular será impossível em aplicativos híbridos no iOS mais recente até que a Apple conserte esse bug. E esse é um grande problema, eu diria.
Então, eu tentaria.

@viattik concordo totalmente. E a propósito: a Apple nos comunicou que é improvável que consertem o bug do UIWebView. Então, de fato: experimente ;-)

Pessoal, se vocês pudessem postar isso como um bug do webkit, de preferência com um caso de reprodução, farei o acompanhamento com alguns de nossos contatos do lado do WebKit. https://bugs.webkit.org/

@naomiblack
Não tenho certeza se é um bug do webkit. Porque isso só acontece em UIWebView no iOS9. Safari no iOS9 funciona bem.

@ raftheunis87 obrigado por suas sugestões de código, funcionou perfeitamente

@ raftheunis87 @CleverCoder é uma forma de trabalhar com angular iônico? você pode ser mais específico?

@abrahamrkj Não tenho nenhuma experiência com iônico. Mas suas personalizações são angulares ao usar iônicos? Caso contrário, eu diria que a mesma correção funcionaria com angular iônico ...

@ raftheunis87 https://github.com/driftyco/ionic/tree/master/js este é o angular que eles estão usando.

@CleverCoder +1 para a solicitação de pull. Eu concordo com @viattik que essa é uma questão importante, pois vai quebrar muitos aplicativos híbridos.

Uma solicitação de pull pode estar em um futuro próximo, embora eu hesite, pois não estou tão perto da base de código Angular quanto os outros. Vou revisitar a solução em breve e tentar torná-la à prova de balas. Parece estranho que uma propriedade no objeto window.location não mude 'imediatamente'. Nos testes que fiz, percebi que a mudança permaneceu contanto que os ganchos de evento 'popstate' e 'hashchange' não estivessem no lugar, o que me levou a pensar que a causa da mudança adiada pode realmente ser fazer algo mais ... Talvez o tempo desses eventos tenha mudado (acho que é isso que eu estava observando).
Vou assistir a isso nos próximos dias e, se nada melhor aparecer, vou cavar um pouco mais fundo para confirmar o que sei até agora e que não há um lugar melhor para lidar com a mudança de comportamento. Desculpe se isso é confuso. Há muita coisa acontecendo sob o capô que eu ainda não entendo completamente com relação a esses eventos.
Saúde!

... e sim, @borrull , concordo. Se a Apple não fizer mais alterações, então esta é uma bomba-relógio séria que realmente levará a uma má publicidade e acusações. Eu não sou um fã dos temporizadores como uma solução alternativa (eu preferia ter melhorado o fluxo lógico em torno dessas propriedades integradas), mas se não podemos depender da mudança de valor depois de definido, então onde desenhamos o linha? Em que outras propriedades não podemos confiar? É estranho.

@CleverCoder Só queria agradecer, seu patch realmente salvou o dia!

@CleverCoder Obrigado pela solução alternativa.

Eu deduzi uma solução que usa o recurso de decorador angular e vem sem corrigir a origem angular.

Com o cssua, esta configuração pode ser configurada para ser usada apenas em ambientes 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 Veja # 12635

@ jd-carroll: Isso é muito interessante. Posso revisitar isso um pouco mais tarde hoje, quando tiver tempo. Muito inundado com coisas. Isso só cria mais mistério, já que parece ser um problema separado que não deve apresentar um atraso na atualização dos valores de location. *.

@realityfilter : Engraçado você mencionar o decorador ... Acabei de implementar algo usando a funcionalidade do decorador Angular. Legal!

Olá a todos,
Só queria acrescentar que essa correção introduziu um bug em nosso código que era fácil de corrigir.

Nossos modelos tinham tags âncora que usavam href = "#" e ng-click = "someCall ()". O href estava fazendo com que o site fosse para index.html usando esta correção. Remover o href corrigiu o problema.

Nosso aplicativo está quebrando durante a navegação com o botão Voltar no Ionic, ele primeiro vai para uma nova visão, depois volta para a visão antiga parcialmente e depois volta novamente para a nova visão no IOS9 beta quaisquer resoluções para Ionic

Mesmo problema com iOS 9 beta 5 13A4325c, Angular 1.4.0 (com cordova-ios 3.9.1). Esperamos que o bug UIWebView subjacente seja corrigido!

O mesmo problema ocorre no Angular v1.2.27

Eu rastreei até a versão 1.2.27, o bug não está na versão 1.2.26 anterior.

Especificamente, esse commit é o culpado.

@damrbaby Parece que posso ter algo a ver com isso.

Mas o fato de estar funcionando com a versão mais recente do angular no Mobile Safari esclarece que tem que fazer algo com o uiWebView no iOS 9. Portanto, a mudança que eles fizeram no código-fonte do Angular não é necessariamente uma coisa ruim.

@CleverCoder @realityfilter @ jyc66 Eu só queria dizer obrigado, você acabou de salvar meu dia.

O problema ainda está presente no iOS9 GM Seed, então atualizem seus aplicativos, pessoal!

Posso confirmar que o problema ainda está presente no iOS9 GM (13A340)

Então, isso significa que a Apple quebrou algo e temos que atualizar nossos aplicativos novamente (alguns dos quais não mudam há meses ou até mesmo mais de um ano) para evitar que travem. Faz sentido :(. Prefiro que a Apple o conserte para os lançamentos do iOS9. Ir para a versão Angular mais recente em um aplicativo antigo pode quebrar algumas outras coisas também.

Eu duvido que o Angular seja o único framework que está tendo problemas com o iOS9.

Portanto, @adamdbradley , @perrygovier e @mhartington da equipe Ionic têm trabalhado o dia todo em uma correção que funcionará para Ionic e também para aplicativos Angular simples. O objetivo é ser uma correção temporária que não exija a modificação do Angular e funcione (com sorte) na maioria das versões 1.2+ do Angular.

Aqui está nossa solução empacotada atual que decora e corrige $browser aplicando este patch . Observação: isso é baseado no Angular 1.4.3 e é uma espécie de "clone com correções" de browser.js do Angular adequado: https://github.com/driftyco/ionic/blob/ios9-patch/js /angular/service/decorators/ios9-browser-fix.js

Também colocamos o patch em nosso CDN. Não recomendo usar o arquivo CDN para produção, ele está lá apenas para ser mais fácil de testar agora.

Para testá-lo, coloque esta tag de script abaixo do arquivo angular ou ionic.bundle.js:

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

Além disso, agora ele aplica o patch, esteja você executando no iOS9 ou não. Em breve, isso será corrigido de forma que ele funcione apenas no iOS 9 UIWebView.

Siga o problema Ionic correspondente aqui: https://github.com/driftyco/ionic/issues/4082#issuecomment -139079725

Olá a todos,

UI-sref funciona perfeitamente, mas $ state.go quebra a animação do botão Voltar
e a página pisca muito mesmo depois de aplicar essa correção.

Cumprimentos,
Ajay Singh

Na quinta-feira, 10 de setembro de 2015 às 6h23, Max Lynch [email protected] escreveu:

Então, @adamdbradley https://github.com/adamdbradley , @perrygovier
https://github.com/perrygovier e @mhartington
https://github.com/mhartington da equipe Ionic tem trabalhado
o dia todo em uma correção que funcionará para Ionic e para aplicativos Angular simples como
Nós vamos. O objetivo é ser uma correção imediata que não exija modificação
Angular, e funcionará (com sorte) na maioria das versões 1.2+ do Angular.

Aqui está nossa solução empacotada atual que decora e corrige $ navegador por
aplicando este patch
https://github.com/angular/angular.js/issues/12241#issuecomment -130744518.
Nota: isso é baseado no Angular 1.4.3 e é uma espécie de "clone com correções" de
browser.js do Angular apropriado:
https://github.com/driftyco/ionic/blob/ios9-patch/js/angular/service/decorators/ios9-browser-fix.js

Também colocamos o patch em nosso CDN. Eu não recomendo usar o arquivo CDN
para produção, está lá apenas para ser mais fácil de testar agora.

Faça um teste e diga-nos como foi, obrigado.

Para testá-lo, coloque esta tag de script abaixo de seu angular ou
Arquivo ionic.bundle.js: