Способы загрузки данных в контроллер через сервис в AngularJS
у меня есть сервис, который загружает данные с помощью $ http и возвращает обещание (упрощенное для краткости):
angular.module('myApp').factory('DataService', ['$http', function($http) {
function unwrapFriendList(data) {
...
return unwrappedFriendList;
}
return {
getFriendList: function() {
return $http.get('/api/friends').then(unwrapFriendList);
}
}
}]);
вот представление, которое использует эти данные, после того, как обещание разрешено и результат хранится в $scope.friends
:
<div ng-repeat='friend in friends'>
{{friend.firstName}} {{friend.lastName}}
</div>
когда дело доходит до загрузки этих данных в контроллер, я столкнулся с несколькими способами сделать это.
Вариант 1: контроллер, который использует данные, загруженные через ng-route resolve
angular.module('myApp').controller('FriendListCtrl', ['$scope', 'friendList', function($scope, friendList) {
$scope.friends = friendList;
}]);
маршрут раздел:
angular.module('myApp', ...).config(function($routeProvider) {
$routeProvider
.when('/friends', {
templateUrl: 'views/friends.html',
controller: 'FriendListCtrl',
resolve: {
friendList: ['DataService', function(DataService) {
return DataService.getFriendList();
}]
}
})
...
});
Вариант 2: контроллер, который запускает загрузку данных сам по себе
angular.module('myApp').controller('FriendListCtrl', ['$scope', 'DataService', function($scope, DataService) {
DataService.getFriendList().then(function(friendList) {
$scope.friends = friendList;
});
}]);
вопросы
- существуют ли другие широко используемые способы сделать это? Если да, проиллюстрируйте это примером кода.
- каковы ограничения каждого подхода?
- каковы преимущества каждого подхода?
- при каких обстоятельствах я должен использовать каждый подход?
2 ответов
тестирование
Вариант 1: Использование разрешений делает насмешливые зависимости в модульных тестах контроллера очень простыми. В вашем первом варианте:
$routeProvider
.when('/friends', {
templateUrl: 'views/friends.html',
controller: 'FriendListCtrl',
resolve: {
friendList: ['DataService', function(DataService) {
return DataService.getFriendList();
}]
}
})
angular.module('myApp')
.controller('FriendListCtrl', ['$scope', 'friendList',
function($scope, friendList) {
$scope.friends = friendList;
}]);
С friendList
вводится в контроллер, издеваясь над ним в тесте так же просто, как передача простого объекта в $controller
сервис:
var friendListMock = [
// ...
];
$controller('FriendListCtrl', {
$scope: scope,
friendList: friendListMock
})
Вариант 2: Вы не можете сделать это со вторым вариантом, и вам придется шпионить за/заглушить DataService. Поскольку данные запросы данных во втором варианте немедленно вызываются при создании контроллера, тестирование будет очень запутанным, как только вы начнете делать несколько, условных или зависимых (подробнее об этом позже) запросов данных.
посмотреть инициализации
Вариант 1: Разрешения предотвращают инициализацию представления, пока не будут выполнены все разрешения. Это означает, что все в представлении, ожидающем данных (включая директивы), будет иметь его немедленно.
2: Если запросы данных происходят в контроллере, представление будет отображаться, но не будет иметь никаких данных, пока запросы не будут выполнены (что будет в какой-то неизвестной точке в будущем). Это сродни флэш-контента нестилизованный и может быть довольно раздражающим, но можно обойти.
реальные осложнения приходят, когда у вас есть компоненты в вашем представлении, ожидающие данных и не предоставляются с ним, потому что они все еще извлекаются. Затем вы должны взломать это, заставляя каждого ваших компонентов, чтобы ждать или задерживать инициализацию в течение некоторого неизвестного количества времени, или иметь их $watch
некоторая произвольная переменная перед инициализацией. Очень грязно.
предпочитаю решает
хотя вы можете выполнять начальную загрузку данных в контроллерах, решения уже делают это намного чище и декларативнее.
однако у ngroute resolver по умолчанию отсутствует несколько ключевых функций, наиболее заметным из которых является зависимое разрешение. Что делать, если вы хотите предоставить 2 фрагменты данных для вашего контроллера: клиент и детали их обычного магазина? Это не просто с ngRoute:
resolve: {
customer: function($routeParams, CustomerService) {
return CustomerService.get($routeParams.customerId);
},
usualStore: function(StoreService) {
// can't access 'customer' object here, so can't get their usual store
var storeId = ...;
return StoreService.get(storeId);
}
}
вы можете взломать это, загрузив usualStore
от контроллера после customer
вводят, но зачем, когда это можно сделать чисто в ui-router с зависимыми постановляет:
resolve: {
customer: function($stateParams, CustomerService) {
return CustomerService.get($stateParams.customerId);
},
usualStore: function(StoreService, customer) {
// this depends on the 'customer' resolve above
return StoreService.get(customer.usualStoreId);
}
}
есть ли другие часто используемые способы сделать это?
зависит, если у вас есть данные, которые находятся в другом домене, и это может занять время загрузки, поэтому вы не можете показать представление, пока оно не будет получено, поэтому вы пойдете на решение одного i.e первый.
каковы ограничения каждого подхода?
ограничение использования первого шаблона решением может быть то, что страница ничего не будет отображать, пока все данные не будут загружен
ограничение второго заключается в том, что получение данных может занять больше времени, и ваше представление будет похоже на " {{}}", Если вы не справились с ним с помощью css
каковы преимущества каждого подхода?
преимущество первого - это то, что я сказал ранее, что вы разрешите данные и убедитесь, что они присутствуют перед представлением
при каких обстоятельствах я должен использовать каждый подход?
решение очень полезно, если нам нужно загрузить некоторые данные, загруженные до инициализации контроллера и визуализации представления
а второй-когда у вас нет чеков, и эти проблемы с загрузкой ожидаются, и данные находятся в ваших руках !