I am new to angular and pardon my ignorance if any. I am trying to create a simple service that will do a get funtionality and serve data into array.
The problem i am having is, no mattter what i do - i always get the same data for any parameter i pass.
Here is my sample service
function myService($http, $q) {
var service = {
getSomeData: getSomeData:
};
var def = $q.defer();
return service;
function getSomeData(category) {
if (category === 'books') {
url = 'http://www.someurl1';
} else {
url = 'http://www.someurl2'
};
$http.get(url, {
params: {
'type': category
}
}).success(function(data) {
def.resolve(data);
}).error(function() {
def.reject('Failed to get data');
});
return def.promise;
}
}
})();
Once i have this, in my controller, i am trying to call it for sample purposes like this
$scope.someData = [] ;
$scope.someData.push(myService.getSomeData('DVDs');
$scope.someData.push(myService.getSomeData('books');
Now when i my look at my $scope.someData,
i have an array of two objects - the problem being that they are always the same and doesnt have data specific to books and dvds.
Another minor issue I have is my object someData has
array --> Promise -- > $$state --> value
which then has the actual data. how can i get the data directly.
i tried return def.promise(data);
One problem is you are only creating one promise outside the getSomeData function.
A promise can only be resolved once. You need a new promise for each request.
Also $http already returns a promise but you can't push it directly into an array as data.
your code should look more like:
Service:
function myService($http, $q) {
var service = {
getSomeData: getSomeData
};
return service;
function getSomeData(category) {
if (category === 'books') {
url = 'http://www.someurl1';
} else {
url = 'http://www.someurl2'
};
return $http.get(url, {
params: {
'type': category
}
}).then(function(response) {
return response.data;
}).catch(function(err) {
console.log("Ooops", err)
});
}
}
Controller
$scope.someData = [] ;
myService.getSomeData('DVDs').then(function(data){
data.forEach(function(item){
$scope.someData.push(item);
});
});
I am trying to handle restangular calls entirely from a service to keep the controller light, and also so I can manipulate the data further within the service later.
Struggling with promises, which I think is my issue. If I can avoid it, I dont want the service to return the promise, and then use .then() in the controller.
If I make the restangular call directly from the controller it works fine.
angular.module('vehicle', ['restangular'])
.config(
function(RestangularProvider) {
RestangularProvider.setBaseUrl('https://jsonplaceholder.typicode.com/');
RestangularProvider.setResponseExtractor(function(response, operation, what) {
if (operation === 'getList' && !Array.isArray(response)) {
return [response];
}
return response;
});
})
.controller('VehicleController', function($scope, VehicleService, Restangular) {
$scope.vehicles = VehicleService.getVehicles();
Restangular.all('posts').getList().then(function(vehicle) {
$scope.moreVehicles = vehicle.plain();
});
})
.service('VehicleService', function(Restangular) {
VehicleService = {};
VehicleService.vehicles = [];
VehicleService.getVehicles = function() {
Restangular.all('posts').getList().then(function(vehicle) {
VehicleService.vehicles = vehicle.plain();
return VehicleService.vehicles;
});
}
return VehicleService;
});
https://plnkr.co/edit/q1cNw6gN12pKsiPaD1o0?p=preview
Any ideas why my combination of service and promise wont return the data to scope?
I have the following controllers which all the ngResource services to get data.
.controller('venueCtrl', function ($scope, $stateParams, VenueService) {
$scope.venue = VenueService.get({ id: $stateParams.id });
})
.controller('tomorrowCtrl', function ($scope, EventService) {
var evts = EventService.query({ period: "Tomorrow" });
evts.$promise.then(function (response) { $scope.events = response; });
}).....
Now I need to add error handling (for example, display an alert box) for the error situations, e.g., no network, web service failed, etc. How to add the code to handle the errors?
You can use Interceptors this way:
angular.module('ngRessourceErrorsHandler', [])
.config(function($resourceProvider) {
angular.forEach($resourceProvider.defaults.actions, function(action) {
action['interceptor'] = {
responseError: function(httpResponse) {
//Do whatever you want here !
}
};
})
});
Please try this and let me know if this works for you. Don't forget to add ngRessourceErrorsHandler dependency to your module or just use config directly.
I don't know if this is even one of the Angular concepts or possible to do but i have a service that call the user information (name, id, age, ...):
.factory('me', function($resource, API_URL, $q) {
return {
getUser: function() {
var deferred = $q.defer();
var url = API_URL + 'api/me';
$resource(url)
.get(function(user) {
deferred.resolve(user);
}, function(response) {
deferred.reject(response);
});
return deferred.promise;
}
};
})
I use this service in many controllers to get the user data and use it to send with other http calls. for example in my newItemCtrl
.controller('newItemCtrl', function($scope, $http, API_URL, me) {
i called the me service as i did in many other controllers
and am wondering is there a way to call this service only once and use it in all the controllers instead of x times in each controller
I do something similar all the time with services by setting the value to the service once it's returned the first time. Now once the data is set in the service it will return the stored user data instead of making a request to your server.
service('myService', function($q, $http) {
this.data;
var self = this;
this.getMyData = function() {
if (angular.isDefined(self.data)) {
// use $q to return a promise
return $q.when(self.data)
}
return $http.get('myurl').then(function(resp) {
self.data = resp;
})
}
}
In your controller you can call myService.getMyData()
//Assume Value1 and Service1 have been correctly injected
if (Value1.data === null) {
Your_Var = Service1.call;
} else {
Your_Var = Value1.data;
}
For simplicity sake I would put that in a ternary expression when implementing.
For DRY compliance.
Going with DRY we need a SharedFactoryFuncion to handle $http requests
Your_Var = Service1.call;
//Inside Service1.call
return Value1.data === undefined ? SharedFactoryFuncion.get("url") : Value1.data;
There are numerous ways to handle the return of data from .get() so I won't go there as it's not pertinent.
Is there a way to return an HttpPromise (or something similar) to mimic a call to $http? I want to set a global variable that indicates whether the real HTTP request is made or whether a fake HttpPromise object is returned with fake data.
For example, I have a service that is similar to this:
angular
.module('myservice')
.factory('MyService', ['$http', function($http) {
return {
get : function(itemId) {
if (isInTestingMode) {
// return a promise obj that returns success and fake data
}
return $http.get("/myapp/items/" + itemId);
}
};
} ]);
And in my controller, I have a call to the aforementioned service that looks similar to this:
// Somewhere in my controller
MyService.get($scope.itemId)
.success(function(data) {
$scope.item = data;
})
.error(function(data, status, headers, config) {
$scope.notFound = true;
});
I'm trying to not change the controller code; I want the success and error chaining to still work when in my "isInTestMode".
Is it possible to fake an HttpPromise in the way that I described in the service?
Below is a revised edition of the "MyService" above (a snippet) containing a success and error on the promise object. But, how do I execute the success method?
return {
get : function(itemId) {
if (isInTestingMode) {
var promise = $.defer().promise;
// Mimicking $http.get's success
promise.success = function(fn) {
promise.then(function() {
fn({ itemId : "123", name : "ItemName"}, 200, {}, {});
});
return promise;
};
// Mimicking $http.get's error
promise.error = function(fn) {
promise.then(null, function(response) {
fn("Error", 404, {}, {});
});
return promise;
};
return promise;
}
return $http.get("/myapp/items/" + itemId);
}
}
Just use the deferred method of the $qservice
var fakeHttpCall = function(isSuccessful) {
var deferred = $q.defer()
if (isSuccessful === true) {
deferred.resolve("Successfully resolved the fake $http call")
}
else {
deferred.reject("Oh no! Something went terribly wrong in your fake $http call")
}
return deferred.promise
}
And then you can call your function like an $http promise (you have to customize whatever you want to put inside of it, of course).
fakeHttpCall(true).then(
function (data) {
// success callback
console.log(data)
},
function (err) {
// error callback
console.log(err)
})
I found that this post is similar to what I was asking.
However, I wanted a way to mock my service call so that fake data could be returned instead of issuing a true HTTP request call. The best way to handle this situation, for me, is to use angular's $httpBackend service. For example, to bypass a GET request to my "items" resource BUT to not bypass GETs of my partials/templates I would do something like this:
angular
.module('myApp', ['ngMockE2E'])
.run(['$httpBackend', function($httpBackend) {
$httpBackend
.whenGET(/^partials\/.+/)
.passThrough();
$httpBackend
.whenGET(/^\/myapp\/items\/.+/)
.respond({itemId : "123", name : "ItemName"});
}]);
See this documentation for more information on $httpBackend.
I finally found a way using jasmin. $httpBackend was no option for me, as there were also non-$http-methods I needed mock on the same service. I also think that the controller test needing to specify the url is not perfect as imho the controller and its test should not need to know about it.
Here is how it works:
beforeEach(inject(function ($controller, $rootScope, $q) {
scope = $rootScope.$new();
mockSvc = {
someFn: function () {
},
someHttpFn: function () {
}
};
// use jasmin to fake $http promise response
spyOn(mockSvc, 'someHttpFn').and.callFake(function () {
return {
success: function (callback) {
callback({
// some fake response
});
},
then: function(callback) {
callback({
// some fake response, you probably would want that to be
// the same as for success
});
},
error: function(callback){
callback({
// some fake response
});
}
}
});
MyCtrl = $controller('MyCtrl', {
$scope: scope,
MyActualSvc: mockSvc
});
}));
You can implement your FakeHttp class:
var FakeHttp = function (promise) {
this.promise = promise;
this.onSuccess = function(){};
this.onError = function(){};
this.premise.then(this.onSuccess, this.onError);
};
FakeHttp.prototype.success = function (callback) {
this.onSuccess = callback;
/**You need this to avoid calling previous tasks**/
this.promise.$$state.pending = null;
this.promise.then(this.onSucess, this.onError);
return this;
};
FakeHttp.prototype.error = function (callback) {
this.onError = callback;
/**You need this to avoid calling previous tasks**/
this.promise.$$state.pending = null;
this.promise.then(this.onSuccess, this.onError);
return this;
};
Then in your code, you would return a new fakeHttp out of the promise.
if(testingMode){
return new FakeHttp(promise);
};
The promise must be asynchronous, otherwise it won't work. For that you can use $timeout.
easy peasy!
You can do it using angular-mocks-async like so:
var app = ng.module( 'mockApp', [
'ngMockE2E',
'ngMockE2EAsync'
]);
app.run( [ '$httpBackend', '$q', function( $httpBackend, $q ) {
$httpBackend.whenAsync(
'GET',
new RegExp( 'http://api.example.com/user/.+$' )
).respond( function( method, url, data, config ) {
var re = /.*\/user\/(\w+)/;
var userId = parseInt(url.replace(re, '$1'), 10);
var response = $q.defer();
setTimeout( function() {
var data = {
userId: userId
};
response.resolve( [ 200, "mock response", data ] );
}, 1000 );
return response.promise;
});
}]);