Im really new in angular and javascript, maybe Im doing lots of thing wrong. Any help I would be grateful, thank you.
You are correct -- you are doing lot's of things wrong. Let's start with the getBrands
function:
//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');
});
The return data
statement is nested inside a function which is the argument of an $http
.success
method. It does not return data to the getBrands
function. Since there is no return
statement in the getBrands
body, the getBrands
function returns undefined
.
In addition the .success
method of the $http
service ignores return values. Thus the $http
service will return a promise that resolves to a response
object instead of a data
object. To return a promise that resolves to a data
object, use the .then
method.
//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;
})
)
}
With a return
statement inside the onFulfilled
function and a return
statement in the body of the getBrands
function, the getBrands
function will return a promise that resolves fulfilled with data
(or resolves rejected with a response
object.)
In the controller, resolve the promise with the .then
and .catch
methods.
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);
});
The $http
legacy promise methods .success
and .error
have been deprecated. Use the standard .then
method instead.
UPDATE
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
This is a classic $q.defer
Anti-Pattern. One of the reasons that the .sucesss
and .error
methods were deprecated was that they encourage this anti-pattern as a result of the fact that those methods ignore return values.
The major problem with this anti-pattern is that it breaks the $http
promise chain and when error conditions aren't handled properly the promise hangs. The other problem is that often programmers fail to create a new $q.defer
on subsequent invocations of the function.
//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
This example shows how to log a rejection and throw the errorResponse
object down the chain to be used by other rejection handlers.
For more information on this, see Angular execution order with $q
.
Also, Why is angular $http
success/error being deprecated?.
And, Is this a “Deferred Antipattern”?