Angular.js: Бесконечный дайджест об изменении местоположения в iOS 9 с UIWebView (не в Safari / WKWebView)

Созданный на 30 июн. 2015  ·  154Комментарии  ·  Источник: angular/angular.js

Следующий простой HTML демонстрирует проблему:

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

Это работает должным образом на большинстве устройств, но вызывает исключение бесконечного дайджеста в iOS 9.
Я могу воспроизводить как на iPad Air 2, так и на iPad 4-го поколения с iOS 9 beta 2.
Я понимаю, что это, вероятно, проблема в iOS, но, возможно, стоит изучить ее.

iOS $location bug

Самый полезный комментарий

Получил немного больше об этом. Я внес изменения в Angular относительно местоположения.
В части «обновление браузера» я изменил $ rootScope. $ EvalAsync на $ rootScope. $ ApplyAsync.

Похоже, что оба метода делают одно и то же. Разница не станет очевидной, пока вы не посмотрите на фактическое выполнение $ digest. Когда AngularJS выполняет дайджест, он просматривает дерево Scope и выполняет привязки $ watch () до тех пор, пока не перестанут генерироваться грязные данные. В течение этого жизненного цикла сбрасываются и очередь $ applyAsync (), и очередь $ evalAsync (); но это происходит в двух очень разных местах.

Очередь $ applyAsync () очищается только в верхней части дайджеста $, прежде чем AngularJS начнет проверку на наличие грязных данных. Таким образом, очередь $ applyAsync () будет очищена не более одного раза во время $ дайджеста и будет очищена только в том случае, если очередь уже была заполнена до начала $ дайджеста.

С другой стороны, очередь $ evalAsync () сбрасывается в верхней части цикла while, который реализует «грязную проверку» внутри $ дайджеста. Это означает, что любое выражение, добавленное в очередь $ evalAsync () во время дайджеста, будет выполнено позже в том же дайджесте.

Чтобы сделать это различие более конкретным, это означает, что асинхронные выражения, добавленные $ evalAsync () из привязки $ watch (), будут выполняться в том же дайджесте. Асинхронные выражения, добавленные $ applyAsync () из привязки $ watch (), будут выполняться в более поздний момент времени (~ 10 мс).

Надеюсь, это уже поможет некоторым из вас :-).


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

Все 154 Комментарий

Я столкнулся с аналогичной проблемой, которая произошла на ios 9, но работает нормально на других устройствах.

Я воспроизвел эту проблему с тем же кодом, что и santaslow в версии 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>

Приведенный выше код работает нормально в браузере настольного компьютера, веб-просмотре Android и iOS 8, но в iOS 9 он выдает исключение, когда я нажимаю ссылку:

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

Я больше не могу воспроизводить в iOS 9 Beta 3.

Я получаю ту же ошибку с публичной бета-версией ios9 (13A4293g)

Я проверил приведенный выше код на ios 9 beta 3 (13A4293g), больше никаких исключений. Но приложение с использованием ng-view по-прежнему генерирует исключения infdig на ios 9 beta 3.

Я получаю ту же ошибку с публичной бета-версией ios9 (13A4293g)

Ошибка: [$ rootScope: infdig ]
Наблюдатели, запущенные за последние 5 итераций: []
http://errors.angularjs.org/1.3.13/ $ rootScope / infdig? p0 = 10 & p1 =% 5B% 5D
файл: ///Users/mac5/Library/Developer/CoreSimulator/Devices/749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE/data/Containers/Bundle/Application/9B5EE368-F2A0-4C99-807B58-EA17Bhops /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. приложение / 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. приложение / www / lib / ionic / js / ionic.bundle.js: 23205: 31
файл: ///Users/mac5/Library/Developer/CoreSimulator/Devices/749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE/data/Containers/Bundle/Application/9B5EE368-F2A0-4C99-807B58-EA17Bhops /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-4CEA17-807Bhops.ua /www/lib/ionic/js/ionic.bundle.js:11713:25
dispatchEvent @ [собственный код]
triggerMouseEvent @ файл : ///Users/mac5/Library/Developer/CoreSimulator/Devices/749DE7E3-D93F-47F9-A1FC-E3D54A1CCEEE/data/Containers/Bundle/Application/9B5EE368-F2A0-4C9917-807-F2A0-4C99-807-80-85 /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-4CEA9917-807B- /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-F2A0-4C9917-807-F2A0-4C997-807-80-85 /www/lib/ionic/js/ionic.bundle.js:2975:13

Мы также получаем эту ошибку в общедоступной бета-версии 3 iOS 9 с нашим собственным приложением Angular. В iOS 8 этого не происходит.

В качестве обходного пути я написал простую директиву для замены ng-view и angular-route.js. Решение хорошо работало в нашем собственном приложении, все исключения infdig исчезли на ios 9 beta / beta 3. Ниже приведен упрощенный код, который предназначен только для нашего собственного приложения, общие варианты использования не рассматриваются. Я НЕ рекомендую другим людям использовать это:

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

Только что установил iOS 9 Beta 4, но проблема не исчезла. Кто-нибудь еще?

Я видел это в iOS 9 Beta 3 и до сих пор вижу в iOS 9 Beta 4.

+1

Да, мы наблюдаем ту же проблему даже с маршрутизатором angular ui. Есть ли у кого-нибудь действующий способ решения этой проблемы?

Видя ту же проблему в uiWebView на последней версии iOS9.

Есть ли у кого-нибудь обновления по этому поводу?

Это все еще проблема. Повторное открытие

Похоже, проблема с iOS. Это где-то отслеживается на webkit?

+1

+1

То же самое. Наше приложение Cordova работает нормально, если оно запущено в Интернете на iPad Safari, но бесконечный дайджест происходит, если оно запускается как приложение Cordova (UIWebView).

Точно такие же проблемы, как у @borrull ! Уже экспериментировал с WKWebView, и тогда проблема не существует. Но мы не можем использовать WKWebView, поскольку нам нужно обслуживание локальных файлов (и мы не хотим запускать локальный сервер в нашем приложении) и файлы cookie. Таким образом, он должен что-то делать с UIWebView в сочетании с Cordova / Mobile Safari на iOS 9. В настоящее время я отлаживаю $ locationWatch в Angular, потому что вижу, что наше приложение хочет перейти в другое место несколько раз, а затем (после 10 раз ) выдается ошибка дайджеста.

такая же проблема здесь на iOS9 béta4
Угловой бесконечный цикл $ дайджеста; (

+1, только в UIWebView, а не в WKWebView, но мы можем использовать UIWebView только в нашем приложении cordova.

+1

Если это проблема, связанная с iOS, пожалуйста, откройте проблему в трекере проблем webkit и предоставьте демонстрацию! +1 здесь вряд ли что-то изменит, так как это действительно действительно похоже на ошибку браузера.

Кто-то сообщил об этой ошибке в Apple для расследования?

Я сообщил об этой ошибке в Apple. Но, возможно, вам всем стоит сделать то же самое, чтобы привлечь их внимание к ошибке.

Не могли бы вы дать нам ссылку, чтобы мы могли +1?

Он находится в нашей личной учетной записи Apple через репортера ошибок .. так что нет общедоступной ссылки; (

Не могли бы вы опубликовать его на openradar и поделиться идентификатором rdar, чтобы мы могли его обмануть!

то же самое на iOS9 béta 5:
работает на мобильном сафари
работает на WKWebview, который мы не можем использовать, потому что он не может обслуживать локальные файлы и не поддерживает NSProtocol.
работает НЕ на UIWebView

то же самое здесь на IOS 9 Beta 5

Я также сообщил об ошибке в Apple. Ссылка Open Radar: https://openradar.appspot.com/22186109 (это должно помочь тем, кому лень сообщать об ошибке). Пожалуйста, оставьте комментарии, если вы можете улучшить формулировку / объяснение ошибки ;-) Вы можете загрузить проект Xcode, чтобы прикрепить его к сообщению об ошибке в билете Open Radar (спасибо @santaslow за JS в OP)

Я создал версию того же проекта Xcode (из @borrull), но с ui-router вместо ng-route. Точно такая же проблема. Для тех, кому интересно, вы можете найти проект здесь: http://s000.tinyupload.com/index.php?file_id=87281871603760127355

Мы тоже видим такую ​​проблему. Мне удалось устранить проблему, заключающуюся в том, что свойства location. * Не обновляются сразу, когда в смесь входит angular. Если вы попытаетесь присвоить значение location.hash (например, то, что делается за службой определения местоположения), немедленно прочтите его обратно, значение не изменилось. Похоже, что есть некоторые побочные эффекты, возникающие в результате обработчиков jqlite, прикрепленных к событиям popstate и hashchanged.

Я попробую загрузить образец, когда буду за компьютером.

+1

@CleverCoder Есть обновления по образцу?

Придется закончить дело с репродукцией утром, так как код, как я, был привязан все выходные. Спасибо за толчок! По мере того, как идет обратный отсчет iOS 9, мы заинтересованы в том, чтобы эта проблема была решена. Я загружу что-нибудь, как только смогу.

+1

Я воспроизвел то, что я считаю основной причиной, когда установка хэша местоположения или свойств href не «применяется» немедленно.
Вот ссылка на проект XCode:
https://www.dropbox.com/s/2jkwv2thhm86nly/iOS%209%20Location%20Bug.zip?dl=0
Сообщите мне, если у вас нет доступа к файлу.

Обратите внимание на полученное значение в location.hash, а также на подключение Safari для отладки. Кажется, что-то откладывает изменение в результате некоторой привязки событий, основанной на событиях «popstate» и «hashchange».

Надеюсь, это поможет.

  • Шон

Мы используем window.location.href вместо state.go, и, похоже, сейчас он работает. Менее глючит.

Значение location.hash будет правильным после запуска цикла выполнения. Angular может легко обойти эту проблему, отложив получение location.hash в setTimeout (..., 0). Думаю, это будет ~ 2 изменения в angular.js / src / ng / location.js.

@hober Пробовал тайм-аут с angular следующим образом:

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

а также

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

Поэтому я добавил setTimout вокруг метода setBrowserUrlWithFallback, но это не решило проблему.

Вот сокращенный тестовый пример, который не полагается на Angular и демонстрирует обходной путь. Мне неясно, как на самом деле реализовать обходной путь в Angular. https://gist.github.com/hober/a29b6c28ac1744c800dd

Получил немного больше об этом. Я внес изменения в Angular относительно местоположения.
В части «обновление браузера» я изменил $ rootScope. $ EvalAsync на $ rootScope. $ ApplyAsync.

Похоже, что оба метода делают одно и то же. Разница не станет очевидной, пока вы не посмотрите на фактическое выполнение $ digest. Когда AngularJS выполняет дайджест, он просматривает дерево Scope и выполняет привязки $ watch () до тех пор, пока не перестанут генерироваться грязные данные. В течение этого жизненного цикла сбрасываются и очередь $ applyAsync (), и очередь $ evalAsync (); но это происходит в двух очень разных местах.

Очередь $ applyAsync () очищается только в верхней части дайджеста $, прежде чем AngularJS начнет проверку на наличие грязных данных. Таким образом, очередь $ applyAsync () будет очищена не более одного раза во время $ дайджеста и будет очищена только в том случае, если очередь уже была заполнена до начала $ дайджеста.

С другой стороны, очередь $ evalAsync () сбрасывается в верхней части цикла while, который реализует «грязную проверку» внутри $ дайджеста. Это означает, что любое выражение, добавленное в очередь $ evalAsync () во время дайджеста, будет выполнено позже в том же дайджесте.

Чтобы сделать это различие более конкретным, это означает, что асинхронные выражения, добавленные $ evalAsync () из привязки $ watch (), будут выполняться в том же дайджесте. Асинхронные выражения, добавленные $ applyAsync () из привязки $ watch (), будут выполняться в более поздний момент времени (~ 10 мс).

Надеюсь, это уже поможет некоторым из вас :-).


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

Вот еще один подход. Я не так хорошо знаком с кодовой базой Angular, но логика кажется рациональной. Функция url браузера (...) в настоящее время зависит от location.href, немедленно возвращающего правильный URL. Поскольку этот метод вызывается в том же цикле выполнения, в рамках цикла стабилизации $ digest он продолжает получать старый URL. Этот патч использует 'pendingHref' для отслеживания назначения, вместо этого возвращая это значение, если оно установлено. Как только значение выровнено с location.href, ожидающее значение очищается. Во время набора URL-адреса таймер устанавливается на 0 мс, чтобы поймать случай, когда функция url () get не вызывается. Это не идеально, но логика вроде работает. В основном это делается для того, чтобы рассмотреть альтернативный подход, не вызывающий задержек в производительности. Это основано на теге 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;
     }
   };

Спасибо @CleverCoder за решение! Кажется, работает как шарм! : +1:

@CleverCoder
было бы здорово, если бы вы сделали запрос на перенос с этим для команды angular.

@viattik Я бы удивился, если бы команда angular приняла обходной путь для UIWebView на iOS9, поскольку ошибка находится в самом UIWebView (Apple). Но всегда можно попробовать ...

@ raftheunis87
Есть много ошибок в разных браузерах и множество обходных путей для этих ошибок в коде angular.
Хотя они официально не поддерживают UIWebView, многие гибридные приложения будут сломаны, и использование angular будет невозможно в гибридных приложениях в последней версии iOS, пока Apple не исправит эту ошибку. И я бы сказал, что это довольно большая проблема.
Так что я бы попробовал.

@viattik Полностью согласен. Кстати, Apple сообщила нам, что вряд ли они исправят ошибку UIWebView. Так что действительно: попробуйте ;-)

Ребята, если вы могли бы опубликовать это как ошибку webkit, в идеале с повторным обращением, я свяжусь с некоторыми из наших контактов на стороне WebKit. https://bugs.webkit.org/

@naomiblack
Не уверен, что это ошибка webkit. Потому что это происходит только в UIWebView на iOS9. Safari на iOS9 работает нормально.

@ raftheunis87 спасибо за ваши предложения кода, отлично работал

@ raftheunis87 @CleverCoder есть способ работы с ionic-angular? Вы можете быть более конкретным?

@abrahamrkj У меня нет опыта работы с ionic. Но есть ли их настройки в angular при использовании ionic? В противном случае я бы сказал, что то же исправление будет работать с ionic-angular ...

@ raftheunis87 https://github.com/driftyco/ionic/tree/master/js это угловой, который они используют.

@CleverCoder +1 для запроса на вытягивание. Я согласен с @viattik, что это важная проблема, поскольку она сломает множество гибридных приложений.

Запрос на перенос может появиться в ближайшем будущем, хотя я сомневаюсь, поскольку я не так близок к кодовой базе Angular, как другие. Я скоро вернусь к решению и постараюсь сделать его пуленепробиваемым. Кажется странным, что свойство объекта window.location не изменяется «сразу». В проведенных мною тестах я заметил, что изменение сохранялось до тех пор, пока не были установлены хуки событий popstate и hashchange, что заставило меня подумать, что причиной отложенного изменения может быть что-то иначе .. Возможно, время этих событий изменилось (я думаю, это то, что я наблюдал).
Я собираюсь посмотреть это в течение следующих нескольких дней, и, если ничего лучше не появится, я копну немного глубже, чтобы подтвердить то, что я знаю до сих пор, и что нет лучшего места для решения проблемы изменения поведения. Извините, если это сбивает с толку. Под капотом происходит много всего, чего я до сих пор не понимаю в связи с этими событиями.
Ваше здоровье!

... и да, @borrull , я согласен. Если Apple больше не будет вносить никаких изменений, то это серьезная бомба замедленного действия, которая действительно приведет к плохой печати и указанию пальца. Я не поклонник таймеров в качестве обходного пути (я бы предпочел улучшить логический поток вокруг этих встроенных свойств), но если мы не можем зависеть от изменения значения после его установки, то где же нам рисовать линия? Каким еще свойствам мы не можем доверять? Это странно.

@CleverCoder Просто хотел сказать спасибо, ваш патч действительно спас положение!

@CleverCoder Спасибо за данный обходной путь.

Я получил решение, которое использует функцию декоратора из angular и поставляется без исправления источника angular.

С помощью cssua эту настройку можно настроить для использования только в определенных средах.

    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 См. № 12635

@ jd-carroll: Это действительно интересно. Я мог бы вернуться к этому чуть позже сегодня, когда у меня будет время. Довольно завален всякой всячиной. Это просто создает большую загадку, поскольку это кажется отдельной проблемой, которая не должна приводить к задержке в обновлении значений location. *.

@realityfilter : Забавно, что вы упомянули декоратор ... Я только что закончил кое-что реализовать, используя функциональность декоратора Angular. Хороший!

Всем привет,
Я просто хотел добавить, что это исправление привело к ошибке в нашем коде, которую легко исправить.

В наших шаблонах были теги привязки, в которых использовались href = "#" и ng-click = "someCall ()". Href заставлял сайт переходить на index.html с помощью этого исправления. Удаление href устранило проблему.

Наше приложение ломается во время навигации по кнопке возврата в Ionic, оно сначала переходит в новое представление, затем частично возвращается к старому представлению, а затем снова возвращается к новому виду в бета-версии IOS9, любые разрешения для Ionic

Та же проблема с iOS 9 beta 5 13A4325c, Angular 1.4.0 (с cordova-ios 3.9.1). Надеюсь, основная ошибка UIWebView будет исправлена!

Та же проблема, что и в Angular v1.2.27

Я проследил это до версии 1.2.27, ошибки нет в предыдущей версии 1.2.26.

В частности, виноват этот коммит .

@damrbaby Похоже, я могу иметь к этому какое-то отношение.

Но тот факт, что он работает с последней версией angular в Mobile Safari, ясно показывает, что он должен что-то делать с uiWebView на iOS 9. Так что изменение, которое они сделали в исходном коде Angular, не обязательно плохо.

@CleverCoder @realityfilter @ jyc66 Я просто хотел поблагодарить вас, вы только что спасли мне день.

Проблема все еще присутствует в iOS9 GM Seed, так что обновляйте свои приложения, люди!

Я могу подтвердить, что проблема все еще присутствует в iOS9 GM (13A340)

Это означает, что Apple что-то сломала, и мы должны снова обновить наши приложения (некоторые из которых не менялись в течение месяцев или даже более года), чтобы предотвратить их сбой. Имеет смысл :(. Я бы предпочел, чтобы Apple исправила это для запусков iOS 9. Переход на последнюю версию Angular в старом приложении обязательно приведет к поломке некоторых других вещей.

Я очень сомневаюсь, что Angular - единственный фреймворк, у которого проблемы с iOS9?

Итак, @adamdbradley , @perrygovier и @mhartington из Ionic Team целый день работали над исправлением, которое будет работать как для Ionic, так и для простых приложений Angular. Цель состоит в том, чтобы быть исправлением, которое не требует модификации Angular, и будет (надеюсь) работать с большинством версий Angular 1.2+.

Вот наше текущее связанное решение, которое украшает и исправляет $browser , применяя этот патч . Примечание: это основано на Angular 1.4.3 и является своего рода «клоном с исправлениями» browser.js собственно Angular: https://github.com/driftyco/ionic/blob/ios9-patch/js /angular/service/decorators/ios9-browser-fix.js

Мы также разместили патч на нашем CDN. Я не рекомендую использовать файл CDN для производства, он только там, чтобы его было проще протестировать прямо сейчас.

Чтобы проверить это, поместите этот тег скрипта под файлом angular или ionic.bundle.js:

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

Кроме того, прямо сейчас он применяет патч независимо от того, работаете ли вы на iOS9 или нет. Скоро это будет исправлено, и теперь оно будет работать только в iOS 9 UIWebView.

Следите за соответствующей проблемой Ionic здесь: https://github.com/driftyco/ionic/issues/4082#issuecomment -139079725

Всем привет,

UI-sref работает как шарм, но $ state.go прерывает анимацию кнопки возврата.
и страница сильно мерцает даже после применения этого исправления.

С уважением,
Аджай Сингх

10 сентября 2015 г. в 6:23 Макс Линч [email protected] написал:

Итак, @adamdbradley https://github.com/adamdbradley , @perrygovier
https://github.com/perrygovier и @mhartington
https://github.com/mhartington из Ionic Team работали
целый день над исправлением, которое будет работать для Ionic и простых приложений Angular как
хорошо. Цель состоит в том, чтобы внести исправление, не требующее изменений.
Angular и будет (надеюсь) работать с большинством версий Angular 1.2+.

Вот наше текущее комплексное решение, которое украшает и исправляет $ browser с помощью
применяя этот патч
https://github.com/angular/angular.js/issues/12241#issuecomment -130744518.
Примечание: это основано на Angular 1.4.3 и является своего рода «клоном с исправлениями»
browser.js из собственно Angular:
https://github.com/driftyco/ionic/blob/ios9-patch/js/angular/service/decorators/ios9-browser-fix.js

Мы также разместили патч на нашем CDN. Я не рекомендую использовать файл CDN
для производства, это только там, чтобы легче было протестировать прямо сейчас.

Пожалуйста, проверьте это и дайте нам знать, как это происходит, спасибо.

Чтобы проверить это, поместите этот тег скрипта под свой angular или
файл ionic.bundle.js: