AngularJS controller does console.log() before service - javascript

I am developing an app in M.E.A.N stack. I'm using an angular controller (from a directive), and a service. The controller calls the service, the service sends a GET request to NodeJS and gets a result. Then I log the result in the service and I get the data.
The problem is that the controller also logs the service before the controller gets results.
In services.js:
var self = this;
self.show = function() {
$http.get('/contactlist').success(function(response) {
console.log(response);
return response;
});
In directives.js:
this.contacts = contactsSrv.show();
console.log(this.contacts);
And that's what I see in the console:
(The directive logs before it gets results from contactsSrv.show())
How can I make the contactsSrv.show() asynchronous?
Thanks!

Using .then as promise return
service.js
var self = this;
self.show = function() {
return $http.get('/contactlist');
};
directive.js
contactsSrv.show()
.then(function (response) {
console.log(response);
this.contacts = response;
});

$http.get replaced by $http.jsonp as I need to retrive some data from ouside stackoverflow.
Replace success with then method - so you can chain results in controller / run section.
angular.module('app', [])
.service('contactsSrv', function($http) {
this.show = function() {
return $http.jsonp('//public-api.wordpress.com/rest/v1/sites/wtmpeachtest.wordpress.com/posts?callback=JSON_CALLBACK').then(function(response) {
return response.data;
});
}
}).run(function(contactsSrv) {
contactsSrv.show().then(function(r) {
console.log('found posts', r.found)
})
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='app'>
</div>

Related

How to return HTTP response through subsequent service call

I am facing problem in returning data from Web API to Angular Form via series of Common Function. I am giving the code in its simplest form to understand the problem:-
HTML Form:-
<label>Request Number</label>
<input type="text" id="txtRequestNum" ng-model="md_reqnumber" />
<label>Employee Number</label>
<input type="text" id="txtEmployeeId" ng-model="md_number" ng-disabled="true" />
Controller.js
var myApp = angular.module('appHome');
myApp.controller("ctrlEmployeeAdd", ['$scope', 'CommonFunctionFactory', function ($scope, CommonFunctionFactory) {
CommonFunctionFactory.AddMasterData($scope.md_reqnumber)
.then(function (dataSuccess) {
$scope.number = dataSuccess;
}, function (dataError) {
});
}]);
CommonFunctionFactory.js
var appService = angular.module('appHome');
appService.factory('CommonFunctionFactory', ['MetadataOrgFactory', function (MetadataOrgFactory) {
var dataFactory = {};
dataFactory.AddMasterData = function (objData) {
MetadataOrgFactory.postApiCall('addemployee', objData, function (dataSuccess) {
alert("The request has been completed succesfully");
return dataSuccess;
}, function (dataError) {
});
}
return dataFactory;
}])
ApiCallService.js
var appService = angular.module('appHome');
appService.factory('MetadataOrgFactory', ['$http', '$q', function ($http, $q) {
var dataFactory = {};
var url = 'http://localhost:XXXXX';
dataFactory.postApiCall = function (controllerName, objData, callbackSuccess, callbackError) {
$http.post(url + '/api/' + controllerName, objData).then
(function success(response) {
alert("Success");
callbackSuccess(response.data);
}, function error(response) {
callbackError(response.status);
});
};
return dataFactory;
}])
WebApi.cs
[Authorize]
[Route("api/addemployee")]
[HttpPost]
public int AddEmployee(EmployeeViewModel vmEmployee)
{
return 54302 //Just for simplicity here, returning hard coded value
}
I am able to get the above hard coded value in ApiCallService.js and also able to get in the datasuccess variable of CommonFunctionFactory.js but not able to return the value back to Controller.js. This is my actual problem here.
Also angular promise defined in Controller.js is giving following error after getting the response from Web API:-
Cannot read property 'then' of undefined
Please help me in solving the problem.
As AnthW pointed out, you are pretty close, but I think your issues revolve around what you are and aren't returning. My changes are below, it doesn't require changing your controller.js or server files, only the two factories.
CommonFunctionFactory.js
For this, I would personally remove this factory, but if you want to keep it, I would just return the $http call and this way in your controller you can use then and access the returned variable.
var appService = angular.module('appHome');
appService.factory('CommonFunctionFactory', ['MetadataOrgFactory', function (MetadataOrgFactory) {
var dataFactory = {};
dataFactory.AddMasterData = function (objData) {
return MetadataOrgFactory.postApiCall('addemployee', objData);
}
return dataFactory;
}])
ApiCallService.js
As AnthW pointed out in his answer, you aren't returning the $http call, which is why you are seeing undefined. Also, there is no reason to use callback functions when you are using promises, promises are just a different way to handle the same use case. In this code, I am removing your callbacks and we are returning the promise, and the promise is returning either the success object or the error object.
var appService = angular.module('appHome');
appService.factory('MetadataOrgFactory', ['$http', '$q', function ($http, $q) {
var dataFactory = {};
var url = 'http://localhost:XXXXX';
dataFactory.postApiCall = function (controllerName, objData) {
var promise = $http.post(url + '/api/' + controllerName, objData)
.then(function(response) {
alert("Success");
return response.data;
}, function(response) {
return response.status;
});
return promise;
};
return dataFactory;
}])
So, in conclusion, my changes are to have your ApiCallService.js return the promise created by $http. The CommonFunctionFactory.js will just handle return this promise to the controller. In your controller, you are already expecting a promise, so this returned promise will then be properly accessed in your controller, and you will be able to retrieve the returned value.
The postApiCall function is not returning anything. You need to return $http:
dataFactory.postApiCall = function (controllerName, objData, callbackSuccess, callbackError) {
return $http.post(url + '/api/' + controllerName, objData)
.then(function success(response) {
alert("Success");
callbackSuccess(response.data);
},
function error(response) {
callbackError(response.status);
});
};
You will then need to do the same with MetadataOrgFactory.postApiCall by adding a return in front of it:
return MetadataOrgFactory.postApiCall('addemployee', objData, function (dataSuccess) {
alert("The request has been completed succesfully");
return dataSuccess;
},
function (dataError) {}
);
Now, you have returned the request so that calling CommonFunctionFactory.AddMasterData($scope.md_reqnumber) will have a then method available.
I imagine that it was just returning undefined previously. You can check by logging out the value of CommonFunctionFactory.AddMasterData($scope.md_reqnumber)

How to get data in Angular directive though Ajax calls?

Please forgive me if my Title doesn't exactly match with the issue.
I have a custom Angular directive to generate kendo grids dynamically.
I am making an Ajax call to get the configuration from server.
Now issue is, directive first gets loaded and then the ajax call gets completed because of which it is throwing error in my directive.
Please let me know if there is any workaround for this.
My directive:
gridApp.directive('grid', function () {
return {
restrict: "EA",
scope: true,
template: '<div kendo-grid="mainGrid" options="gridOptions"></div>',
controller: function ($scope, $element, $attrs, gridService) {
var gridConfig = gridService.getGridConfig(); //this is undefined. However, when I check in console it gets loaded once the directive is executed.
//removed for breveity
}
};
});
My Service:
angularApp.factory('gridService', gridService);
function gridService($http, $q) {
var getGridConfig = function (gridId) {
var deferred = $q.defer();
$http.get('/Base/GetGridConfiguration?GridId=' + gridId)
.then(function (response) {
deferred.resolve(response.data);
});
return deferred.promise;
}
return {
getGridConfig: getGridConfig
};
}
promises are async. you need to use .then
gridService.getGridConfig()
.then(function(data) {
////
// code dependent on server response
////
});
RFTD link
Chnage your service to return deferred object rather than data.
return $http.get('/Base/GetGridConfiguration?GridId=' + gridId)
resolve the promise inside the directive.
var gridConfig, _self = this;
gridService.getGridConfig().then((response) => {
_self.gridConfig = response.data;
});

Difficulty in calling function properly in angular js

i have a function attached to angular app module through "app.factory" which has certain http requests in it. That function is invoked in the controller and the returned data from function is assigned to a scope variable in controller.
Problem is controller is being executed first and then the http requests in the function are executed due to which the data coming from http request can't be captured into scope variable. How that can be corrected.
function resourceapp.factory('dataService', function($http){
var data = {};
data.EnterprisePrograms = [];
data.Resources=[];
data.Skills=[];
data.Allocations=[];
Execution = function($http) {
$http.get('http://localhost:8080/api/enterpriseProgram')
.then(function(resources) {
data.EnterprisePrograms=resources.data;
});
$http.get('http://localhost:8080/api/resource')
.then(function(resources) {
data.Resources=resources.data;
});
$http.get('http://localhost:8080/api/skill')
.then(function(resources) {
data.Skills=resources.data;
});
$http.get('http://localhost:8080/api/allocation')
.then(function(allocation) {
data.Allocations = allocation.data;
});
}
return data;
});
controller
resourceapp.controller('AllocationList', function($scope, dataService) {
$scope.allocationList = dataService.Allocations;
});
It should be like :
In factory : Create a method to return promise.
resourceapp.factory('dataService', function($http){
var data = {};
data.getEnterprisePrograms = function($http){
return $http.get('http://localhost:8080/api/enterpriseProgram');
}
data.getResourceData = function($http) {
return $http.get('http://localhost:8080/api/resource');
}
data.getSkillData = function($http) {
return $http.get('http://localhost:8080/api/skill');
}
data.getAllocationData = function($http) {
return $http.get('http://localhost:8080/api/allocation');
}
return data;
});
In Controller, call the factory method to fetch promise and resolve them with $q.all()
resourceapp.controller('AllocationList', function($scope, dataService, $q){
var allocationPromise = dataService.getAllocationData();
var skillPromise= dataService.getSkillData();
// other promise
$q.all([allocationPromise,skillPromise, ..other promise]).then(function (result) {
$scope.allocationData = result[0].data;
$scope.skillData = result[1].data;
// other data
})
});
See this for $q.all()
You could also create a function for each $http.get using promises.
EnterprisePrograms = function(){
return $http.get('http://localhost:8080/api/enterpriseProgram')
.then(function(resources){
return resources.data;
});
}
Resources = function(){
return $http.get('http://localhost:8080/api/resource')
.then(function(resources){
return resources.data;
});
}
Skills = function(){
return $http.get('http://localhost:8080/api/skill')
.then(function(resources){
return resources.data;
});
}
Allocations = function(){
return $http.get('http://localhost:8080/api/allocation')
.then(function(allocation){
return allocation.data;
});
}

sequence of promises while posting and getting data from api with angular controller and service misordered

i cant sort the following out.
i have an angular app
this consists of a service and a controller
the controller interacts with the view and calls the service to
get entries
post entries
in the background theres an api processing data...
everythings fine so far but when i post an entry and want to get all entries afterwards to update the scope, things become akward.
i think my main problem is the sequence of events/promises being wrong somehow and i can't figure out what i am doing wrong..
maybe there is someone who could help me since i spent hours on that problem now without any progress.. i read a lot and all i have read sound logical for me .. still i cant get it working..
i have created a fiddle for you guys, with a fake api in the background doing nothing... plz have a look at the console ouput and you can see that things are happening in the wrong sequence...
http://jsfiddle.net/ft7zjpk6/101/
the view
<div ng-app="app" ng-controller="EntryController as fake">
<button ng-click="submit()">post entry</button>
</div>
the service
(function(){
var app = angular.module('Entry', []);
app.factory('EntryService', function($http, $q)
{
var EntryService = {};
var deferred = $q.defer();
var entries = [];
EntryService.getEntries = function () {
console.log('get entries');
return entries;
};
EntryService.fetchEntries = function(url)
{
$http.get(url)
.success(function(data) {
console.log('fetch entries');
entries = data;
deferred.resolve();
})
.error(function(error) {
console.log('Error: Entries could not be fetched');
deferred.resolve();
});
return deferred.promise;
};
EntryService.postEntry = function(url, entry)
{
$http.post(url, entry)
.success(function(data) {
console.log('post entry');
deferred.resolve();
})
.error(function() {
console.log('Error: Entry could not be saved.');
deferred.resolve();
});
return deferred.promise;
};
return EntryService;
});
})();
the controller
(function () {
var app = angular.module('app', ['Entry']);
app.controller('EntryController', ['$scope', 'EntryService', function ($scope, EntryService) {
$scope.entryService = EntryService;
$scope.entries = {};
loadEntries();
function loadEntries() {
$scope.entryService.fetchEntries('/echo/json')
.then(function(){
$scope.entries = $scope.entryService.getEntries();
});
}
$scope.submit = function(ngModel) {
$scope.entryService.postEntry('/echo/json', {fakeData: 'unimportant'})
.then(loadEntries());
}
}]);
})();
thanks for reading.. best regards
uschihund
Your could (should) get rid of $q and use the promise returned by $http. Moreover, code will be shorter:
EntryService.fetchEntries = function(url) {
return $http.get(url).then(function (response) {
entries = response.data;
});
}
EntryService.postEntry = function(url, entry) {
return $http.post(url, entry);
};

Processing $http response in service

I recently posted a detailed description of the issue I am facing here at SO. As I couldn't send an actual $http request, I used timeout to simulate asynchronous behavior. Data binding from my model to view is working correct, with the help of #Gloopy
Now, when I use $http instead of $timeout (tested locally), I could see the asynchronous request was successful and data is filled with json response in my service. But, my view is not updating.
updated Plunkr here
Here is a Plunk that does what you want: http://plnkr.co/edit/TTlbSv?p=preview
The idea is that you work with promises directly and their "then" functions to manipulate and access the asynchronously returned responses.
app.factory('myService', function($http) {
var myService = {
async: function() {
// $http returns a promise, which has a then function, which also returns a promise
var promise = $http.get('test.json').then(function (response) {
// The then function here is an opportunity to modify the response
console.log(response);
// The return value gets picked up by the then in the controller.
return response.data;
});
// Return the promise to the controller
return promise;
}
};
return myService;
});
app.controller('MainCtrl', function( myService,$scope) {
// Call the async method and then do stuff with what is returned inside our own then function
myService.async().then(function(d) {
$scope.data = d;
});
});
Here is a slightly more complicated version that caches the request so you only make it first time (http://plnkr.co/edit/2yH1F4IMZlMS8QsV9rHv?p=preview):
app.factory('myService', function($http) {
var promise;
var myService = {
async: function() {
if ( !promise ) {
// $http returns a promise, which has a then function, which also returns a promise
promise = $http.get('test.json').then(function (response) {
// The then function here is an opportunity to modify the response
console.log(response);
// The return value gets picked up by the then in the controller.
return response.data;
});
}
// Return the promise to the controller
return promise;
}
};
return myService;
});
app.controller('MainCtrl', function( myService,$scope) {
$scope.clearData = function() {
$scope.data = {};
};
$scope.getData = function() {
// Call the async method and then do stuff with what is returned inside our own then function
myService.async().then(function(d) {
$scope.data = d;
});
};
});
Let it be simple. It's as simple as
Return promise in your service(no need to use then in service)
Use then in your controller
Demo. http://plnkr.co/edit/cbdG5p?p=preview
var app = angular.module('plunker', []);
app.factory('myService', function($http) {
return {
async: function() {
return $http.get('test.json'); //1. this returns promise
}
};
});
app.controller('MainCtrl', function( myService,$scope) {
myService.async().then(function(d) { //2. so you can use .then()
$scope.data = d;
});
});
Because it is asynchronous, the $scope is getting the data before the ajax call is complete.
You could use $q in your service to create promise and give it back to
controller, and controller obtain the result within then() call against promise.
In your service,
app.factory('myService', function($http, $q) {
var deffered = $q.defer();
var data = [];
var myService = {};
myService.async = function() {
$http.get('test.json')
.success(function (d) {
data = d;
console.log(d);
deffered.resolve();
});
return deffered.promise;
};
myService.data = function() { return data; };
return myService;
});
Then, in your controller:
app.controller('MainCtrl', function( myService,$scope) {
myService.async().then(function() {
$scope.data = myService.data();
});
});
tosh shimayama have a solution but you can simplify a lot if you use the fact that $http returns promises and that promises can return a value:
app.factory('myService', function($http, $q) {
myService.async = function() {
return $http.get('test.json')
.then(function (response) {
var data = reponse.data;
console.log(data);
return data;
});
};
return myService;
});
app.controller('MainCtrl', function( myService,$scope) {
$scope.asyncData = myService.async();
$scope.$watch('asyncData', function(asyncData) {
if(angular.isDefined(asyncData)) {
// Do something with the returned data, angular handle promises fine, you don't have to reassign the value to the scope if you just want to use it with angular directives
}
});
});
A little demonstration in coffeescript: http://plunker.no.de/edit/ksnErx?live=preview
Your plunker updated with my method: http://plnkr.co/edit/mwSZGK?p=preview
A much better way I think would be something like this:
Service:
app.service('FruitsManager',function($q){
function getAllFruits(){
var deferred = $q.defer();
...
// somewhere here use: deferred.resolve(awesomeFruits);
...
return deferred.promise;
}
return{
getAllFruits:getAllFruits
}
});
And in the controller you can simply use:
$scope.fruits = FruitsManager.getAllFruits();
Angular will automatically put the resolved awesomeFruits into the $scope.fruits.
I had the same problem, but when I was surfing on the internet I understood that $http return back by default a promise, then I could use it with "then" after return the "data". look at the code:
app.service('myService', function($http) {
this.getData = function(){
var myResponseData = $http.get('test.json').then(function (response) {
console.log(response);.
return response.data;
});
return myResponseData;
}
});
app.controller('MainCtrl', function( myService, $scope) {
// Call the getData and set the response "data" in your scope.
myService.getData.then(function(myReponseData) {
$scope.data = myReponseData;
});
});
When binding the UI to your array you'll want to make sure you update that same array directly by setting the length to 0 and pushing the data into the array.
Instead of this (which set a different array reference to data which your UI won't know about):
myService.async = function() {
$http.get('test.json')
.success(function (d) {
data = d;
});
};
try this:
myService.async = function() {
$http.get('test.json')
.success(function (d) {
data.length = 0;
for(var i = 0; i < d.length; i++){
data.push(d[i]);
}
});
};
Here is a fiddle that shows the difference between setting a new array vs emptying and adding to an existing one. I couldn't get your plnkr working but hopefully this works for you!
Related to this I went through a similar problem, but not with get or post made by Angular but with an extension made by a 3rd party (in my case Chrome Extension).
The problem that I faced is that the Chrome Extension won't return then() so I was unable to do it the way in the solution above but the result is still Asynchronous.
So my solution is to create a service and to proceed to a callback
app.service('cookieInfoService', function() {
this.getInfo = function(callback) {
var model = {};
chrome.cookies.get({url:serverUrl, name:'userId'}, function (response) {
model.response= response;
callback(model);
});
};
});
Then in my controller
app.controller("MyCtrl", function ($scope, cookieInfoService) {
cookieInfoService.getInfo(function (info) {
console.log(info);
});
});
Hope this can help others getting the same issue.
I've read http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/
[AngularJS allows us to streamline our controller logic by placing a promise directly on the scope, rather than manually handing the resolved value in a success callback.]
so simply and handy :)
var app = angular.module('myApp', []);
app.factory('Data', function($http,$q) {
return {
getData : function(){
var deferred = $q.defer();
var promise = $http.get('./largeLoad').success(function (response) {
deferred.resolve(response);
});
// Return the promise to the controller
return deferred.promise;
}
}
});
app.controller('FetchCtrl',function($scope,Data){
$scope.items = Data.getData();
});
Hope this help
I really don't like the fact that, because of the "promise" way of doing things, the consumer of the service that uses $http has to "know" about how to unpack the response.
I just want to call something and get the data out, similar to the old $scope.items = Data.getData(); way, which is now deprecated.
I tried for a while and didn't come up with a perfect solution, but here's my best shot (Plunker). It may be useful to someone.
app.factory('myService', function($http) {
var _data; // cache data rather than promise
var myService = {};
myService.getData = function(obj) {
if(!_data) {
$http.get('test.json').then(function(result){
_data = result.data;
console.log(_data); // prove that it executes once
angular.extend(obj, _data);
});
} else {
angular.extend(obj, _data);
}
};
return myService;
});
Then controller:
app.controller('MainCtrl', function( myService,$scope) {
$scope.clearData = function() {
$scope.data = Object.create(null);
};
$scope.getData = function() {
$scope.clearData(); // also important: need to prepare input to getData as an object
myService.getData($scope.data); // **important bit** pass in object you want to augment
};
});
Flaws I can already spot are
You have to pass in the object which you want the data added to, which isn't an intuitive or common pattern in Angular
getData can only accept the obj parameter in the form of an object (although it could also accept an array), which won't be a problem for many applications, but it's a sore limitation
You have to prepare the input object $scope.data with = {} to make it an object (essentially what $scope.clearData() does above), or = [] for an array, or it won't work (we're already having to assume something about what data is coming). I tried to do this preparation step IN getData, but no luck.
Nevertheless, it provides a pattern which removes controller "promise unwrap" boilerplate, and might be useful in cases when you want to use certain data obtained from $http in more than one place while keeping it DRY.
As far as caching the response in service is concerned , here's another version that seems more straight forward than what I've seen so far:
App.factory('dataStorage', function($http) {
var dataStorage;//storage for cache
return (function() {
// if dataStorage exists returned cached version
return dataStorage = dataStorage || $http({
url: 'your.json',
method: 'GET',
cache: true
}).then(function (response) {
console.log('if storage don\'t exist : ' + response);
return response;
});
})();
});
this service will return either the cached data or $http.get;
dataStorage.then(function(data) {
$scope.data = data;
},function(e){
console.log('err: ' + e);
});
Please try the below Code
You can split the controller (PageCtrl) and service (dataService)
'use strict';
(function () {
angular.module('myApp')
.controller('pageContl', ['$scope', 'dataService', PageContl])
.service('dataService', ['$q', '$http', DataService]);
function DataService($q, $http){
this.$q = $q;
this.$http = $http;
//... blob blob
}
DataService.prototype = {
getSearchData: function () {
var deferred = this.$q.defer(); //initiating promise
this.$http({
method: 'POST',//GET
url: 'test.json',
headers: { 'Content-Type': 'application/json' }
}).then(function(result) {
deferred.resolve(result.data);
},function (error) {
deferred.reject(error);
});
return deferred.promise;
},
getABCDATA: function () {
}
};
function PageContl($scope, dataService) {
this.$scope = $scope;
this.dataService = dataService; //injecting service Dependency in ctrl
this.pageData = {}; //or [];
}
PageContl.prototype = {
searchData: function () {
var self = this; //we can't access 'this' of parent fn from callback or inner function, that's why assigning in temp variable
this.dataService.getSearchData().then(function (data) {
self.searchData = data;
});
}
}
}());

Categories