Trì hoãn thay đổi tuyến đường AngularJS cho đến khi mô hình được tải để ngăn nhấp nháy


321

Tôi tự hỏi liệu có cách nào (tương tự như Gmail) để AngularJS trì hoãn hiển thị tuyến mới cho đến sau mỗi mô hình và dữ liệu của nó đã được tìm nạp bằng các dịch vụ tương ứng.

Ví dụ: nếu có một ProjectsControllerdanh sách liệt kê tất cả các Dự án và project_index.htmllà mẫu hiển thị các Dự án này, Project.query()sẽ được tìm nạp hoàn toàn trước khi hiển thị trang mới.

Cho đến lúc đó, trang cũ vẫn sẽ tiếp tục hiển thị (ví dụ: nếu tôi đang duyệt một trang khác và sau đó quyết định xem chỉ mục Dự án này).

Câu trả lời:


374

$ routeProvider giải quyết thuộc tính cho phép trì hoãn thay đổi tuyến đường cho đến khi dữ liệu được tải.

Đầu tiên xác định một tuyến đường với resolvethuộc tính như thế này.

angular.module('phonecat', ['phonecatFilters', 'phonecatServices', 'phonecatDirectives']).
  config(['$routeProvider', function($routeProvider) {
    $routeProvider.
      when('/phones', {
        templateUrl: 'partials/phone-list.html', 
        controller: PhoneListCtrl, 
        resolve: PhoneListCtrl.resolve}).
      when('/phones/:phoneId', {
        templateUrl: 'partials/phone-detail.html', 
        controller: PhoneDetailCtrl, 
        resolve: PhoneDetailCtrl.resolve}).
      otherwise({redirectTo: '/phones'});
}]);

lưu ý rằng resolvetài sản được xác định trên tuyến đường.

function PhoneListCtrl($scope, phones) {
  $scope.phones = phones;
  $scope.orderProp = 'age';
}

PhoneListCtrl.resolve = {
  phones: function(Phone, $q) {
    // see: https://groups.google.com/forum/?fromgroups=#!topic/angular/DGf7yyD4Oc4
    var deferred = $q.defer();
    Phone.query(function(successData) {
            deferred.resolve(successData); 
    }, function(errorData) {
            deferred.reject(); // you could optionally pass error data here
    });
    return deferred.promise;
  },
  delay: function($q, $defer) {
    var delay = $q.defer();
    $defer(delay.resolve, 1000);
    return delay.promise;
  }
}

Lưu ý rằng định nghĩa của bộ điều khiển chứa một đối tượng phân giải khai báo những thứ cần có cho hàm tạo của bộ điều khiển. Ở đây, phonesđược đưa vào bộ điều khiển và nó được định nghĩa trong thuộc resolvetính.

Các resolve.phoneschức năng có trách nhiệm trả lại một lời hứa. Tất cả các lời hứa được thu thập và thay đổi tuyến đường bị trì hoãn cho đến khi tất cả các lời hứa được giải quyết.

Bản demo hoạt động: http://mhevery.github.com/angular-phonecat/app/#/phones Nguồn: https://github.com/mhevery/angular-phonecat/commit/ba33d3ec2d01b70eb5d3d531619bf9015349683131


10
@MiskoHevery - điều gì xảy ra nếu bộ điều khiển của bạn nằm trong một mô-đun và được định nghĩa là một chuỗi chứ không phải là hàm. Làm thế nào bạn có thể thiết lập thuộc tính giải quyết như bạn làm?
aar0n

53
Làm thế nào điều này được sử dụng trong angular.controller()định nghĩa bộ điều khiển loại? Trong phần này $routeProvider, tôi nghĩ bạn phải sử dụng tên chuỗi của bộ điều khiển.
Ben Lesh

6
Bất kỳ ví dụ nào sử dụng angular.contler () và với phiên bản mới nhất của AngularJS?
Laurent

22
@bledh, khi bạn sử dụng angular.controller(), bạn có thể gán kết quả của hàm này cho một biến ( var MyCtrl = angular.controller(...)) và sau đó làm việc với điều đó ( MyCtrl.loadData = function(){..}). Hãy xem video của egghead, mã được hiển thị ngay lập tức: egghead.io/video/0uvAseNXDr0
petrkotek

17
Tôi vẫn muốn có một cách hay để làm mà không cần đặt bộ điều khiển của mình ra toàn cầu. Tôi không muốn xả rác khắp nơi trên thế giới. Bạn có thể làm điều đó với một hằng số, nhưng sẽ rất tuyệt nếu có thể đặt chức năng phân giải trên / trong bộ điều khiển, chứ không phải ở nơi nào khác.
Erik Honn

51

Đây là một ví dụ hoạt động tối thiểu hoạt động cho Angular 1.0.2

Bản mẫu:

<script type="text/ng-template" id="/editor-tpl.html">
    Editor Template {{datasets}}
</script>

<div ng-view>

</div>

JavaScript:

function MyCtrl($scope, datasets) {    
    $scope.datasets = datasets;
}

MyCtrl.resolve = {
    datasets : function($q, $http) {
        var deferred = $q.defer();

        $http({method: 'GET', url: '/someUrl'})
            .success(function(data) {
                deferred.resolve(data)
            })
            .error(function(data){
                //actually you'd want deffered.reject(data) here
                //but to show what would happen on success..
                deferred.resolve("error value");
            });

        return deferred.promise;
    }
};

var myApp = angular.module('myApp', [], function($routeProvider) {
    $routeProvider.when('/', {
        templateUrl: '/editor-tpl.html',
        controller: MyCtrl,
        resolve: MyCtrl.resolve
    });
});​

http://jsfiddle.net/dTJ9N/3/

Phiên bản được sắp xếp hợp lý:

Vì $ http () đã trả lại một lời hứa (còn gọi là hoãn lại), chúng tôi thực sự không cần phải tạo riêng cho mình. Vì vậy, chúng ta có thể đơn giản hóa MyCtrl. quyết tâm:

MyCtrl.resolve = {
    datasets : function($http) {
        return $http({
            method: 'GET', 
            url: 'http://fiddle.jshell.net/'
        });
    }
};

Kết quả của $ http () chứa dữ liệu , trạng thái , tiêu đề và đối tượng cấu hình , vì vậy chúng ta cần thay đổi phần thân của MyCtrl thành:

$scope.datasets = datasets.data;

http://jsfiddle.net/dTJ9N/5/


Tôi đang cố gắng làm một cái gì đó như thế này nhưng gặp rắc rối với việc tiêm 'bộ dữ liệu' vì nó không được xác định. Có suy nghĩ gì không?
Rob Bygrave

Xin chào mb21, tôi nghĩ rằng bạn có thể giúp tôi giải đáp câu hỏi này: stackoverflow.com/questions/14271713/ chủ
Winduptoy

Ai đó có thể giúp tôi chuyển đổi câu trả lời này sang định dạng app.contler ('MyCtrl') không? jsfiddle.net/5usya/1 không làm việc cho tôi.
dùng1071182

tôi gặp lỗi:Unknown provider: datasetsProvider <- datasets
chovy

Bạn có thể làm cho câu trả lời của mình đơn giản hơn bằng cách thay thế các bộ dữ liệu bằng cách này:function($http) { return $http({method: 'GET', url: '/someUrl'}) .then( function(data){ return data;}, function(reason){return 'error value';} ); }
Morteza Tourani

32

Tôi thấy một số người hỏi làm thế nào để làm điều này bằng cách sử dụng phương pháp angular.contler với phép tiêm phụ thuộc thân thiện với thu nhỏ. Vì tôi vừa mới làm việc nên tôi cảm thấy bắt buộc phải quay lại và giúp đỡ. Đây là giải pháp của tôi (được thông qua từ câu hỏi ban đầu và câu trả lời của Misko):

angular.module('phonecat', ['phonecatFilters', 'phonecatServices', 'phonecatDirectives']).
  config(['$routeProvider', function($routeProvider) {
    $routeProvider.
      when('/phones', {
        templateUrl: 'partials/phone-list.html', 
        controller: PhoneListCtrl, 
        resolve: { 
            phones: ["Phone", "$q", function(Phone, $q) {
                var deferred = $q.defer();
                Phone.query(function(successData) {
                  deferred.resolve(successData); 
                }, function(errorData) {
                  deferred.reject(); // you could optionally pass error data here
                });
                return deferred.promise;
             ]
            },
            delay: ["$q","$defer", function($q, $defer) {
               var delay = $q.defer();
               $defer(delay.resolve, 1000);
               return delay.promise;
              }
            ]
        },

        }).
      when('/phones/:phoneId', {
        templateUrl: 'partials/phone-detail.html', 
        controller: PhoneDetailCtrl, 
        resolve: PhoneDetailCtrl.resolve}).
      otherwise({redirectTo: '/phones'});
}]);

angular.controller("PhoneListCtrl", [ "$scope", "phones", ($scope, phones) {
  $scope.phones = phones;
  $scope.orderProp = 'age';
}]);

Vì mã này có nguồn gốc từ câu hỏi / câu trả lời phổ biến nhất, nó chưa được kiểm tra, nhưng nó sẽ gửi cho bạn đúng hướng nếu bạn đã hiểu cách tạo mã góc thân thiện. Một phần mà mã riêng của tôi không yêu cầu là tiêm "Điện thoại" vào chức năng phân giải cho 'điện thoại', tôi cũng không sử dụng bất kỳ đối tượng 'trì hoãn' nào.

Tôi cũng đề xuất video youtube này http://www.youtube.com/watch?v=P6KITGRQujQ&list=UUKW92i7iQFuNILqQOUOCrFw&index=4&feature=plcp , điều này đã giúp tôi khá nhiều

Nếu bạn quan tâm, tôi đã quyết định dán mã của riêng mình (Viết bằng coffeescript) để bạn có thể thấy tôi đã làm cho nó hoạt động như thế nào.

FYI, trước đây tôi sử dụng bộ điều khiển chung giúp tôi thực hiện CRUD trên một số mô hình:

appModule.config ['$routeProvider', ($routeProvider) ->
  genericControllers = ["boards","teachers","classrooms","students"]
  for controllerName in genericControllers
    $routeProvider
      .when "/#{controllerName}/",
        action: 'confirmLogin'
        controller: 'GenericController'
        controllerName: controllerName
        templateUrl: "/static/templates/#{controllerName}.html"
        resolve:
          items : ["$q", "$route", "$http", ($q, $route, $http) ->
             deferred = $q.defer()
             controllerName = $route.current.controllerName
             $http(
               method: "GET"
               url: "/api/#{controllerName}/"
             )
             .success (response) ->
               deferred.resolve(response.payload)
             .error (response) ->
               deferred.reject(response.message)

             return deferred.promise
          ]

  $routeProvider
    .otherwise
      redirectTo: '/'
      action: 'checkStatus'
]

appModule.controller "GenericController", ["$scope", "$route", "$http", "$cookies", "items", ($scope, $route, $http, $cookies, items) ->

  $scope.items = items
      #etc ....
    ]

Tôi có suy luận chính xác từ ví dụ của bạn và những lần thử thất bại của tôi mà giờ đây không thể tham chiếu một resolvechức năng trong bộ điều khiển, trong các phiên bản gần đây của Angular không? Vì vậy, nó phải được khai báo ngay trong cấu hình như ở đây?
XML

@XMLilley Tôi khá chắc chắn đó là trường hợp. Ví dụ này là từ 1.1.2 khi tôi viết nó, tôi tin. Tôi không thấy bất kỳ tài liệu nào về việc đưa giải quyết vào bên trong bộ điều khiển
bitwit

2
Tuyệt thật, cảm ơn nhé. Có rất nhiều ví dụ về việc làm như vậy trên SO (như hai người đứng đầu ở đây), nhưng tất cả đều từ năm 2012 đến đầu năm 2013. Đó là một cách tiếp cận thanh lịch, nhưng dường như bị phản đối. Sự thay thế sạch nhất hiện nay dường như là viết các dịch vụ riêng lẻ là đối tượng hứa hẹn.
XML

Cảm ơn điều này đã làm việc cho tôi. Đối với bất kỳ ai khác gặp lỗi về $deferdịch vụ không xác định , hãy lưu ý rằng trong phiên bản 1.5.7 của AngularJS, bạn muốn sử dụng $timeoutthay thế.
racl101

18

Cam kết này , là một phần của phiên bản 1.1.5 trở lên, phơi bày $promiseđối tượng của $resource. Các phiên bản của ngResource bao gồm cam kết này cho phép giải quyết các tài nguyên như thế này:

$ routeProvider

resolve: {
    data: function(Resource) {
        return Resource.get().$promise;
    }
}

bộ điều khiển

app.controller('ResourceCtrl', ['$scope', 'data', function($scope, data) {

    $scope.data = data;

}]);

Những phiên bản bao gồm cam kết đó, xin vui lòng?
XML

Phiên bản không ổn định mới nhất (1.1.5) bao gồm cam kết này. ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js
Maximilian Hoffmann

Tôi thích cách tiếp cận ít dài dòng này. Sẽ thật tuyệt khi tạo một lời hứa từ đối tượng dữ liệu thực tế và truyền trực tiếp điều đó, nhưng đây là mã ít đến mức nó hoạt động độc đáo.
Sam Barnum

1
Làm thế nào tài nguyên sẽ truy cập $ routeParams? Ví dụ: trong GET '/api/1/apps/:appId'-> App.get({id: $routeParams.appId}).$promise();Tôi không thể sử dụng như thế này
zeronone

2
@zeronone bạn tiêm $routeđể giải quyết và sử dụng của bạn $route.current.params. Hãy cẩn thận, $routeParamsvẫn chỉ vào tuyến đường cũ.
Brice Stacey

16

Đoạn mã này là tiêm phụ thuộc thân thiện với (tôi thậm chí sử dụng nó kết hợp giữa ngminuglify ) và đó là một giải pháp dựa trên miền thanh lịch hơn .

Ví dụ dưới đây đăng ký một điện thoại tài nguyênliên tục phoneRoutes , trong đó có tất cả các thông tin định tuyến của bạn cho rằng (điện thoại) miền. Một cái gì đó tôi không thích trong câu trả lời được cung cấp là vị trí của logic giải quyết - mô-đun chính không nên biết bất cứ điều gì hoặc bị làm phiền về cách các đối số tài nguyên được cung cấp cho bộ điều khiển. Bằng cách này, logic vẫn ở trong cùng một miền.

Lưu ý: nếu bạn đang sử dụng ngmin (và nếu bạn không: bạn nên), bạn chỉ phải viết các hàm giải quyết với quy ước mảng DI.

angular.module('myApp').factory('Phone',function ($resource) {
  return $resource('/api/phone/:id', {id: '@id'});
}).constant('phoneRoutes', {
    '/phone': {
      templateUrl: 'app/phone/index.tmpl.html',
      controller: 'PhoneIndexController'
    },
    '/phone/create': {
      templateUrl: 'app/phone/edit.tmpl.html',
      controller: 'PhoneEditController',
      resolve: {
        phone: ['$route', 'Phone', function ($route, Phone) {
          return new Phone();
        }]
      }
    },
    '/phone/edit/:id': {
      templateUrl: 'app/phone/edit.tmpl.html',
      controller: 'PhoneEditController',
      resolve: {
        form: ['$route', 'Phone', function ($route, Phone) {
          return Phone.get({ id: $route.current.params.id }).$promise;
        }]
      }
    }
  });

Phần tiếp theo là tiêm dữ liệu định tuyến khi mô-đun ở trạng thái cấu hình và áp dụng nó cho $ routeProvider .

angular.module('myApp').config(function ($routeProvider, 
                                         phoneRoutes, 
                                         /* ... otherRoutes ... */) {

  $routeProvider.when('/', { templateUrl: 'app/main/index.tmpl.html' });

  // Loop through all paths provided by the injected route data.

  angular.forEach(phoneRoutes, function(routeData, path) {
    $routeProvider.when(path, routeData);
  });

  $routeProvider.otherwise({ redirectTo: '/' });

});

Kiểm tra cấu hình tuyến đường với thiết lập này cũng khá dễ dàng:

describe('phoneRoutes', function() {

  it('should match route configuration', function() {

    module('myApp');

    // Mock the Phone resource
    function PhoneMock() {}
    PhoneMock.get = function() { return {}; };

    module(function($provide) {
      $provide.value('Phone', FormMock);
    });

    inject(function($route, $location, $rootScope, phoneRoutes) {
      angular.forEach(phoneRoutes, function (routeData, path) {

        $location.path(path);
        $rootScope.$digest();

        expect($route.current.templateUrl).toBe(routeData.templateUrl);
        expect($route.current.controller).toBe(routeData.controller);
      });
    });
  });
});

Bạn có thể thấy nó trong vinh quang đầy đủ trong thí nghiệm mới nhất (sắp tới) của tôi . Mặc dù phương pháp này hoạt động tốt với tôi, tôi thực sự tự hỏi tại sao người tiêm $ không trì hoãn việc xây dựng bất cứ thứ gì khi phát hiện ra việc tiêm bất cứ thứ gì là đối tượng hứa hẹn ; nó sẽ làm cho mọi thứ soooOOOOOooOOOOO dễ dàng hơn nhiều.

Chỉnh sửa: đã sử dụng Angular v1.2 (RC2)


2
Câu trả lời xuất sắc này có vẻ phù hợp hơn nhiều với triết lý "Angular" (đóng gói, v.v.). Tất cả chúng ta nên thực hiện một nỗ lực có ý thức để ngăn chặn logic leo lên khắp cơ sở mã hóa như kudzu.
zakdances

I really wonder why the $injector isn't delaying construction of anything when it detects injection of anything that is a promise objectTôi đoán họ đã bỏ qua chức năng này bởi vì nó có thể khuyến khích các mẫu thiết kế ảnh hưởng tiêu cực đến khả năng đáp ứng của các ứng dụng. Ứng dụng lý tưởng trong tâm trí của họ là một ứng dụng thực sự không đồng bộ, vì vậy việc giải quyết phải là một trường hợp cạnh.
zakdances

11

Trì hoãn hiển thị tuyến đường chắc chắn sẽ dẫn đến một mớ không đồng bộ ... tại sao không chỉ đơn giản là theo dõi trạng thái tải của thực thể chính của bạn và sử dụng nó trong chế độ xem. Ví dụ: trong bộ điều khiển của bạn, bạn có thể sử dụng cả các cuộc gọi lại thành công và lỗi trên ngResource:

$scope.httpStatus = 0; // in progress
$scope.projects = $resource.query('/projects', function() {
    $scope.httpStatus = 200;
  }, function(response) {
    $scope.httpStatus = response.status;
  });

Sau đó, trong chế độ xem, bạn có thể làm bất cứ điều gì:

<div ng-show="httpStatus == 0">
    Loading
</div>
<div ng-show="httpStatus == 200">
    Real stuff
    <div ng-repeat="project in projects">
         ...
    </div>
</div>
<div ng-show="httpStatus >= 400">
    Error, not found, etc. Could distinguish 4xx not found from 
    5xx server error even.
</div>

6
Có lẽ việc hiển thị trạng thái HTTP cho chế độ xem không đúng, còn hơn là xử lý các lớp CSS và các thành phần DOM thuộc về bộ điều khiển. Có lẽ tôi sẽ sử dụng cùng một ý tưởng nhưng trạng thái trừu tượng đi trong isValid () và isLoaded ().
jpsimons

1
Đây thực sự không phải là sự phân tách mối quan tâm tốt nhất, cộng với việc nó sẽ sụp đổ nếu bạn có các bộ điều khiển lồng nhau phụ thuộc vào đối tượng cụ thể.
null

Điều này khá thông minh .. vì để lộ mã trạng thái cho chế độ xem, bạn có thể chỉ cần dán logic http vào thuộc tính phạm vi trong bộ điều khiển, sau đó liên kết với chúng. Ngoài ra nếu bạn thực hiện nhiều cuộc gọi ajax xảy ra trong nền, bạn sẽ muốn thực hiện điều này bằng mọi cách.
KingOfHypocites 21/07/2015

Điều này sẽ ổn nếu vấn đề chỉ là trì hoãn một quan điểm. Nhưng giải quyết được sử dụng tốt nhất nếu bạn cần trì hoãn việc khởi tạo bộ điều khiển-- không chỉ là chế độ xem. (Ví dụ: Nếu bạn cần phải chắc chắn JSON của bạn được nạp bởi vì điều khiển của bạn vượt qua nó để một chỉ thị trước khi nó có dây.) Từ các tài liệu: "router sẽ chờ đợi cho họ tất cả được giải quyết hoặc một bị từ chối trước bộ điều khiển là khởi tạo ".
Dan

8

Tôi đã làm việc từ mã của Misko ở trên và đây là những gì tôi đã làm với nó. Đây là một giải pháp hiện tại hơn kể từ khi $deferđã được thay đổi thành $timeout. $timeoutTuy nhiên, việc thay thế sẽ đợi khoảng thời gian chờ (theo mã của Misko, 1 giây), sau đó trả lại dữ liệu với hy vọng nó được giải quyết kịp thời. Với cách này, nó trả về càng sớm càng tốt.

function PhoneListCtrl($scope, phones) {
  $scope.phones = phones;
  $scope.orderProp = 'age';
}

PhoneListCtrl.resolve = {

  phones: function($q, Phone) {
    var deferred = $q.defer();

    Phone.query(function(phones) {
        deferred.resolve(phones);
    });

    return deferred.promise;
  }
}

7

Sử dụng AngularJS 1.1.5

Cập nhật chức năng 'điện thoại' trong câu trả lời của Justen bằng cú pháp AngularJS 1.1.5 .

Nguyên:

phones: function($q, Phone) {
    var deferred = $q.defer();

    Phone.query(function(phones) {
        deferred.resolve(phones);
    });

    return deferred.promise;
}

Cập nhật:

phones: function(Phone) {
    return Phone.query().$promise;
}

Ngắn hơn nhiều nhờ đội ngũ Angular và những người đóng góp. :)

Đây cũng là câu trả lời của Maximilian Hoffmann. Rõ ràng cam kết đó đã biến nó thành 1.1.5.


1
Tôi dường như không thể tìm thấy bất cứ điều gì về $promisetrong các tài liệu . Nó có thể đã được gỡ bỏ kể từ phiên bản 2.0.
zakdances

Nó chỉ có sẵn trong 1,2
Thomas

5

Bạn có thể sử dụng thuộc tính giải quyết $ routeProvider để trì hoãn thay đổi tuyến đường cho đến khi dữ liệu được tải.

angular.module('app', ['ngRoute']).
  config(['$routeProvider', function($routeProvider, EntitiesCtrlResolve, EntityCtrlResolve) {
    $routeProvider.
      when('/entities', {
        templateUrl: 'entities.html', 
        controller: 'EntitiesCtrl', 
        resolve: EntitiesCtrlResolve
      }).
      when('/entity/:entityId', {
        templateUrl: 'entity.html', 
        controller: 'EntityCtrl', 
        resolve: EntityCtrlResolve
      }).
      otherwise({redirectTo: '/entities'});
}]);

Lưu ý rằng resolvetài sản được xác định trên tuyến đường.

EntitiesCtrlResolveEntityCtrlResolvelà các đối tượng không đổi được định nghĩa trong cùng một tệp như EntitiesCtrlEntityCtrlbộ điều khiển.

// EntitiesCtrl.js

angular.module('app').constant('EntitiesCtrlResolve', {
  Entities: function(EntitiesService) {
    return EntitiesService.getAll();
  }
});

angular.module('app').controller('EntitiesCtrl', function(Entities) {
  $scope.entities = Entities;

  // some code..
});

// EntityCtrl.js

angular.module('app').constant('EntityCtrlResolve', {
  Entity: function($route, EntitiesService) {
    return EntitiesService.getById($route.current.params.projectId);
  }
});

angular.module('app').controller('EntityCtrl', function(Entity) {
  $scope.entity = Entity;

  // some code..
});

3

Tôi thích ý tưởng của Darkporter vì sẽ dễ dàng cho một nhóm phát triển mới với AngularJS hiểu và làm việc ngay lập tức.

Tôi đã tạo ra sự thích ứng này sử dụng 2 div, một cho thanh tải và một cho nội dung thực tế được hiển thị sau khi dữ liệu được tải. Xử lý lỗi sẽ được thực hiện ở nơi khác.

Thêm cờ 'sẵn sàng' vào phạm vi $:

$http({method: 'GET', url: '...'}).
    success(function(data, status, headers, config) {
        $scope.dataForView = data;      
        $scope.ready = true;  // <-- set true after loaded
    })
});

Trong chế độ xem html:

<div ng-show="!ready">

    <!-- Show loading graphic, e.g. Twitter Boostrap progress bar -->
    <div class="progress progress-striped active">
        <div class="bar" style="width: 100%;"></div>
    </div>

</div>

<div ng-show="ready">

    <!-- Real content goes here and will appear after loading -->

</div>

Xem thêm: Tài liệu thanh tiến trình Boostrap


Tách ra một chút nếu bạn đang tải nhiều mẩu dữ liệu. Làm thế nào để bạn biết nếu mọi thứ được tải?
toxaq

Mọi thứ đã được chuyển từ khi tôi thêm câu trả lời này vào tháng Hai, với nhiều hoạt động hơn trên trang này. Có vẻ như có sự hỗ trợ tốt hơn trong Angular để giải quyết vấn đề này ngay bây giờ so với những gì được đề xuất ở đây. Chúc mừng,
reggoodwin

Tôi đến hơi muộn, nhưng việc xử lý nhiều mẩu dữ liệu không phải là vấn đề lớn. Bạn chỉ cần sử dụng các biến riêng biệt (booleans: isReadyData1, isReadyData2, v.v.) cho mỗi yêu cầu và đặt $ scope. Yet = isReadyData1 && isReadyData2 ...; làm việc tốt cho tôi
GuillaumeA

1

Tôi thích câu trả lời ở trên và học được rất nhiều từ chúng nhưng có một điều còn thiếu trong hầu hết các câu trả lời ở trên.

Tôi đã bị mắc kẹt trong một kịch bản tương tự khi tôi đang giải quyết url với một số dữ liệu được tìm nạp trong yêu cầu đầu tiên từ máy chủ. Vấn đề tôi gặp phải là nếu lời hứa là gì rejected.

Tôi đã sử dụng một nhà cung cấp tùy chỉnh mà sử dụng để trả về một Promisetrong đó đã được giải quyết bởi resolvecác $routeProvidertại thời điểm giai đoạn config.

Điều tôi muốn nhấn mạnh ở đây là khái niệm về whennó làm một cái gì đó như thế này.

Nó thấy url trong thanh url và sau đó whenkhối tương ứng trong bộ điều khiển được gọi và chế độ xem được giới thiệu cho đến nay rất tốt.

Hãy nói rằng tôi có mã pha cấu hình sau.

App.when('/', {
   templateUrl: '/assets/campaigns/index.html',
   controller: 'CampaignListCtr',
   resolve : {
      Auth : function(){
         return AuthServiceProvider.auth('campaign');
      }
   }
})
// Default route
.otherwise({
   redirectTo: '/segments'
});

Trên url gốc trong trình duyệt, khối chạy đầu tiên được gọi nếu không otherwiseđược gọi.

Hãy tưởng tượng một kịch bản tôi nhấn rootUrl trong AuthServicePrivider.auth()hàm thanh địa chỉ được gọi.

Hãy nói rằng Promise trở lại là trong trạng thái từ chối những gì sau đó ???

Không có gì được kết xuất cả.

Otherwise khối sẽ không được thực thi vì nó dành cho bất kỳ url nào không được xác định trong khối cấu hình và không xác định được pha cấu hình angularJs.

Chúng tôi sẽ phải xử lý sự kiện bị sa thải khi lời hứa này không được giải quyết. Thất bại $routeChangeErorrbị sa thải$rootScope .

Nó có thể được chụp như thể hiện trong mã dưới đây.

$rootScope.$on('$routeChangeError', function(event, current, previous, rejection){
    // Use params in redirection logic.
    // event is the routeChangeEvent
    // current is the current url
    // previous is the previous url
    $location.path($rootScope.rootPath);
});

IMO Nói chung, nên đặt mã theo dõi sự kiện trong khối ứng dụng. Mã này chạy ngay sau giai đoạn cấu hình của ứng dụng.

App.run(['$routeParams', '$rootScope', '$location', function($routeParams, $rootScope, $location){
   $rootScope.rootPath = "my custom path";
   // Event to listen to all the routeChangeErrors raised
   // by the resolve in config part of application
   $rootScope.$on('$routeChangeError', function(event, current, previous, rejection){
       // I am redirecting to rootPath I have set above.
       $location.path($rootScope.rootPath);
   });
}]);

Bằng cách này, chúng ta có thể xử lý thất bại tại thời điểm cấu hình.


0

Tôi đã có một giao diện bảng điều khiển trượt đa cấp phức tạp, với lớp màn hình bị vô hiệu hóa. Tạo lệnh trên lớp màn hình vô hiệu hóa sẽ tạo sự kiện nhấp để thực thi trạng thái như

$state.go('account.stream.social.view');

đã tạo ra một hiệu ứng flicking. history.back () thay vì nó hoạt động tốt, tuy nhiên nó không phải lúc nào cũng quay lại lịch sử trong trường hợp của tôi. VÌ VẬY, điều tôi tìm ra là nếu tôi chỉ đơn giản tạo thuộc tính href trên màn hình vô hiệu hóa thay vì state.go, hoạt động như một bùa mê.

<a class="disable-screen" back></a>

Chỉ thị 'trở lại'

app.directive('back', [ '$rootScope', function($rootScope) {

    return {
        restrict : 'A',
        link : function(scope, element, attrs) {
            element.attr('href', $rootScope.previousState.replace(/\./gi, '/'));
        }
    };

} ]);

app.js Tôi chỉ lưu trạng thái trước đó

app.run(function($rootScope, $state) {      

    $rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams) {         

        $rootScope.previousState = fromState.name;
        $rootScope.currentState = toState.name;


    });
});

-2

Một giải pháp khả thi có thể là sử dụng chỉ thị ng-cloak với phần tử mà chúng ta đang sử dụng các mô hình, ví dụ

<div ng-cloak="">
  Value in  myModel is: {{myModel}}
</div>

Tôi nghĩ rằng điều này cần ít nỗ lực nhất.

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.