在我的Angular应用程序中,我有两个控制器,它们都需要访问相同的数据。
为此,我创建了一项服务,负责持有和提供对该数据的访问:
angular.module("SomeModule").factory( "SomeService", function( $http ) {
var svc = {};
var data = {};
// on initialization, load data from the server
$http.get( "somefile.json" )
.success( function( data ) {
svc.data = data;
} );
svc.getItem = function( id ) {
// find the specified item within svc.data, and return its value
};
return svc;
} );
...我已将该服务注入两个控制器中的每一个:
angular.module("SomeModule").controller( "SomeController", function( $routeParams, SomeService ) {
var ctrl = this;
ctrl.item = null; // set an initial value
// load the item that was requested in the URL
ctrl.item = SomeService.getItem( $routeParams.id );
} );
这几乎有效 - 但它有一个很大的缺陷。如果SomeController
在SomeService.getItem()
完成加载SomeService
之前调用somefile.json
,则SomeService
将无法返回任何数据。
在实践中,如果我加载应用程序几次,一些加载将起作用(即,SomeService
将首先加载somefile.json
,控制器将根据需要显示数据,而其他负载不会( ie ,SomeController
将尝试在数据实际到之前从SomeService
检索数据加载,一切都会崩溃和燃烧。)
显然,我需要找到一些方法来推迟getItem()
的执行,直到SomeService
实际上准备好处理这些调用。但我不确定最好的方法。
我可以想到一些相当多的解决方案,例如在SomeService
中构建我自己的调用队列,以及连接一堆复杂的回调。但是必须有一个更优雅的解决方案。
我怀疑 Angular's $q service在这里有用。但是,我是承诺的新手,而且我不确定我应该在这里使用$q
(甚至我是不是正在吠叫正确的树)。
你能把我推向正确的方向吗?我非常感激。
答案 0 :(得分:2)
我建议更好地利用AngularJS的路由功能,它允许您解析依赖项以及$ http服务缓存,并相应地构建应用程序。
我认为你需要完全摆脱你的服务。
从下面的示例开始,taken straight from the Angular documentation:
phonecatApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/phones', {
templateUrl: 'partials/phone-list.html',
controller: 'PhoneListCtrl'
}).
when('/phones/:phoneId', {
templateUrl: 'partials/phone-detail.html',
controller: 'PhoneDetailCtrl'
}).
otherwise({
redirectTo: '/phones'
});
}]);
所以PhoneListCtrl和PhoneDetailCtrl都需要来自 somefile.json 的数据。我会将这些数据注入每个控制器,如下所示:
(function(){
angular.module('phonecatApp').controller('PhoneListCtrl', ['somefileJsonData', function(somefileJsonData){
this.someFileJsonData = someFileJsonData;
}]);
})();
PhoneDetailCtrl 的相同想法。
然后像这样更新您的路由:
phonecatApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/phones', {
templateUrl: 'partials/phone-list.html',
controller: 'PhoneListCtrl',
resolve:{ somefileJsonData: ['$http',function($http){
return $http.get("somefile.json", { cache: true });
}] }
}).
when('/phones/:phoneId', {
templateUrl: 'partials/phone-detail.html',
controller: 'PhoneDetailCtrl',
//same resolve
}).
otherwise({
redirectTo: '/phones'
});
}]);
这样,您可以让角度负责解决此依赖关系作为路由过程的一部分。
将缓存设置为true也会缓存它,因此您不会两次执行相同的Get请求,而Angular只会在解析依赖关系时显示您的视图。
因此,在您的应用中, SomeController 与路由过程中的视图配对,请使用解决来解析项,并将其注入控制器。
答案 1 :(得分:2)
试试这段代码
angular.module("SomeModule").factory("SomeService", function ($http) {
var svc = {};
svc.getList = function () {
return $http.get("somefile.json");
};
svc.getItem = function (id) {
svc.getList().then(function (response) {
// find the specified item within response, and return its value
});
};
return svc;
});
答案 2 :(得分:1)
以下是我在自己项目中的表现。
您的服务
angular.module("SomeModule").factory( "SomeService", function( $http ) {
var svc = {};
svc.data = {};
// on initialization, load data from the server
svc.getData = function(){
return $http.get( "somefile.json" );
};
return svc;
} );
您的控制器
angular.module("SomeModule").controller( "SomeController", function( $routeParams, SomeService ) {
ctrl.items = null; // set an initial value
// load the item that was requested in the URL
SomeService.getData().success(function(data){
ctrl.items = data;
}).error(function(response){
console.err("damn");
});
} );
重点:承诺
在我看来,处理异步调用的责任归于控制器。我随时都会返回$ http promiss。
svc.getData = function(){
return $http.get( "somefile.json" );
};
您可以在服务中添加一些逻辑,但始终必须返回承诺。 (要知道:。promise上的.success()返回承诺)
控制器将具有根据异步调用的响应知道如何操作的逻辑。他必须知道如果在成功和出现错误的情况下如何表现。
如果您有更多问题可以随意提问。我希望它对你有帮助。
答案 3 :(得分:0)
是你可以选择的两个不错的选择
使用回调
使用$ q return promise
使用回调:
svc.getItem = function( id,callback ) {
$http.get( "somefile.json" )
.success( function( data ) {
svc.data = data;
callback(svc.data)
} );
};
控制器中的
SomeService.getItem( $routeParams.id,function(data){
ctrl.item = data
} );
使用承诺:
svc.getItem = function( id) {
var deferred = $q.defer();
$http.get( "somefile.json" )
.success( function( data ) {
svc.data = data;
deferred.resolve(svc.data);
} )
.error(function (error) {
deferred.reject(error);
});
return deferred.promise;
;
};
控制器中的
SomeService.getItem( $routeParams.id).then(function (data) {
ctrl.item = data
},
function (error) {
//do something with error
});
答案 4 :(得分:0)
我们是如何做到的,我们使用$ q来推迟异步调用将提供服务的任何内容,然后我将响应的数据部分解析并解析它,它将所需的数据发送到控制器(没有状态,标题......)。
我在我的服务中使用try catch语句,以防止错误处理远离控制器。
angular.module("JobsService", [])
.factory("JobsService", ['$q', '$http', '$log', function ($q, $http, $log) {
var serviceName = "JobsService";
var url = "http://localhost:8080/path/to/resource/";
var service = {};
service.getAll = function () {
var deferred = $q.defer();
try {
$http.get(url + "/jobs")
.success(function (response, status) {
$log.debug("GET response in " + serviceName + " returned with status " + status);
deferred.resolve(response);
})
.error(function (error, status) {
deferred.reject(error + " : " + status);
});
} catch (err) {
$log.error(err);
deferred.reject();
}
return deferred.promise;
};
return service;
}]);
然后在控制器
JobsService.getAll()
.then(function (response) {
$scope.jobs = response;
// records are stored in $scope.jobs
}, function (response) {
$scope.jobs = undefined;
})
.finally(function () {
// will always run
});