(Angular-ui-router) Hiển thị hoạt ảnh đang tải trong quá trình giải quyết


80

Đây là một câu hỏi gồm hai phần:

  1. Tôi đang sử dụng thuộc tính giải quyết bên trong $ stateProvider.state () để lấy dữ liệu máy chủ nhất định trước khi tải bộ điều khiển. Tôi sẽ làm thế nào về việc tải hoạt ảnh để hiển thị trong quá trình này?

  2. Tôi có các trạng thái con cũng sử dụng thuộc tính giải quyết. Vấn đề là ui-router dường như muốn hoàn thiện tất cả các giải pháp trước khi tải bất kỳ bộ điều khiển nào . Có cách nào tôi có thể yêu cầu bộ điều khiển cha tải lên sau khi giải quyết của chúng đã được giải quyết mà không phải đợi tất cả các giải quyết con không? Một câu trả lời cho điều này có thể cũng sẽ giải quyết vấn đề đầu tiên.


4
Bạn đã bao giờ giải quyết vấn đề này?
Stefan Henze

3
@StefanHenze Không, tôi chỉ chấp nhận đây là một lỗ hổng kiến ​​trúc ui-router.
Matt Way

2
Có một cuộc thảo luận về vấn đề chính xác này ở đây: github.com/angular-ui/ui-router/issues/456
Johnny Oshika

6
@StefanHenze lol, không có ý định chơi chữ ....
Dave

Câu trả lời:


71

CHỈNH SỬA: Đây là một giải pháp thậm chí còn dễ dàng hơn, đã được thử nghiệm và hoạt động tốt:

Trong bộ điều khiển chính của tôi, tôi chỉ đơn giản có

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

Điều này hiển thị vòng quay bất cứ khi nào chúng ta sắp chuyển đến trạng thái có bất kỳ điều gì cần giải quyết và ẩn nó, khi quá trình thay đổi trạng thái hoàn tất. Bạn có thể muốn thêm một số kiểm tra phân cấp trạng thái (tức là cũng hiển thị spinner nếu trạng thái mẹ đang được tải giải quyết vấn đề gì đó) nhưng giải pháp này hoạt động tốt đối với tôi.

Đây là gợi ý cũ của tôi để tham khảo và thay thế:

  1. Trong bộ điều khiển ứng dụng của bạn, hãy lắng nghe stateChangeStartsự kiện và kiểm tra xem bạn có sắp chuyển sang trạng thái mà bạn muốn hiển thị vòng quay trong quá trình giải quyết hay không (xem https://github.com/angular-ui/ui-router/wiki/Quick -Tham khảo # wiki-event-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. Cuối cùng khi bộ điều khiển của bạn được gọi, bạn có thể ẩn con quay

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

Bạn cũng có thể muốn kiểm tra bất kỳ lỗi nào có thể đã xảy ra trong quá trình giải quyết bằng cách lắng nghe $stateChangeErrorsự kiện và ẩn hoạt ảnh trong khi bạn xử lý lỗi.

Điều này không hoàn toàn rõ ràng khi bạn phân phối logic cho spinner giữa các bộ điều khiển, nhưng đó là một cách. Hy vọng nó giúp.


1
if (toState.resolve)kiểm tra xem cấu hình trạng thái có từ điển giải quyết hay không. Trong trường hợp của tôi, đây là một ước tính đủ tốt để trạng thái thực hiện các cuộc gọi dịch vụ không đồng bộ. Tôi giả sử rằng nếu có một khối giải quyết, thì đó là các cuộc gọi dịch vụ sẽ được giải quyết. Mã chỉ hiển thị và ẩn con quay khi cuộc gọi dịch vụ được thực hiện. Như tôi đã mô tả ở trên, mã này có thể cần được tinh chỉnh để kiểm tra trạng thái gốc, tùy thuộc vào trường hợp sử dụng của bạn.
Stefan Henze

trong 'đề xuất cũ' bạn tham chiếu $scopebên trong cuộc gọi $rootScope. nơi nào $scopeđến từ đây?
pdeva

@pdeva, trình xử lý sự kiện được định nghĩa bên trong một số bộ điều khiển; đó cũng là nơi mà showSpinnerhàm được định nghĩa.
Stefan Henze

Tôi không thể sử dụng toState.resolvenên tôi đã sử dụng fromState.name !== toState.name. Bên cạnh đó, câu trả lời của bạn rất tuyệt vời, rất tốt!
Jacob Hulse

22

Tôi đã phát triển giải pháp sau đây phù hợp với tôi.

1. Thêm app.run sau

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. Đặt chỉ báo tải ngay phía trên ui-view. Thêm id = "ui-view" vào ui-view div.

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

3. thêm phần sau vào css của bạn

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

GHI CHÚ:

A. Đoạn mã trên sẽ hiển thị chỉ báo tải trong hai trường hợp 1) khi ứng dụng góc được tải lần đầu tiên 2) khi chế độ xem được thay đổi.

B. Nếu bạn không muốn chỉ báo được hiển thị khi ứng dụng góc tải lần đầu tiên (trước khi bất kỳ chế độ xem nào được tải), hãy thêm lớp ẩn vào div đang tải như bên dưới

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

15
runMã đó không phải là Angular Way. Thay vì thao tác DOM ở đó, bộ biến mô như loadingVisibleviewVisible, và sau đó trong HTML, phải nhất thiết nói khi các yếu tố có thể nhìn thấy hoặc ẩn, như: <div class="page-loading" ng-show="viewVisible">Loading...</div>. Mặc dù vậy, để làm trống khung nhìn, nó có thể yêu cầu một chỉ thị tùy chỉnh.
Matthias

2
Điều này KHÔNG được khuyến khích. Việc sử dụng jQuery bên trong anglecontext là điều không tốt và đang tạo ra sự phụ thuộc không mong muốn giữa chế độ xem và bộ điều khiển của bạn - không phải là cách tốt!
Silas Hansen

Tôi thích điều này nhưng ... Nó có thực sự chính xác liên quan đến DOM không?
contributorpw

Vấn đề khi thực hiện điều này 'theo cách góc cạnh' như @MatthiasDailey đề xuất là bạn cần phải sao chép logic hiển thị / ẩn ở khắp nơi. Điều thú vị về giải pháp này là bạn có thể có một phương thức toàn cục để hiển thị / ẩn nội dung hoặc bộ tải. Nếu bạn muốn thực sự muốn làm điều này một cách kiễu góc, sau đó đưa ra một chỉ thị mới mà kết thúc tốt đẹp ui-view
thomaux

1
Tôi nghĩ ng-class sẽ là một cách tiếp cận tốt hơn cho điều này.
UserX

16

Tôi thấy việc sử dụng thanh tải góc hoạt động thực sự tốt cho các giải pháp kéo dài do truy cập mạng.


8

Làm thế nào về việc thêm nội dung vào div sẽ được lấp đầy bởi ui-router khi các thuộc tính đã được giải quyết?

Trong của bạn index.html

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

Bây giờ người dùng sẽ thấy "Đang tải ..." trong khi các thuộc tính được giải quyết. Khi mọi thứ đã sẵn sàng, nội dung sẽ được ui-router thay thế bằng nội dung ứng dụng của bạn.


7
Điều này sẽ hoạt động nếu ứng dụng của bạn có thể có một phần thay thế tải ứng dụng đầy đủ, nhưng sẽ không thành công nếu bạn muốn xây dựng 'tải ...' theo thứ bậc vào bố cục của mình (ví dụ: có một phần hiển thị ứng dụng của bạn, với một phần khác hiển thị tải ... ).
Matt Way

1
Đó vẫn là một cách nhanh chóng / bẩn thỉu / dễ dàng để giúp bạn chuyển sang những việc quan trọng hơn ... như tăng tốc độ xử lý của bạn.
Brian Vanderbusch

3
Nếu bạn đang tải nhiều mẫu vào dạng xem đó, lý tưởng nhất là bạn muốn. Thông báo "đang tải ...." sẽ chỉ xuất hiện lần đầu tiên.
Yasser Shaikh

Điều này có thể xảy ra trong Ionic? Tôi không thể sử dụng cách này trong Ionic.
Anoop.PA

7

Tôi sử dụng một gif động mà tôi chỉ hiển thị khi $httpcó yêu cầu đang chờ xử lý.

Trong mẫu trang cơ sở của tôi, tôi có bộ điều khiển thanh điều hướng và thanh điều hướng. Phần có liên quan của bộ điều khiển trông giống như sau:

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

Mã tương ứng trong html của tôi là:

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

Tôi hy vọng rằng sẽ giúp!


3
Như bạn có thể thấy trong câu hỏi của tôi, tôi không có bất kỳ quyền truy cập nào vào bộ điều khiển cho đến khi tất cả các giải pháp đã được hoàn tất (đó là vấn đề)!
Matt Way

1
Bạn không nên sử dụng hàm cho ng-show hoặc ng-if, vì chúng sẽ được gọi trong mỗi vòng lặp thông báo. Nó sẽ làm giảm hiệu suất.
Vikas Gautam

4

Ý tưởng của tôi là đi trên biểu đồ trạng thái giữa các trạng thái chuyển tiếp $stateChangeStartvà thu thập tất cả các chế độ xem có liên quan. Sau đó, mọi ui-viewchỉ thị sẽ theo dõi nếu chế độ xem tương ứng tham gia vào quá trình chuyển đổi và thêm 'ui-resolving'lớp vào phần tử của nó.

Bản demo plunker giới thiệu hai trạng thái gốc: firstsecond, trạng thái sau có hai giá trị con second.sub1second.sub2. Nhà nước second.sub2cũng nhắm mục tiêu footerchế độ xem thuộc về ông bà của nó.


3

Đây là trình tải cho toàn cầu khi điều hướng trang giữa bất kỳ trạng thái nào (bất kỳ trang nào), đặt trong 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;
            })
        }
    ])

Trong html:

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

Hoàn hảo! Cảm ơn!
Brynner Ferreira

Không thể rõ ràng hơn ... có thể giải thích rằng làm thế nào để bạn đưa thứ gì đó vào ui-view trong khi quá trình tải đang diễn ra? Xác định 2 biến là quá tầm thường
DDD

2

Tôi thích sử dụng một chỉ thị để khớp với bất kỳ hoạt động tải nào, chủ yếu là để giữ cho cơ sở mã của tôi sạch sẽ

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

Việc sử dụng $stateChangeStartvà những thứ tương tự đã không còn được dùng nữa và được thay thế bằng Móc chuyển tiếp . Vì vậy, đối với câu trả lời của Stefan Henze, phiên bản cập nhật sẽ là:

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

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

Bạn có thể sử dụng điều này trong bộ điều khiển cha mẹ của bạn. Nhớ tiêm $transitions-

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

Ngoài ra, hãy nhớ rằng một resolveđối tượng trống sẽ vẫn hiển thị transition.to().resolve == true, vì vậy đừng để trống chỗ dành sẵn resolvetrong khai báo trạng thái.


1

Ý tưởng của tôi là sử dụng chế độ xem trong khi sử dụng cộng hưởng trong bộ định tuyến, nó đang hoạt động tuyệt vời. thử cái này.

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

Nếu bất kỳ ai đang sử dụng ngRoute, chờ đợi resolvetrước khi tải chế độ xem tiếp theo và sử dụng angular-bootstrap-uicho ui, bạn có thể làm như sau :

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;
          }
        ]
      }
    });
  }
]);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.