I'm using a deferred promise because I need the app to wait for the result of an asynchronous call. The function retrieves the correct data, but I can't seem to get that back to my controller.
Specifically, the line of code
data : multiBarChartData
is not getting the resolved data from
function multiBarChartData()
Here is the factory:
angular.module('hapDash.services', []).factory('DataService', function ($http, $q, SESSION_ID) {
return {
multiBarChart : {
data : multiBarChartData
}
};
function multiBarChartData() {
var deferred = $q.defer();
$http({
method : 'GET',
url : '/services/data/v37.0/analytics/reports/00OP0000000Q9iB',
headers : {
'Authorization' : 'Bearer ' + SESSION_ID
}
}).success(function (data) {
var report02ChartData = [];
angular.forEach(data.groupingsDown.groupings, function (de, di) {
var report02Values = [];
report02ChartData.push({'key' : de.label, 'values' : report02Values});
angular.forEach(data.groupingsAcross.groupings, function (ae, ai) {
report02Values.push({'x' : ae.label, 'y' : data.factMap[de.key + '!' + ae.key].aggregates[0].value});
});
});
data = report02ChartData;
deferred.resolve(data);
}).error(function () {
deferred.reject('There was an error accessing the Analytics API')
})
return deferred.promise;
}
});
... and here is the controller:
var app = angular.module('hapDash', ['config', 'nvd3', 'gridster', 'hapDash.services']);
app.controller('HapDashCtrl', function ($scope, $timeout, DataService) {
$scope.dashboard = {
widgets : [{
col : 0,
row : 0,
sizeY : 1,
sizeX : 1,
name : "Multi Bar Chart",
chart : {
data : DataService.multiBarChart.data(),
api : {}
}
}
]
};
});
I'm trying to get the controller code
data : DataService.multiBarChart.data(),
to pull the async response once it is complete.
What am I doing wrong?
DataService.multiBarChart.data()
returns a promise and not the real data. You can access the real data in the controller via:
DataService.multiBarChart.data().then(
function(data) {
/*do something with data*/
}
).catch(
function(err) {
/*handle error*/
}
);
Furthermore you're falling for a famous Promise anti-pattern here by not using the promise returned by the $http service and therefor breaking the promise chain. The following snippet wouldn't break the promise chain and would let you handle any error that might occur at a single catch block.
function multiBarChartData() {
return $http(/*...*/).then(
function (response) {
var report02ChartData = [];
.success(function (data) {
var report02ChartData = [];
angular.forEach(response.data.groupingsDown.groupings, function (de, di) {
/*...*/
});
});
data = report02ChartData;
return data;
});
}
This way the $http errors are also emitted in the .catch(fuction(err){..}) block in the controller, because you didn't break the chain.
UPDATE:
Since .success/.error are not chainable and marked deprecated, you should use .then/.error instead.
You need to initialize $scope.dashboard object when promise is resolved:
app.controller('HapDashCtrl', function($scope, $timeout, DataService) {
DataService.multiBarChart.data().then(function(data) {
$scope.dashboard = {
widgets: [{
col: 0,
row: 0,
sizeY: 1,
sizeX: 1,
name: "Multi Bar Chart",
chart: {
data: data,
api: {}
}
}]
};
});
});
I am new to Angularjs and studied a lot. But I stuck at a point. Google doesn't help me. I have a controller and I have data in $scope.results
app.controller('manage_categories', function($scope, $http, $filter, $window) {
$scope.results = [];
$http({
url: base_url + 'employee/fetchData?table=results',
method: "POST",
}).success(function(data) {
$scope.results = data;
});
})
now i want to access the same in other without any other $http call. I have done with another call but i don't want this . because i need this in many other controllers.something like this
app.controller('manage_users', function($scope, $http, $filter, $window,results) {
$scope.results = results;
//~ $http({
//~ url: base_url + 'employee/fetchData?table=results',
//~ method: "POST",
//~ }).success(function(data) {
//~ $scope.results = data;
//~ });
})
or any other method. Thanks.
update
I tried this
var myApp = angular.module('myApp',[]);
myApp.factory('results', function() {
return {
name : [{id:21,name:'this is test'}]
};
});
app.controller('manage_users', function($scope, $http, $filter, $window,results) {
$scope.results = results;
})
This is working fine . But not working with $http call .
var myApp = angular.module('myApp',[]);
myApp.factory('results', function($scope,$http) {
$scope.results=[];
$http({
url: base_url + 'employee/fetchData?table=results',
method: "POST",
}).success(function(data) {
$scope.results = data;
});
return {
name : results
};
});
update 2
after answers i write it like
var canapp = angular.module('canApp', ["ngRoute", "angularFileUpload"]);
canapp.service('ResultsFactory', ['$http', function($http) {
// http call here
var url=base_url + 'employee/fetchData?table=results';
$http.post(url,data).success(function(data){
this.results = data;
});
}])
call like this
canapp.controller('get_candidates', function($scope, $http, $filter, $timeout, $window, ResultsFactory) {
$scope.check=ResultsFactory.results;
});
but it is not setting the value in template
Use $broadcast to share the data between controllers. Your code will look like this
app.controller('manage_categories', function($scope, $http, $filter, $window, $rootScope) {
$scope.results = [];
$http({
url: base_url + 'employee/fetchData?table=results',
method: "POST",
}).success(function(data) {
$scope.results = data;
$rootScope.$broadcast("results",data);
});
});
app.controller('otherCtrlr', function($scope, $rootScope) {
$rootScope.$on("results", function(event, data){
$scope.results = data;
});
});
But using a service call in the controller is not a best approach. Create a factory and create a method to call your service.
From controller you need to call this method. But to avoid two service calls, you definitely need to use broadcast/emit(depending on data transfer is from parent or child)
There are various possible way of communicating between two controllers. If you just Google share data between controllers angularjs, you may found various links:
Using Services to Share Data Between Controllers
Sharing Data Between Controllers
Share data between AngularJS controllers
Passing data between controllers in Angular JS?
So, in short, possible ways are:
Using Angular Factories (recommended)
Using $rootScope (not recommended)
Using top most controller's scope as root scope
You can do this:
app.factory('ResultsFactory', resultsFactory);
resultsFactory.$inject = ['$http'];
function resultsFactory = function(){
var self = {};
var results = null;
self.getResults = function(){
if(!results){
$http.post(url,data).success(function(data){
results = data;
});
}else{
return results;
}
}
return self;
}
Only the first time that you call to ResultsFactory.getResults() this executes the $http call.
Here's a small fiddle explaining how to share data between controllers.
https://jsfiddle.net/frishi/zxnLwz6d/10/
(Check the browser console to see that both controllers can access data via the service.)
Basically the premise of a service is that it is a singleton that can be used by all the controllers registered on your module.
You want to make that $http call in a service:
.service('myService', ['$http', function($http) {
this.getData = function(){
// Simple GET request example:
return $http({
method: 'GET',
url: 'https://api.github.com/users/mralexgray/repos' // example API
}).then(function successCallback(response) {
return response;
}, function errorCallback(response) {
// return error message
});
}
}])
In your controller:
.controller('Controller2',['$scope','myService',function ($scope,myService) {
$scope.foo = myService.getData();
//resolve the promise:
$scope.foo.then(function(data){
console.log(data);
})
}
])
It is strongly recommended to use separated services as frishi pointed out. This sample is in single file and module just to make it readeable. Following implementation stores the promise and actual request is only made on the initial call to getFoo. The rest will get the response from the in memory promise.
'use strict';
angular.module('foo', [])
.factory('FooResource', function SessionResource($http) {
var fooPromise;
return {
getFoo: function getFoo() {
if(!fooPromise) {
fooPromise = $http.post('employee/fetchData?table=results');
}
return fooPromise;
}
};
})
.controller('FooController', function($scope, FooResource) {
FooResource.getFoo().then(function getFooSuccess(data) {
$scope.results = data;
});
});
I use this angular code with ionic framework
may be its help you..
my factory is..
angular.module('starter.services', [])
.factory('Chats', function() {
// Might use a resource here that returns a JSON array
// Some fake testing data
var chats = [{
id: 0,
name: 'Ben Sparrow',
lastText: 'You on your way?',
face: 'img/ben.png'
}, {
id: 1,
name: 'Max Lynx',
lastText: 'Hey, it\'s me',
face: 'img/max.png'
}, {
id: 2,
name: 'Adam Bradleyson',
lastText: 'I should buy a boat',
face: 'img/adam.jpg'
}, {
id: 3,
name: 'Perry Governor',
lastText: 'Look at my mukluks!',
face: 'img/perry.png'
}, {
id: 4,
name: 'Mike Harrington',
lastText: 'This is wicked good ice cream.',
face: 'img/mike.png'
}];
return {
all: function() {
return chats;
},
remove: function(chat) {
chats.splice(chats.indexOf(chat), 1);
},
get: function(chatId) {
for (var i = 0; i < chats.length; i++) {
if (chats[i].id === parseInt(chatId)) {
return chats[i];
}
}
return null;
}
};
});
and i use this factory in many controllers
.controller('ChatsCtrl', function($scope, Chats) {
$scope.chats = Chats.all();
$scope.remove = function(chat) {
Chats.remove(chat);
};
})
.controller('ChatDetailCtrl', function($scope, $stateParams, Chats) {
$scope.chat = Chats.get($stateParams.chatId);
})
in this code factory hit http:// request only one time and i use response on two controllers.
Hope its help you.
I'm trying to make an update function to replace some data on an user.
I've created the factory:
factory('Details', ['$resource', function ($resource) {
return $resource('/api/client/:id', null, {
'update': { method: 'PUT'}
});
}]);
and the controller:
.controller('ClientDetails', function ($scope, Details, $routeParams) {
$scope.client = Details.query({ id: $routeParams.id });
$scope.editClient = function () {
$scope.client.$update();
}
});
and when entering function editClient() it throws and error:
$scope.client.$update is not a function
What have I done wrong? Thanks
By default, the query method is defined to return an array of instances: 'query': {method:'GET', isArray:true}, see documentation for ng-resource. And the array does not have the $update method. From your code, you need to use the get to fetch the instance, like this:
$scope.client = Details.get({ id: $routeParams.id });
$scope.editClient = function () {
$scope.client.$update();
}
resolve: {
document: function ($stateParams, specialGet, $http) {
var id = $stateParams.id
return specialGet(id)
},
},
.factory('specialGet', function (Pusher, $q, $http) {
return function (id) {
return
$http.get('/api/document/'+id)
}
})
When I inject document into my controller I get an object with these properties
config: Object
data: Object
headers: function (name) {
status: 200
statusText: "OK"
How do I pass just the data into document instead of the return being this object and having to get the data afterwards?
Continue on the http promise using the then method, and return the desired data there.
resolve: {
document: function ($stateParams, $http) {
var nid = $stateParams.id
return $http.get('/api/document/'+id).then(function(response) { return response.data; });
},
},
I am passing three parameters from my controller to the factory following way.
In my controller I am trying to pass three parameters id,sdt and edt ..
$scope.val = function () {
$scope.tech = techRepository.getTech.query({ id: $scope.id, sdt: $scope.sDate, edt: $scope.eDate }, function(data) {
scope.tech = data;
});
};
In my factory I have
App.factory('techRepository', ['$resource', function ($resource) {
return {
getTech: $resource('/api/Tech/GetRange/:id', {id: '#id', start: '#sdt', end: '#edt'}, {query: {method: 'GET', isArray:true}})
};
}]);
When I run this I get Bad Request error. Please let me know how to pass multiple parameters. Thanks
This works fine, presuming you want :id in your query string to be replaced with the value of $scope.id, and two query parameters (sdt and edt) attached like:
http://www.example.com/api/Tech/GetRange/123?edt=20140610&sdt=20140609
It seems like you may instead be expecting a URL that looks like:
http://www.example.com/api/Tech/GetRange/123?end=20140610&start=20140609
... in which case, your code should look like:
// in controller
$scope.val = function () {
$scope.tech = techRepository.getTech.query({ id: $scope.id, start: $scope.sDate, end: $scope.eDate }, function(data) {
scope.tech = data;
});
};
.factory('techRepository', ['$resource', function ($resource) {
return {
getTech: $resource('/:id', {id: '#id'}, {query: {method: 'GET', isArray:true}})
};
}]);
Demo