我有这项服务,初始化品牌和实际品牌变量。
.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中未定义,因为它在服务之前加载。
为什么我可以解决这个问题? 我是角度和JavaScript的新手,也许我做了很多错事。 任何帮助,我将不胜感激,谢谢。
答案 0 :(得分:3)
SmartPointerManager.getInstance(element.getProject()).createSmartPsiElementPointer(element);
的原因实际上是因为undefined
请求的异步性质。没有什么(嗯,基本上没什么)你可以改变这种行为。您将不得不使用最常见的方式解决此问题(如果您继续使用Angular和大多数Web开发语言,这将成为一项长期的实践)并使用promise。
什么是承诺?它的在线指南很多。向您解释它的最基本方法是在异步操作返回之前不会执行promise。
https://docs.angularjs.org/api/ng/service/$q
http://andyshora.com/promises-angularjs-explained-as-cartoon.html
现在,当你$http
属性时,它可能/可能没有被加载(实际上,它可能没有),因此你得到console.log
。
此外,我对此并不十分确定,但您使用的是工厂提供商,但是您没有从提供程序功能返回任何内容,因此您正在使用它,就好像它是一项服务一样。我不能100%确定Angular是否允许这样做,但当然最好的做法是返回你想要从工厂创建的实例。
答案 1 :(得分:2)
我在角度和JavaScript方面真的很新,也许我做了很多错事。任何帮助,我将不胜感激,谢谢。
你是对的 - 你做了很多错事。让我们从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
方法的参数。它确实不将数据返回到getBrands
函数。由于return
正文中没有getBrands
语句,getBrands
函数会返回undefined
。
此外,.success
服务的$http
方法会忽略返回值。因此,$http
服务将返回一个解析为response
对象而非data
对象的promise。要返回解析为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;
})
)
}
return
函数中包含onFulfilled
语句,return
函数正文中包含getBrands
语句,getBrands
函数将返回一个承诺通过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
已弃用
$http
遗留承诺方法.success
和.error
。改为使用标准.then
方法。
我使用
$q
承诺来解决它。我在控制器中使用.then
,但我无法在工厂中使用它。如果你想检查它。并感谢您的反馈。
//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
这是classic $q.defer
Anti-Pattern。 .sucesss
和.error
方法被弃用的原因之一是,由于这些方法忽略返回值,他们会鼓励这种反模式。
这种反模式的主要问题是它打破了$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
对象抛出链以供其他拒绝处理程序使用。
有关详细信息,请参阅Angular execution order with $q
。
答案 2 :(得分:1)
我认为您遇到的问题是您的控制器正在尝试访问由$ http.get调用设置的属性。 $ http.get返回一个promise,它是对您提供的用于获取品牌的URL的异步调用。这意味着拨打电话的控制器不会等待品牌加载。
有几个选项可以解决此问题,但您只需从服务中返回承诺并在控制器中解决它。
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');
});
还有其他选项(例如延迟加载或将工厂直接放在您的示波器上),但如果您愿意,可以查看这些选项。
答案 3 :(得分:0)
我使用$ 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 Anti-Pattern。
.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