Http 服务工厂 Returns 未定义到 Angular 中的控制器
Http Service Factory Returns Undefined to Controller in Angular
我有这项服务,可以初始化品牌和 actualBrand 变量。
.factory('brandsFactory', ['$http', 'ENV', function brandsFactory($http, ENV){
var actualBrand = {};
var brands = getBrands(); //Also set the new actualBrand by default
function getBrands(){
var URL;
console.log('MOCKS enable? ' + ENV.mocksEnable);
if(ENV.mocksEnable){
URL = ENV.apiMock + ENV.getBrandsMock;
}else{
URL = ENV.apiURL + ENV.getBrands;
}
$http.get(URL).
success(function(data){
console.log('data is :' + data);
actualBrand = data[0];
console.log('Actual brand is ' + actualBrand);
return data;
}).
error(function(){
console.log('Error in BrandsController');
});
}
var setActualBrand = function(activeBrand) {
actualBrand = activeBrand;
};
var isSetAsActualBrand = function(checkBrand) {
return actualBrand === checkBrand;
};
return {
brands : brands,
actualBrand : actualBrand
};
我的控制器看起来像这样:
/* BrandController to manage the html content of a single brand */
.controller('BrandCController', function(brandsFactory, $scope){
$scope.brands = brandsFactory.brands;
console.log('brands in controller is ' + $scope.brands + ' -- ' + brandsFactory.brands);
});
控制器中的问题是它在 brandsFactory.brands 中未定义,因为它在服务之前加载。
我为什么要解决这个问题?
我真的是 angular 和 javascript 的新手,也许我做错了很多事。
任何帮助,我将不胜感激,谢谢。
之所以是undefined
其实是因为$http
请求的异步性。没有什么(好吧,基本上没有)可以改变这种行为。您将不得不使用最常见的方式(如果您继续使用 Angular 和大多数 Web 开发语言,这将成为一种长期的做法)和承诺来解决这个问题。
什么是承诺?关于它是什么,网上有很多指南。最基本的解释方式就是一个promise要到异步操作returns.
才会执行
https://docs.angularjs.org/api/ng/service/$q
http://andyshora.com/promises-angularjs-explained-as-cartoon.html
现在,当你 console.log
属性 时,它 may/may 还没有被加载(事实上,它可能还没有)因此你得到 undefined
.
此外,我对此不太确定,但是您正在使用工厂提供程序,但您没有 return 提供程序功能中的任何内容,因此您将其用作服务.我不是 100% 确定 Angular 是否允许这样做,但当然最好的做法是 return 你想从工厂创建的实例。
我认为您遇到的问题是您的控制器正在尝试访问由 $http.get 调用设置的 属性。 $http.get return 是一个承诺,它是对您提供的 URL 的异步调用以获取您的品牌。这意味着发出调用的控制器不会等待加载品牌。
有几个选项可以解决这个问题,但您可以简单地 return 服务的承诺并在控制器中解决它。
function getBrands(){
var URL;
console.log('MOCKS enable? ' + ENV.mocksEnable);
if(ENV.mocksEnable){
URL = ENV.apiMock + ENV.getBrandsMock;
}else{
URL = ENV.apiURL + ENV.getBrands;
}
return $http.get(URL);
});
然后在您的控制器中,您可以将成功和错误函数按原样放置...
brandsFactory.brands().then(function(response){
$scope.brand = response.data;
$scope.actualBrand = response.data[0];
}, function(err){
console.log('Error in brands factory');
});
还有其他选项(例如延迟加载或将工厂直接放在范围内),但如果您愿意,可以查看这些选项。
Im really new in angular and javascript, maybe Im doing lots of thing wrong. Any help I would be grateful, thank you.
你是对的——你做错了很多事。让我们从 getBrands
函数开始:
//Erroneously constructed function returns undefined
//
function getBrands(){
var URL;
console.log('MOCKS enable? ' + ENV.mocksEnable);
if(ENV.mocksEnable){
URL = ENV.apiMock + ENV.getBrandsMock;
}else{
URL = ENV.apiURL + ENV.getBrands;
}
$http.get(URL).
success(function(data){
console.log('data is :' + data);
actualBrand = data[0];
console.log('Actual brand is ' + actualBrand);
//SUCCESS METHOD IGNORES RETURN STATEMENTS
return data;
}).
error(function(){
console.log('Error in BrandsController');
});
return data
语句 嵌套在 函数中,该函数是 $http
.success
方法的参数。它 而不是 return 数据到 getBrands
函数。由于 getBrands
主体中没有 return
语句,因此 getBrands
函数 returns undefined
.
此外 $http
服务的 .success
方法 忽略 return 值。因此 $http
服务将 return 一个解析为 response
对象而不是 data
对象的承诺。要 return 解析为 data
对象的承诺,请使用 .then
方法。
//Correctly constructed function that returns a promise
//that resolves to a data object
//
function getBrands(){
//return the promise
return (
$http.get(URL)
//Use .then method
.then (function onFulfilled(response){
//data is property of response object
var data = response.data;
console.log('data is :' + data);
var actualBrand = data[0];
console.log('Actual brand is ' + actualBrand);
//return data to create data promise
return data;
})
)
}
在 onFulfilled
函数中使用 return
语句,在 getBrands
函数主体中使用 return
语句,getBrands
函数将 return 用 data
解决的承诺(或用 response
对象解决拒绝的承诺。)
在控制器中,使用 .then
和 .catch
方法解析承诺。
brandsFactory.getBrands().then(function onFulfilled(data){
$scope.brand = data;
$scope.actualBrand = data[0];
}).catch( function onRejected(errorResponse){
console.log('Error in brands factory');
console.log('status: ', errorResponse.status);
});
Deprecation Notice1
The $http
legacy promise methods .success
and .error
have been deprecated. Use the standard .then
method instead.
更新
I use $q
promise to solve it. I use the .then
in the controller, but I couldn't make it work in the factory. If you want to check it. And thanks for the feedback.
//Classic `$q.defer` Anti-Pattern
//
var getBrands = function(){
var defered = $q.defer();
var promise = defered.promise;
$http.get(URL)
.success(function(data){
defered.resolve(data);
})
.error(function(err){
console.log('Error in BrandsController');
defered.reject(err);
});
return promise;
};//End getBrands
这是一个。 .sucesss
和 .error
方法被弃用的原因之一是它们鼓励这种反模式,因为这些方法 忽略 return 值.
此反模式的主要问题是它破坏了 $http
承诺链,并且当错误条件未正确处理时,承诺会挂起。另一个问题是程序员经常无法在后续调用函数时创建新的 $q.defer
。
//Same function avoiding $q.defer
//
var getBrands = function(){
//return derived promise
return (
$http.get(URL)
.then(function onFulfilled(response){
var data = response.data;
//return data for chaining
return data;
}).catch(function onRejected(errorResponse){
console.log('Error in BrandsController');
console.log('Status: ', errorResponse.status;
//throw value to chain rejection
throw errorResponse;
})
)
};//End getBrands
此示例说明如何记录拒绝并将 errorResponse
对象扔到链中以供其他拒绝处理程序使用。
有关这方面的更多信息,请参阅 。
另外,.
并且,
我用 $q promise 解决了。
.factory('brandsFactory', ['$http', '$q', 'ENV', function brandsFactory($http, $q, ENV){
var actualBrand = {};
var getBrands = function(){
var URL;
var defered = $q.defer();
var promise = defered.promise;
if(ENV.mocksEnable){
URL = ENV.apiMock + ENV.getBrandsMock;
}else{
URL = ENV.apiURL + ENV.getBrands;
}
$http.get(URL)
.success(function(data){
actualBrand = data[0];
defered.resolve(data);
})
.error(function(err){
console.log('Error in BrandsController');
defered.reject(err);
});
return promise;
};//End getBrands
var setActualBrand = function(activeBrand) {
actualBrand = activeBrand;
};
var isSetAsActualBrand = function(checkBrand) {
return actualBrand === checkBrand;
};
return {
setActualBrand : setActualBrand,
getBrands : getBrands
};
}])//End Factory
在我的控制器中:
/* BrandController to manage the html content of a single brand */
.controller('BrandCController', function(brandsFactory, $scope){
$scope.brands = [];
$scope.actualBrand = {};
$scope.setActualBrand = function(brand){
brandsFactory.setActualBrand(brand);
$scope.actualBrand = brand;
};
brandsFactory.getBrands()
.then(function(data){
$scope.brands = data;
$scope.actualBrand = data[0];
})
.catch(function(errorResponse){
console.log('Error in brands factory, status : ', errorResponse.status);
});
});//End controller
如果我可以改进我的答案,请告诉我。我使用它从以前的答案中获得的所有信息。谢谢大家的帮助。
更新
正在移除我工厂中的 $q.defer 。
.factory('brandsFactory', ['$http', '$q', 'ENV', function brandsFactory($http, $q, ENV){
var actualBrand = {};
var getBrands = function(){
var URL;
if(ENV.mocksEnable){
URL = ENV.apiMock + ENV.getBrandsMock;
}else{
URL = ENV.apiURL + ENV.getBrands;
}
return (
$http.get(URL)
.then(function onFulfilled(response){
var data = response.data;
//return data for chaining
return data;
}).catch(function onRejected(errorResponse){
console.log('Error in BrandsController');
console.log('Status: ', errorResponse.status);
return errorResponse;
})
);
};//End getBrands
var setActualBrand = function(activeBrand) {
actualBrand = activeBrand;
};
return {
setActualBrand : setActualBrand,
getBrands : getBrands
};
}]);//End Factory
我有这项服务,可以初始化品牌和 actualBrand 变量。
.factory('brandsFactory', ['$http', 'ENV', function brandsFactory($http, ENV){
var actualBrand = {};
var brands = getBrands(); //Also set the new actualBrand by default
function getBrands(){
var URL;
console.log('MOCKS enable? ' + ENV.mocksEnable);
if(ENV.mocksEnable){
URL = ENV.apiMock + ENV.getBrandsMock;
}else{
URL = ENV.apiURL + ENV.getBrands;
}
$http.get(URL).
success(function(data){
console.log('data is :' + data);
actualBrand = data[0];
console.log('Actual brand is ' + actualBrand);
return data;
}).
error(function(){
console.log('Error in BrandsController');
});
}
var setActualBrand = function(activeBrand) {
actualBrand = activeBrand;
};
var isSetAsActualBrand = function(checkBrand) {
return actualBrand === checkBrand;
};
return {
brands : brands,
actualBrand : actualBrand
};
我的控制器看起来像这样:
/* BrandController to manage the html content of a single brand */
.controller('BrandCController', function(brandsFactory, $scope){
$scope.brands = brandsFactory.brands;
console.log('brands in controller is ' + $scope.brands + ' -- ' + brandsFactory.brands);
});
控制器中的问题是它在 brandsFactory.brands 中未定义,因为它在服务之前加载。
我为什么要解决这个问题? 我真的是 angular 和 javascript 的新手,也许我做错了很多事。 任何帮助,我将不胜感激,谢谢。
之所以是undefined
其实是因为$http
请求的异步性。没有什么(好吧,基本上没有)可以改变这种行为。您将不得不使用最常见的方式(如果您继续使用 Angular 和大多数 Web 开发语言,这将成为一种长期的做法)和承诺来解决这个问题。
什么是承诺?关于它是什么,网上有很多指南。最基本的解释方式就是一个promise要到异步操作returns.
才会执行https://docs.angularjs.org/api/ng/service/$q
http://andyshora.com/promises-angularjs-explained-as-cartoon.html
现在,当你 console.log
属性 时,它 may/may 还没有被加载(事实上,它可能还没有)因此你得到 undefined
.
此外,我对此不太确定,但是您正在使用工厂提供程序,但您没有 return 提供程序功能中的任何内容,因此您将其用作服务.我不是 100% 确定 Angular 是否允许这样做,但当然最好的做法是 return 你想从工厂创建的实例。
我认为您遇到的问题是您的控制器正在尝试访问由 $http.get 调用设置的 属性。 $http.get return 是一个承诺,它是对您提供的 URL 的异步调用以获取您的品牌。这意味着发出调用的控制器不会等待加载品牌。
有几个选项可以解决这个问题,但您可以简单地 return 服务的承诺并在控制器中解决它。
function getBrands(){
var URL;
console.log('MOCKS enable? ' + ENV.mocksEnable);
if(ENV.mocksEnable){
URL = ENV.apiMock + ENV.getBrandsMock;
}else{
URL = ENV.apiURL + ENV.getBrands;
}
return $http.get(URL);
});
然后在您的控制器中,您可以将成功和错误函数按原样放置...
brandsFactory.brands().then(function(response){
$scope.brand = response.data;
$scope.actualBrand = response.data[0];
}, function(err){
console.log('Error in brands factory');
});
还有其他选项(例如延迟加载或将工厂直接放在范围内),但如果您愿意,可以查看这些选项。
Im really new in angular and javascript, maybe Im doing lots of thing wrong. Any help I would be grateful, thank you.
你是对的——你做错了很多事。让我们从 getBrands
函数开始:
//Erroneously constructed function returns undefined
//
function getBrands(){
var URL;
console.log('MOCKS enable? ' + ENV.mocksEnable);
if(ENV.mocksEnable){
URL = ENV.apiMock + ENV.getBrandsMock;
}else{
URL = ENV.apiURL + ENV.getBrands;
}
$http.get(URL).
success(function(data){
console.log('data is :' + data);
actualBrand = data[0];
console.log('Actual brand is ' + actualBrand);
//SUCCESS METHOD IGNORES RETURN STATEMENTS
return data;
}).
error(function(){
console.log('Error in BrandsController');
});
return data
语句 嵌套在 函数中,该函数是 $http
.success
方法的参数。它 而不是 return 数据到 getBrands
函数。由于 getBrands
主体中没有 return
语句,因此 getBrands
函数 returns undefined
.
此外 $http
服务的 .success
方法 忽略 return 值。因此 $http
服务将 return 一个解析为 response
对象而不是 data
对象的承诺。要 return 解析为 data
对象的承诺,请使用 .then
方法。
//Correctly constructed function that returns a promise
//that resolves to a data object
//
function getBrands(){
//return the promise
return (
$http.get(URL)
//Use .then method
.then (function onFulfilled(response){
//data is property of response object
var data = response.data;
console.log('data is :' + data);
var actualBrand = data[0];
console.log('Actual brand is ' + actualBrand);
//return data to create data promise
return data;
})
)
}
在 onFulfilled
函数中使用 return
语句,在 getBrands
函数主体中使用 return
语句,getBrands
函数将 return 用 data
解决的承诺(或用 response
对象解决拒绝的承诺。)
在控制器中,使用 .then
和 .catch
方法解析承诺。
brandsFactory.getBrands().then(function onFulfilled(data){
$scope.brand = data;
$scope.actualBrand = data[0];
}).catch( function onRejected(errorResponse){
console.log('Error in brands factory');
console.log('status: ', errorResponse.status);
});
Deprecation Notice1
The
$http
legacy promise methods.success
and.error
have been deprecated. Use the standard.then
method instead.
更新
I use
$q
promise to solve it. I use the.then
in the controller, but I couldn't make it work in the factory. If you want to check it. And thanks for the feedback.
//Classic `$q.defer` Anti-Pattern
//
var getBrands = function(){
var defered = $q.defer();
var promise = defered.promise;
$http.get(URL)
.success(function(data){
defered.resolve(data);
})
.error(function(err){
console.log('Error in BrandsController');
defered.reject(err);
});
return promise;
};//End getBrands
这是一个.sucesss
和 .error
方法被弃用的原因之一是它们鼓励这种反模式,因为这些方法 忽略 return 值.
此反模式的主要问题是它破坏了 $http
承诺链,并且当错误条件未正确处理时,承诺会挂起。另一个问题是程序员经常无法在后续调用函数时创建新的 $q.defer
。
//Same function avoiding $q.defer
//
var getBrands = function(){
//return derived promise
return (
$http.get(URL)
.then(function onFulfilled(response){
var data = response.data;
//return data for chaining
return data;
}).catch(function onRejected(errorResponse){
console.log('Error in BrandsController');
console.log('Status: ', errorResponse.status;
//throw value to chain rejection
throw errorResponse;
})
)
};//End getBrands
此示例说明如何记录拒绝并将 errorResponse
对象扔到链中以供其他拒绝处理程序使用。
有关这方面的更多信息,请参阅
另外,
并且,
我用 $q promise 解决了。
.factory('brandsFactory', ['$http', '$q', 'ENV', function brandsFactory($http, $q, ENV){
var actualBrand = {};
var getBrands = function(){
var URL;
var defered = $q.defer();
var promise = defered.promise;
if(ENV.mocksEnable){
URL = ENV.apiMock + ENV.getBrandsMock;
}else{
URL = ENV.apiURL + ENV.getBrands;
}
$http.get(URL)
.success(function(data){
actualBrand = data[0];
defered.resolve(data);
})
.error(function(err){
console.log('Error in BrandsController');
defered.reject(err);
});
return promise;
};//End getBrands
var setActualBrand = function(activeBrand) {
actualBrand = activeBrand;
};
var isSetAsActualBrand = function(checkBrand) {
return actualBrand === checkBrand;
};
return {
setActualBrand : setActualBrand,
getBrands : getBrands
};
}])//End Factory
在我的控制器中:
/* BrandController to manage the html content of a single brand */
.controller('BrandCController', function(brandsFactory, $scope){
$scope.brands = [];
$scope.actualBrand = {};
$scope.setActualBrand = function(brand){
brandsFactory.setActualBrand(brand);
$scope.actualBrand = brand;
};
brandsFactory.getBrands()
.then(function(data){
$scope.brands = data;
$scope.actualBrand = data[0];
})
.catch(function(errorResponse){
console.log('Error in brands factory, status : ', errorResponse.status);
});
});//End controller
如果我可以改进我的答案,请告诉我。我使用它从以前的答案中获得的所有信息。谢谢大家的帮助。
更新
正在移除我工厂中的 $q.defer
.factory('brandsFactory', ['$http', '$q', 'ENV', function brandsFactory($http, $q, ENV){
var actualBrand = {};
var getBrands = function(){
var URL;
if(ENV.mocksEnable){
URL = ENV.apiMock + ENV.getBrandsMock;
}else{
URL = ENV.apiURL + ENV.getBrands;
}
return (
$http.get(URL)
.then(function onFulfilled(response){
var data = response.data;
//return data for chaining
return data;
}).catch(function onRejected(errorResponse){
console.log('Error in BrandsController');
console.log('Status: ', errorResponse.status);
return errorResponse;
})
);
};//End getBrands
var setActualBrand = function(activeBrand) {
actualBrand = activeBrand;
};
return {
setActualBrand : setActualBrand,
getBrands : getBrands
};
}]);//End Factory