With AngularJS, I'm trying to implement a simple service that returns a list of clients from a REST service which is then available in a controller.
I am stuck in figuring out how to properly pass data to the controller. Below is my service and it pulls the data just fine. I've verified that the data is there
app.service('clientsService', ['$http', function ($http) {
var serviceBase = 'http://localhost:56879/api/';
this.getClients = function () {
return $http.get(serviceBase + 'clients').then(function (results) {
console.log(results.data);
return results.data;
});
};
}]);
Next I attempt to use this in a controller
app.controller('clientsController', ['$scope', 'clientsService', function ($scope, clientsService) {
this.clients = clientsService.getClients();
console.log(this.clients);
}]);
In this controller, this.clients doesn't contain the data, it just contains a try-catch block
Object {then: function, catch: function, finally: function}
catch: function (a){return this.then(null,
finally: function (a){function b(a,c){var d=e();c?d.resolve(a):d.reject(a);return d.promise}function d(e,g){var f=null;try{f=(a||c)()}catch(h){return b(h,!1)}return f&&P(f.then)?f.then(function(){return b(e,g)},function(a){return b(a,!1)}):b(e,g)}return this.then(function(a){return d(a,!0)},function(a){return d(a,!1)})}
then: function (b,g,h){var m=e(),u=function(d){try{m.resolve((P(b)?b:c)(d))}catch(e){m.reject(e),a(e)}},F=function(b){try{m.resolve((P(g)?g:d)(b))}catch(c){m.reject(c),a(c)}},v=function(b){try{m.notify((P(h)?h:c)(b))}catch(d){a(d)}};f?f.push([u,F,v]):k.then(u,F,v);return m.promise}
__proto__: Object
I can't understand quite yet what it is that I've done incorrectly to actually pass data from the service to the controller.
That is because getClients method returns a promise, not data. The promise resolves to return the data in the callback. The methods that you are seeing in the console is of that of the promise object returned by the service method. So you should register a callback to then method of the promise:-
var _that = this;
clientsService.getClients().then(function(data) { //Runs when promise is resolved
_that.clients = data;
}).catch(function(){ //<-- Runs if the promise is rejected
});
You could look at the ngResource service which is really easy to use. It is based on $http but with a little more abstraction :
https://docs.angularjs.org/api/ngResource/service/$resource
Related
I'm quite new to promises. I have a hard time trying to update an object used in my view from 2 chained promises :
function Test($resource, FC, UserDetailsService) {
'ngInject';
var self = this;
self.data = {
};
function getTestData() {
firstPromise.then(function(response) {
//I want self.data to be displayed in my view
angular.extend(self.data, response);
//Now my view should display my object
resource().get(user)
.$promise.then(function(responsePts){
//And THEN update/refresh my view here
angular.extend(self.data, responsePts);
});
});
};
self.getTestData = getTestData;
};
EDIT : firstPromise is exposed in another service, and used by other services :
$resource(apiUrl).get(user).$promise.then(function(bookData){
angular.extend(self.bookings, bookData);
});
In my controller :
function TestController(Test) {
'ngInject';
var $ctrl = this;
$ctrl.testData = {};
Test.getTestData();
$ctrl.testData = Test.data;
};
Instead self.data will not be displayed until the resolution of the second promise. How can I make my object available for my controller directly when the first promise is resolved ?
It the firstPromise is a $q Service promise, the view should be updating. Since the view is not updating, use $q.when() to convert the unknown promise to a $q Service promise:
function getTestData() {
//firstPromise.then(function(response) {
$q.when(firstPromise).then(function(response) {
//I want self.data to be displayed in my view
angular.extend(self.data, response);
//Now my view should display my object
//return to chain
return resource().get(user).$promise;
}).then(function(responsePts){
//And THEN update/refresh my view here
angular.extend(self.data, responsePts);
});
};
$q Service promises are integrated with the AngularJS framework and its digest cycle.
$q.when(value)
Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted.
-- AngularJS $q Service API Reference - $q.when
I am new to angular 1.5, and used to rely on $scope for doing something like this, where I would bind the return value of a promise to the scope. I'm inside a service, and am trying to return data via a promise, and I want that data to then be exposed via this service. however, the data, mockData is undefined:
let mockData;
function getWidgets() {
return $http.get(`${RuntimeConfig.DEV_API_URL}/widgets`, { cache: widgetsCache })
}
getWidgets().then(function(data) {
mockData = data.data;
})
console.log(mockData); //undefined.
return value of this service:
return {
mockData
};
my previous way to solve this would have been by declaring $scope.mockData, but I can't do that because a) it's an antipattern now b) i'm in a service. what am I doing wrong or missing?
mockData will be undefined since it get's returned even before your GET request finish. One way to handle this is to return the promise from the service and handle the promise resolution in let's say controller.
Inject $q dependency in your code.
function getWidgets() {
var deferred = $q.defer();
$http.get(`${RuntimeConfig.DEV_API_URL}/widgets`, { cache: widgetsCache })
return deferred.promise
}
I solved this by setting mockData to be an array or an object rather than a primitive
In my webapp, I write a factory methods for serving ajax calls and returning promises by using the $q service. As you can probably tell, I am still on the learning curve of using AngularJS; I found something interesting on the $q.defer() object, which cannot be shared by factory methods. I write the following fake ajax calls in a factory component (plnker here):
(function() {
'use strict';
angular.module('testAjax')
.factory('AjaxPromiseService', AjaxPromiseService);
AjaxPromiseService.$inject = ['$q', '$timeout'];
function AjaxPromiseService($q, $timeout) {
//var deferred = $q.defer(); //cannot be claimed for sharing here
var methodObj = {getDummyData : getDummyData,
getName: getName
};
return methodObj;
/******************** implementations below **********/
function getDummyData() {
var data = {"data" : "dummy data from ajax!"};
var deferred = $q.defer();
$timeout(function() {
deferred.resolve(data);
}, 3000); //3 seconds
return deferred.promise;
} //getDummyData
function getName() {
var deferred = $q.defer();
$timeout(function() {
deferred.resolve({"name": "my name is john doe!"});
}, 2000); //2 seconds
return deferred.promise;
} //getName
}
}());
In my controller, I have the following:
(function() {
'use strict';
angular.module('testAjax', ['ui.router', 'Constants'])
.controller('testController', testController);
testController.$inject = ['$log', 'AjaxPromiseService', '$http', '$q', 'URL_CONFIGS'];
function testController($log, AjaxPromiseService, $http, $q, URL_CONFIGS) {
$log.info('in the testController');
var vm = this;
vm.message = URL_CONFIGS.home;
vm.getData = function() {
AjaxPromiseService.getDummyData().then(function(data) {
vm.message += data.data;
//$log.info($q);
}).catch(function(err) {
$log.info('error in getting data');
}).finally(function() {
$log.info('getData is completed');
}); //getDummyData
}; //getData
vm.getName = function() {
AjaxPromiseService.getName().then(function(data) {
vm.message += data.name;
//$log.info($q);
}).catch(function(err) {
$log.info('error in getting data');
}).finally(function() {
$log.info('getData is completed');
}); //getDummyData
}; //getName
}
}());
In my template, I have the following two buttons that invoke the above two functions in the controller.
<button class="btn btn-primary" ng-click="contentView.getData()">Get data</button>
<button class="btn btn-primary" ng-click="contentView.getName()">Get name</button>
<strong>Message: {{contentView.message}}</strong>
In the factory AjaxPromiseService component, the var deferred object cannot be shared between the two functions inside the factory, and I have to define a deferred object for each function, otherwise it wouldn't work. So I was wondering why deferred cannot be shared between methods in a factory?
Why deferred cannot be shared between methods?
Because a Deferred object is linked to the promise it resolves. Each promise needs its own one. If you share a single deferred, each method would return the same promise.
See also What are the differences between Deferred, Promise and Future in JavaScript?.
actually you can share the deferred object. Just think about it not as a service, but a simple JS object. One deferred object can be resolved only once, that's done for purpose. In your AjaxPromiseService you obviously need two differend deferreds, because you resolve them with different data.
for example, $http.post() every time returns different deferred objects
sharing of one deferred between several functions is useful when your deferral can be resolved from different sources (for example you try to get some data simultaneously from localStorage cache, http source and some WebWorker, that is calculating this data)
If i understand you correctly you want to reuse the same object created by $q.defer().
$q.defer() returns an object that will be resolved at some point in the future by calling the .resolve method. So basically it represents a certain action that will be completed at some point in the future.
You cannot share the same promise object for multiple actions that complete in the future.
See also the link in Bergi's anwser.
Also you plunker is broken because
var methodObj = {getDummyData : getDummyData};
is missing the getName I fixed it in this plunker
I am new to angularjs and so i was going through the basic examples i found online initially just to understand the working and concepts used. When i encountered the concept of "factory service creation" (which is a way to expose data from server to views in and angularjs), i found it difficult to understand the flow between the service's function arguments and the calls made to it .
`<html ng-app="countryApp">
<head>
<meta charset="utf-8">
<title>Angular.js Example</title>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.10/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.10/angular-route.min.js"></script>
<script>
var countryApp = angular.module('countryApp', ['ngRoute']);
countryApp.config(function($routeProvider) {
$routeProvider.
when('/', {
templateUrl: 'country-list.html',
controller: 'CountryListCtrl'
}).
when('/:countryName', {
templateUrl: 'country-detail.html',
controller: 'CountryDetailCtrl'
}).
otherwise({
redirectTo: '/'
});
});
countryApp.factory('countries', function($http){
return {
list: function(callback){
$http.get('countries.json').success(callback);
},
find: function(name, callback){
$http.get('countries.json').success(function(data) {
var country = data.filter(function(entry){
return entry.name === name;
})[0];
callback(country);
});
}
};
});
countryApp.controller('CountryListCtrl', function ($scope, countries){
countries.list(function(countries) {
$scope.countries = countries;
});
});
countryApp.controller('CountryDetailCtrl', function ($scope, $routeParams, countries){
countries.find($routeParams.countryName, function(country) {
$scope.country = country;
});
});
</script>
</head>
<body>
<div ng-view></div>
</body>
</html>`
so in the code i ve posted, can anyone let me know or explain the flow between the "factory's list and find methods( keeping the callback argument particularly in mind)?
i am not able to understand why the same factory method is called again by itself( callback argument)
please help me out..
Regarding the list function
When the CountryListCtrl controller is instantiated, the countries service (which is an object) is being passed as an argument.
Then the countries.list function (defined within countries service obviously) is being called and passed a callback function.
The countries.list function makes a GET request, and if successful (i.e. the $http promise is resolved with success), the anonymous callback function, that was passed in when the function was called in the CountryListController controller is called and the $http service passes the returned data as an argument - Which the anonymous function then assigns to the $scope.countries property.
The countries.find function is the same basic pattern, with the difference that $routeParams is picking up the /:countryName from the route, and passing it into the countries.find function as an argument for the purpose (it seems) of picking out a specific country from the response data returned by the server and then assigning it to the $scope.country property.
the portion of code i am commenting on is
countryApp.factory('countries', function($http){
return {
list: function(callback){
$http.get('countries.json').success(callback);
},
find: function(name, callback){
$http.get('countries.json').success(function(data) {
var country = data.filter(function(entry){
return entry.name === name;
})[0];
callback(country);
});
}
};
});
Here the factory is returning an object with two functions namely list and find.
both the function has a parameter called callback. callback is basically the function that you want to call when the service is executed successfully. As both list and find are going to make Asynchronous calls to server, you would want to be notified when the call is done.
Angular however has a neater way of doing this, called promise. and if we implement promise api the code becomes
countryApp.factory('countries', function($http, $q){
return {
list: function(){
var defered = $q.defer();
$http.get('countries.json').success(function(result){
defered.resolve(result);
})
.error(function(error){
defered.reject(error)
})
return defer.promise
},
find: function(name){
var defered = $q.defer();
$http.get('countries.json').success(function(data) {
var country = data.filter(function(entry){
return entry.name === name;
})[0];
defered.resolve(country);
})
.error(function(error){
defered.reject(error)
})
return defer.promise;
}
};
});
Angulars promise api is very well documented here
https://docs.angularjs.org/api/ng/service/$q
in short what it says is promise object is a contract that when an asynchronous work is completed then it would either be resolved() (completed successfully) or rejected (completed unsuccessfully) and promise objects then function would be called.
then(success(), error())
your controller would become.
countryApp.controller('CountryListCtrl', function ($scope, countries){
countries.list().then(function(countries) {
$scope.countries = countries;
});
}, function(error){
console.log("unable to fetch the list of countries : " + error)
});
countryApp.controller('CountryDetailCtrl', function ($scope, $routeParams, countries){
countries.find($routeParams.countryName).then(function(country) {
$scope.country = country;
}, function(error){
console.log("unable to find the country: " + error)
}));
Hope it helped you.
first of all we are defining modules for any application in angularJS.
then we are defining configuration for the module where inside [] we are keeping all required dependency.we can define our own angular directive which will connect java controller to get value in respective format like json etc.Then while defining angular controller we can invoke our defined directive in our angular controller to make availability for data, and from angular controller we can get value to angular view which will display in html or any view page.
Below is the recommended way to get data into a controller from a factory using $http -- according to https://github.com/johnpapa/angularjs-styleguide
What i don't get is how the two success callbacks on $http work (i commented what i think the two callbacks are).
1) What is the point of the first callback?
2) Where does vm.avengers point to? Is it a reference to another object?
3) is 'data' in the second callback = 'response.data.results' from the first?
4) I'm counting 3 total callbacks chained, is that correct?
P.S. i already know about promises, but want to learn this pattern specifically
The factory
/* recommended */
// dataservice factory
angular
.module('app.core')
.factory('dataservice', dataservice);
dataservice.$inject = ['$http', 'logger'];
function dataservice($http, logger) {
return {
getAvengers: getAvengers
};
function getAvengers() {
return $http.get('/api/maa')
.then(getAvengersComplete)
.catch(getAvengersFailed);
//Callback One
function getAvengersComplete(response) {
return response.data.results;
}
function getAvengersFailed(error) {
logger.error('XHR Failed for getAvengers.' + error.data);
}
}
}
The Controller
function Avengers(dataservice, logger) {
var vm = this;
vm.avengers = [];
activate();
function activate() {
return getAvengers().then(function() { //Callback 3
logger.info('Activated Avengers View');
});
}
function getAvengers() {
return dataservice.getAvengers()
.then(function(data) { //Callback 2
vm.avengers = data;
return vm.avengers;
});
}}
The point of this first callback is to do any manipulation with the data prior to it entering into the app and to actually pull the useful data out of the http response object.
vm.avengers is declared at the top of your controller. It's using the "controller as" syntax and is being put on a reference to the "this" object of the controller. You're ultimately using vm.avengers to access the data in the view.
Correct.
HTTP Call -> getAvengersComplete -> getAvengers, so correct 3 callbacks.