(Angular-ui-router) Показати анімацію завантаження під час розв’язання


80

Це питання з двох частин:

  1. Я використовую властивість Resolution у $ stateProvider.state (), щоб захопити певні дані сервера перед завантаженням контролера. Як я можу отримати завантажувальну анімацію для показу під час цього процесу?

  2. У мене є дочірні держави, які також використовують властивість резолюції. Проблема в тому, що користувальницький інтерфейс, схоже, хоче завершити всі вирішення, перш ніж завантажувати будь-який контролер. Чи можу я якось змусити батьківські контролери завантажуватися після того, як їх вирішення будуть вирішені, не потрібно чекати, поки всі дочірні рішення вирішать? Відповідь на це, ймовірно, також вирішить першу проблему.


4
Ви коли-небудь вирішували цю проблему?
Stefan Henze

3
@StefanHenze Ні, я щойно прийняв це як архітектурний недолік інтерфейсу маршрутизатора.
Matt Way

2
Тут обговорюється саме ця проблема: github.com/angular-ui/ui-router/issues/456
Джонні Ошика

6
@StefanHenze, ха-ха, каламбур не призначений ....
Дейв

Відповіді:


71

РЕДАГУВАТИ: Ось ще простіше рішення, перевірене та приємно працює:

У моєму головному контролері я просто є

$scope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
    if (toState.resolve) {
        $scope.showSpinner();
    }
});
$scope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
    if (toState.resolve) {
        $scope.hideSpinner();
    }
});

Це показує обертач, коли ми збираємося перейти до стану, який має щось вирішити, і приховує його, коли зміна стану завершиться. Можливо, ви захочете додати деяку перевірку ієрархії стану (тобто також показати вертушку, якщо батьківський стан, який завантажується, щось вирішує), але це рішення для мене добре працює.

Ось моя стара пропозиція для довідки та як альтернатива:

  1. У своєму контролері додатків прослухайте stateChangeStartподію та перевірте, чи збираєтеся ви перейти в стан, коли ви хочете показати обертання під час вирішення (див. Https://github.com/angular-ui/ui-router/wiki/Quick -Посилання # wiki-events-1 )

    $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams){
        if (toState.name == 'state.with.resolve') {
            $scope.showSpinner();  //this is a function you created to show the loading animation
        }
    })
    
  2. Коли нарешті викликається контролер, ви можете приховати спінер

    .controller('StateWithResolveCtrl', function($scope) {
        $scope.hideSpinner();
    })
    

Ви також можете перевірити наявність помилок, які могли виникнути під час вирішення, прослухавши файл $stateChangeError подію та приховавши анімацію під час обробки помилки.

Це не зовсім чисто, оскільки ви розподіляєте логіку спінера між контролерами, але це спосіб. Сподіваюся, це допоможе.


1
if (toState.resolve)перевіряє, чи має конфігурація стану словник вирішення. У моєму випадку це досить гарна оцінка того, що держава здійснює виклики асинхронних служб. Я припускаю, що якщо є блок вирішення, це вирішуються службові виклики. Код показує та приховує обертач лише у випадку, якщо були здійснені телефонні дзвінки. Як я вже описав вище, цей код, можливо, доведеться вдосконалити, щоб також перевірити батьківські стани, залежно від вашого випадку використання.
Стефан Хенце

у "старій пропозиції", на яку ви посилаєтесь $scopeу дзвінку $rootScope. звідки $scopeпоходить сюди?
pdeva

@pdeva, обробник подій був визначений всередині якогось контролера; тут також showSpinnerбула визначена функція.
Стефан Хенце

Я не міг користуватися, toState.resolveтому використовував fromState.name !== toState.name. Окрім цього, ваша відповідь фантастична, молодці!
Jacob

22

Я розробив таке рішення, яке ідеально підходить мені.

1. Додайте наступне app.run

app.run(function($rootScope){

    $rootScope
        .$on('$stateChangeStart', 
            function(event, toState, toParams, fromState, fromParams){ 
                $("#ui-view").html("");
                $(".page-loading").removeClass("hidden");
        });

    $rootScope
        .$on('$stateChangeSuccess',
            function(event, toState, toParams, fromState, fromParams){ 
                $(".page-loading").addClass("hidden");
        });

});

2. Розмістіть індикатор завантаження трохи вище інтерфейсу користувача. Додайте id = "ui-view" до div div ui-view.

<div class="page-loading">Loading...</div>
<div ui-view id="ui-view"></div>

3. додайте наступне у свій css

.hidden {
  display: none !important;
  visibility: hidden !important;
}

ПРИМІТКА:

А. Наведений вище код відображатиме індикатор завантаження у двох випадках 1) коли кутова програма завантажується вперше 2) при зміні подання.

Б. Якщо ви не хочете, щоб індикатор відображався під час першого завантаження кутової програми (до завантаження будь-якого подання), додайте прихований клас до div завантаження, як показано нижче

<div class="page-loading hidden">Loading...</div>

15
Цей runкод - це не "Кутовий шлях". Замість того , щоб маніпулювати DOM там, набір область видимості змінних , як loadingVisibleі viewVisible, а потім в HTML, владно сказати , коли елементи видимі чи приховані, як: <div class="page-loading" ng-show="viewVisible">Loading...</div>. Для спорожнення подання може знадобитися спеціальна директива.
Маттіас

2
Це НЕ рекомендується. Використання jQuery всередині angularcontext неприємно і створює небажану залежність між вашим видом та контролером - не найкраща практика!
Сайлас Хансен

Мені це подобається, але ... Чи справді це правильно стосовно DOM?
contributorpw

Проблема з виконанням цього `` кутового шляху '', як пропонує @MatthiasDailey, полягає в тому, що вам потрібно буде дублювати логіку шоу / приховувати всюди. Акуратне в цьому рішенні полягає в тому, що ви можете мати загальний метод показу / приховування вмісту або завантажувачів. Якщо ви дійсно хочете зробити це за допомогою Angular, тоді зробіть нову директиву, яка обгортає ui-view
thomaux

1
Я думаю, що ng-клас був би кращим підходом до цього.
UserX 07


8

Як щодо додавання вмісту до div, який буде заповнено користувальницьким інтерфейсом, як тільки властивості будуть вирішені?

У вашому index.html

<div ui-view class="container">
    Loading....
</div>

Тепер користувач побачить "Завантаження ...", поки властивості будуть вирішені. Як тільки все буде готове, вміст буде замінено користувацьким маршрутизатором на вміст ваших програм.


7
Це буде працювати, якщо у вашому додатку може бути повноцінна заміна програми для завантаження, але не вдається, якщо ви хочете вбудувати "завантаження ..." ієрархічно у ваш макет (наприклад, мати частину програми, яка показує, а інша частина відображає завантаження ... ).
Matt Way

1
Це все ще швидкий / брудний / простий спосіб змусити вас перейти до більш важливих речей ... наприклад, прискорення вирішення.
Брайан Вандербуш

3
Якщо ви завантажуєте декілька шаблонів у це представлення, що в ідеалі ви хотіли б. Повідомлення "завантаження ...." з'являється лише вперше.
Ясер Шайх

Чи можливо це в йонічному? Я не зміг використати цей спосіб в йонічному.
Anoop.PA

7

Я використовую анімований gif, який показую лише тоді, коли $httpє очікувані запити.

У шаблоні базової сторінки у мене є панель навігації та контролер навігаційної панелі. Відповідна частина контролера виглядає так:

controllers.controller('NavbarCtrl', ['$scope', '$http',
    function ($scope, $http) {
        $scope.hasPendingRequests = function () {
            return $http.pendingRequests.length > 0;
        };
    }]);

Відповідний код у моєму html:

<span class="navbar-spinner" ng-show="hasPendingRequests()">
    <img src="/static/img/spinner.gif">
</span>

Я сподіваюся, що це допоможе!


3
Як ви можете зрозуміти з мого запитання, я не маю доступу до контролерів, поки не буде остаточно вирішено всі проблеми (в чому проблема)!
Matt Way

1
Не слід використовувати функцію для ng-show або ng-if, оскільки вони будуть викликані в кожному циклі дайджесту. Це зменшить продуктивність.
Вікас Гаутам,

4

Моя ідея полягає в тому, щоб пройти шлях на графіку стану між перехідними станами $stateChangeStartта зібрати всі залучені перегляди. Потім кожна ui-viewдиректива спостерігає, якщо відповідний вигляд бере участь у переході та додає'ui-resolving' клас до свого елемента.

Plunker демо вводить два кореня стану: firstі second, останній має два підстанів second.sub1і second.sub2. Держава second.sub2також націлена на footerпогляд, який належить її бабусі та дідусю.


3

Це завантажувач для глобального пошуку, коли сторінка здійснює навігацію між будь-яким станом (будь-якою сторінкою), розміщеним в app.js

.run(
    ['$rootScope',
        function($rootScope) {
            $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
                $rootScope.preloader = true;
            })
            $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
                $rootScope.preloader = false;
            })
        }
    ])

У html:

<div ng-show="preloader">Loading...</div>

Ідеально! Дякую!
Brynner Ferreira

Не може бути більш очевидним ... можливо, пояснити, як ви вкладаєте щось в інтерфейс користувача, поки триває завантаження? Визначення 2 змінних є настільки тривіальним
DDD

2

Я вважаю за краще використовувати директиву, яка відповідає будь-якій завантажувальній діяльності, головним чином для підтримання чистоти моєї кодової бази

angular.module('$utilityElements', [])
.directive('loader',['$timeout','$rootScope', function($timeout, $rootScope) {
    return {
      restrict: 'A',
      template: '<div id="oneloader" class="hiddenload">loading...</div>',
      replace: true,
      compile: function (scope, element, attrs) {
        $timeout(function(){
          $rootScope
              .$on('$stateChangeStart',
                  function(event, toState, toParams, fromState, fromParams){
                      $("#oneloader").removeClass("hiddenload");
              });

          $rootScope
              .$on('$stateChangeSuccess',
                  function(event, toState, toParams, fromState, fromParams){
                      //add a little delay
                      $timeout(function(){
                        $("#oneloader").addClass("hiddenload");
                      },500)
              });
        }, 0);
      }
    }
  }]);

2

З $stateChangeStartтих пір використання і тому подібне було припинено та замінено перехідними гачками . Отже, для відповіді Штефана Хенце оновленою версією буде:

$transitions.onStart({}, function(transition) {
  if (transition.to().resolve) {
    $scope.showSpinner();
  }
});

$transitions.onSuccess({}, function(transition) {
  if (transition.to().resolve) {
    $scope.hideSpinner();
  }
});

Ви можете використовувати це у своєму батьківському контролері. Не забувайте вводити $transitions-

.controller('parentController',['$transitions',function($transitions){...}]);

Також майте на увазі, resolveщо порожній об’єкт все одно відображатиметься transition.to().resolve == true, тому не залишайте порожній заповнювач resolveу державній декларації.


1

Моя ідея використовувати view під час використання resole в маршрутизаторі, це працює неймовірно. спробуйте це.

//edit index.html file 
<ion-nav-view>
    <div ng-show="loadder" class="loddingSvg">
        <div class="svgImage"></div>
    </div>
</ion-nav-view>

// css file

.loddingSvg {
    height: 100%;
    background-color: rgba(0, 0, 0, 0.1);
    position: absolute;
    z-index: 99;
    left: 0;
    right: 0;
}

.svgImage {
    background: url(../img/default.svg) no-repeat;
    position: relative;
    z-index: 99;
    height: 65px;
    width: 65px;
    background-size: 56px;
    top: 50%;
    margin: 0 auto;
}

// edit app.js

 .run(function($ionicPush, $rootScope, $ionicPlatform) {




        $rootScope.$on('$stateChangeStart',
            function(event, toState, toParams, fromState, fromParams) {
                $rootScope.loadder = true;
            });

        $rootScope.$on('$stateChangeSuccess',
            function(event, toState, toParams, fromState, fromParams) {
                $rootScope.loadder = false;
            });

});

0

Якщо хтось використовує ngRoute, чекає, resolveперш ніж завантажити наступний вигляд, і використовує angular-bootstrap-uiдля ui, ви можете зробити наступне :

app.config([
  "$routeProvider", "$locationProvider", function($routeProvider, $locationProvider) {
    return $routeProvider.when("/seasons/:seasonId", {
      templateUrl: "season-manage.html",
      controller: "SeasonManageController",
      resolve: {
        season: [
          "$route", "$q", "$http", "$modal", function($route, $q, $http, $modal) {
            var modal, promise, seasonId;
            modal = $modal.open({
              backdrop: "static",
              template: "<div>\n  <div class=\"modal-header\">\n    <h3 class=\"modal-title\">\n      Loading...\n    </h3>\n  </div>\n  <div class=\"modal-body\">\n    <progressbar\n      class=\"progress-striped active\"\n      value=\"'100'\">\n    </progressbar>\n  </div>\n</div>",
              keyboard: false,
              size: "lg"
            });
            promise = $q.defer();
            seasonId = $route.current.params.seasonId;
            $http.get("/api/match/seasons/" + seasonId).success(function(data) {
              modal.close();
              promise.resolve(data);
            }).error(function(data) {
              modal.close();
              promise.reject(data);
            });

            return promise.promise;
          }
        ]
      }
    });
  }
]);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.