Angular : Weird data hierarchy when using promises - angularjs

I have a service that retrieves data using promise :
...
return {
getData : getData
}
function getData(aParam) {
$http.get('test.json').then(function (response) {
return response.data;
});
}
...
In my controller, where the service is injected, I can call my method like this :
...
var myData = MyService.getData('foobar');
...
The service is called but when I inspect the myData content, I have to access it like this :
myData.$$state.value.foobarArray
Question : Is it possible to avoid the $$state.value part ?
Regards.

I'd add this as just a comment if I had enough pointz, but try adding return before $http.get.

Your service exposes an async function so it either needs to return a promise..
function getData(aParam) {
return $http.get('test.json');
}
...and resolve the promise in the controller.
MyService.getData('foobar').then(function(data){
var myData = data;
});
Or use a callback function...
function getData(aParam, callback) {
$http.get('test.json').then(function (response) {
callback(response.data);
});
}
... passed in from the controller.
MyService.getData('foobar', function(data){
var myData = data;
});
I cant explain the 'weird hierachy' off the top of my head but this should get you headed in the right direction.

Related

Using the service data in the controller

I have placed the service and the controller in the same js file. So Im trying to fetch the data from the service and use it in my html. In my code Im able to generate the data from the service but not able to assign it to a $scope in the controller and use it in the html. So how do I get the data and assign it to the $scope so that I can use it in my html.
var app = angular.module("app",[]);
app.factory('factoryServices',function($http){
var newObject = {};
var _getChart= function(){
$http.get("http://citibikenyc.com/stations/json")
.success(function(data, status){
if(data) {
return data;
}
}).error(function(data,status){
return error;
});
}
newObject.getChart = _getChart;
return newObject;
});
app.controller("chartController",function($scope,$http,factoryServices){
factoryServices.getChart($scope.chartServicesCompleted);
$scope.chartServicesCompleted = function(data){
$scope.serviceResponse = data;
}
})
If you rewrite your code like this, it should work as expected:
var app = angular.module("app",[]);
app.factory('factoryServices',function($http){
var newObject = {};
var _getChart= function(){
return $http.get("http://citibikenyc.com/stations/json")
.then(function(response){
if(response.data) {
return response.data;
}
}, function(response){
console.error("getChart failed with ",response);
});
}
newObject.getChart = _getChart;
return newObject;
});
and your controller
app.controller("chartController",function($scope,$http,factoryServices){
factoryServices.getChart().then(chartServicesCompleted);
function chartServicesCompleted(data){
$scope.serviceResponse = data;
}
})
The reason your initial code doesn't work, is because your getChart doesn't actually take an argument. So passing your callback like this: getChart($scope.chartServicesCompleted) doesn't do anything. In the rewritten code, I've made it so the getChart function returns the promise created by $http.get(..) which then allows you to use .then([callback]) in your controller.
you are passing a callback function but not handling inside the service method.
do change as
var _getChart= function(callback){
$http.get("http://citibikenyc.com/stations/json")
.success(function(data, status){
callback(data);
}).error(function(data,status){
callback(data);
});
}
now factoryServices.getChart($scope.chartServicesCompleted); will work
you can make more generic by handling success and error callback separately.
or one more way is to implement the success and error logic inside your controller.
but do not forget to check the function type i.e
if(typeof callback == 'function'){
callback(data);
}
Edit: as per advanced you call implement promises.
if you are using angular version 1.6, the success and error methods have been depreciated.
secondly you can do inside service return the http object
var _getChart= function(){
return $http.get("http://citibikenyc.com/stations/json");
}
and then handle the promise in the controller like
factoryServices.getChart().then(successMethod, error method);
Let the service return the promise to the controller. ex:
var _getChart= function(){
return $http.get("http://citibikenyc.com/stations/json");
}
and in the controller handle the promise. Use 'then' instead
factoryServices.getChart().then(function(response){var theDate = response.data},function(error){});
you can declare methods instead in the controller for handling the success and error
factoryServices.getChart).then(onSuccess,onError);
Don use the .success method and .error method. They dont't behave as other promises. So get used to 'then'
You really don't need to handle errors in the service method.I use angular interceptors in most of the cases. Check em out. But sometimes you need to handle the error in the controller. So its good to get the callback in the controller

how to retrieve ajax response value from service?

I am using 2 service in controller
First Service is to get AjaxResponse where logic to fetching the response is mentioned
The second Service calls the first service to make Http request and get result and then, in turn, return it to the controller
Ctrl Dependency injected firstService,secondService
this.getService = secondService.getData(param);
First Service--> firstService
this.httpResponse(param){
var re = $http.get(param);
return re.then(success,fail);
}
function success(data){
return data;
}
function fail(data){
console.log(data);
}
Second Service (Dependency injection of First Service)
function secondService(firstService){
this.getData = function(param){
return firstService.httpResponse(param);
};
}
this.getService is coming as undefined, all the call are going properly.
Even tried the following code:
secondService.getData(param).then(function(data){console.log(data);});
That doesn't help either.
You should chain the promises in these kinds of situations.
First, you define your service. It should contain two distinct functions. As an example, I did a GET and a POST.
angular.module("myApp",[]).factory("SharedServices", function($http) {
return {
getItem: function() {
return $http.get('path/to/api');
},
postItem: function(payload) {
return $http.post('path/to/api', payload);
}
};
});
Then, reference the service in your controller. getItem() will return a promise, where you can use it using .then to call your second service in the success callback.
angular.module("myApp",[]).controller("MainCtrl", function($scope, SharedServices) {
SharedServices.getItem().then(function(response) {
//success, got something
//call the second part
var payload = { myPayload: response.data.someItem };
SharedServices.postItem(payload).then(function(response) {
//success
}, function(response) {
//an error has occurred to the second call--POST
});
}, function(response) {
//an error occurred to the first call--GET
});
});
Used Callback to get the result.It is similar to deferred(promise)

Angular - set controller variable after service $http call

I'm finding it difficult making sense of all the differing blogs and examples out there on how to use a promise correctly in angular, so would appreciate some clarification from someone please.
Is using a callback passed in to the service get method to set the controller variable like this wrong?
In the Session service:
self.getSessions = function(callback) {
$http.get(self.urls.sessionsList).then(
function (response) {
callback(response.data);
},
function (response) {
// failure
}
);
};
from this controller:
.controller('SessionsController', ['Session', function(Session) {
var self = this;
self.sessions = [];
Session.getSessions(function(data) {
self.sessions = data;
});
}])
Q) Is using a callback passed in to the service get method to set the controller >>variable like this wrong?.
No, it is not wrong, but you can use the power of the promise and change your code to be something like below, where you can chain to the "then" method :
self.getSessions = function() {
return $http.get(self.urls.sessionsList);
}
and change your controller code to be:
.controller('SessionsController', ['Session', function(Session) {
var self = this;
self.sessions = [];
Session.getSessions().then(function(response) {
self.sessions = response.data;
});
}]);
Then, you can see that the caller can chain the to "then" and do more and more functionality,...
hope that helps.
Use Deffered promise is bad thing since es6 promise is released

Controlling order of execution in angularjs

I have inherited an angular app and now need to make a change.
As part of this change, some data needs to be set in one controller and then used from another. So I created a service and had one controller write data into it and one controller read data out of it.
angular.module('appRoot.controllers')
.controller('pageController', function (myApiService, myService) {
// load data from API call
var data = myApiService.getData();
// Write data into service
myService.addData(data);
})
.controller('pageSubController', function (myService) {
// Read data from service
var data = myService.getData();
// Do something with data....
})
However, when I go to use data in pageSubController it is always undefined.
How can I make sure that pageController executes before pageSubController? Or is that even the right question to ask?
EDIT
My service code:
angular.module('appRoot.factories')
.factory('myService', function () {
var data = [];
var addData = function (d) {
data = d;
};
var getData = function () {
return data;
};
return {
addData: addData,
getData: getData
};
})
If you want your controller to wait untill you get a response from the other controller. You can try using $broadcast option in angularjs.
In the pagecontroller, you have to broadcast your message "dataAdded" and in the pagesubcontroller you have to wait for the message using $scope.$on and then process "getData" function.
You can try something like this :
angular.module('appRoot.controllers')
.controller('pageController', function (myApiService, myService,$rootScope) {
// load data from API call
var data = myApiService.getData();
// Write data into service
myService.addData(data);
$rootScope.$broadcast('dataAdded', data);
})
.controller('pageSubController', function (myService,$rootScope) {
// Read data from service
$scope.$on('dataAdded', function(event, data) {
var data = myService.getData();
}
// Do something with data....
})
I would change your service to return a promise for the data. When asked, if the data has not been set, just return the promise. Later when the other controller sets the data, resolve the previous promises with the data. I've used this pattern to handle caching API results in a way such that the controllers don't know or care whether I fetched data from the API or just returned cached data. Something similar to this, although you may need to keep an array of pending promises that need to be resolved when the data does actually get set.
function MyService($http, $q, $timeout) {
var factory = {};
factory.get = function getItem(itemId) {
if (!itemId) {
throw new Error('itemId is required for MyService.get');
}
var deferred = $q.defer();
if (factory.item && factory.item._id === itemId) {
$timeout(function () {
deferred.resolve(factory.item);
}, 0);
} else {
$http.get('/api/items/' + itemId).then(function (resp) {
factory.item = resp.data;
deferred.resolve(factory.item);
});
}
return deferred.promise;
};
return factory;
}

Return value of a promise to a function in AngularJS

I have a problem with outputting the values of a promise function.
$scope.getTeacher = function(teacherAbr) {
var promise = dataService.getTeacher(teacherAbr);
var testje = promise.then(function(payload) {
return payload;
});
return testje; // outputs an d object, not the direct payload values
};
The output of this controller function is this:
However, when I'm doing testje.$$state it returns undefined. How can I output the value object? I can't output the payload variable in a new $scope, because this data is dynamic.
Here is a simplified version of this on Plunker.
You should change the way you think about things when you work with asynchronous code. You no longer return values, instead you use Promise or callback patterns to invoke code when data becomes available.
In your case your factory can be:
.factory('dataService', function($http, $log, $q) {
return {
getTeacher: function(teacher) {
// Originally this was a jsonp request ('/teacher/' + teacher)
return $http.get('http://echo.jsontest.com/key/value/one/two').then(function(response) {
return response.data;
}, function() {
$log.error(msg, code);
})
}
};
});
And usage in controller:
dataService.getTeacher('Lanc').then(function(data) {
$scope.teacher = data;
});
Also $http.get already returns promise, so you don't have to create one more with $q.defer().
Demo: http://plnkr.co/edit/FNYmZg9NJR7GpjgKkWd6?p=preview
UPD
Here is another effort for combining lessons with teachers.
Demo: http://plnkr.co/edit/SXT5QaiZuiQGZe2Z6pb4?p=preview
//in your services file
return {
getTeacher: function (teacher) {
// Originally this was a jsonp request ('/teacher/' + teacher)
return $http.get('http://echo.jsontest.com/key/value/one/two')
})
//change the controller to this
dataService.getTeacher(teacherAbr).then(function(msg){
$scope.getdata=msg.data;
console.log("From server"+msg.data);
});

Resources