Wait for $http.get to finish when the service is called - angularjs

I am facing a problem where the controller calls the service function getMyList but myLists is not populated yet as $http call takes some time to complete.
How do I resolve this problem?
Here is my code snippet:
Service
app.factory('myService', function($http) {
var myList = [];
$http.get("http://myService/json.data").then(function (resp) {
myLists = resp.data;
});
return {
getMyList: function (name) {
for (var i = 0; i < myLists.length; i++) {
if (myList[i].name == name) {
return myLists[i];
}
}
}
}
}
Controller:
app.controller('MainCtrl', function( myService,$scope) {
var testName = "test"
myService.getMyList(testName).then(function(resp) {
// do something with resp data
});
});

You first need to fix the typos, and choose a better naming. Then you need to return a promise of element rather than an element (that's what the controller expects, by the way):
app.factory('myService', function($http) {
// this defines a promise which, when resolved, contains an array of elements
var listPromise = $http.get("http://myService/json.data").then(function(resp) {
return resp.data;
});
return {
// this returns a promise which, when resolved, contains the element with the given name
getElementFromList: function(name) {
return listPromise.then(function(list) {
return list.filter(function(element) {
return element.name === name;
})[0];
});
}
};
});

Every HTTP call returns a promise in angular, so in your service just return the HTTP call and it return a promise.
Then in your controller you can use then and error call back to handle the response from the service or api call.
Factory:
app.factory('myService', function($http) {
var myList = [];
return {
getMyList: function (name) {
$http.get("http://myService/json.data")
}
}
}
Controller:
app.controller('MainCtrl', function( myService,$scope) {
var testName = "test"
myService.getMyList()
.then(function successCallback(resp) // Success call back
{
if(resp.data)
{
myLists = resp.data;
for (var i = 0; i < myLists.length; i++) {
if (myList[i].name == testName) { // you can use your testName here itself.
return myLists[i];
}
}
}
},
function errorCallback(resp) // Error call back
{
console.log(resp)
});
});

If you are using some client-side router implementation: have a look at Route.Resolve that both Ui.Router and ngRoute implement.
If you don't have any router, then, there is a little trick that you can do... Simply return a object reference from your factory, and, when the resolution occurs, fill that object with the expected value:
angular
.module('test', [])
.constant('API', 'https://jsonplaceholder.typicode.com')
.factory("PostResolved", function($http, API) {
var result = [];
$http
.get(API + '/posts/1')
.then(function(response) {
result.push(response.data);
})
;
return result;
})
.controller('TestCtrl', function($scope, PostResolved) {
$scope.posts = PostResolved;
})
;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<section ng-app="test">
<article ng-controller="TestCtrl">
<div ng-repeat="post in posts">
<h1 ng-bind="post.title"></h1>
</div>
</article>
</section>

Why don't use something like below:
app.factory('myService', function($http) {
return {
getMyList: function (name) {
var myList = [];
$http.get("http://myService/json.data").then(function (resp) {
myLists = resp.data;
});
scope.$evalAsync(function () {
for (var i = 0; i < myLists.length; i++) {
if (myList[i].name == name) {
return myLists[i];
}
}
});
}
}
}

I managed to solve this by using $timeout. Anybody who has a better suggestion let me know. I couldn't get scope.$evalAsync to work
Service
app.factory('myService', function($http, $timeout) {
var lists = [];
$http.get("http://myService/json.data").then(function (resp) {
lists = resp.data;
});
return {
getMyList: function (name) {
return $timeout(function() {
for (var i = 0; i < lists.length; i++) {
if (lists[i].name == name) {
return lists[i];
}
}
}, 2000)
}
}
controller
app.controller('MainCtrl', function( myService,$scope) {
var testName = "test"
myService.getMyList(testName).then(function(resp) {
// do something with resp data
});
});

Related

Why is .then not a function?

service.js
.factory('EventService', function ($http, $cordovaSQLite) {
return {
//some code here..
populateData: function (data) {
var items = [];
for (i = 0; i < data.length; i++) {
items.push(data[i]);
}
return items;
}
}
})
controller.js
.controller('NearCtrl', function ($scope, $http, $cordovaSQLite, EventService) {
EventService.getDataFromDB().then(function (result) {
if (result.length > 0) {
EventService.populateData(result).then(function (items) {
$scope.items = items;
})
} else {
EventService.getDataFromApi().then(function () {
EventService.getDataFromDB().then(function (result) {
EventService.populateData(result).then(function (items) {
$scope.items = items;
})
})
})
}
});
})
When I'm trying to run this code, I get "TypeError: EventService.populateData(...).then is not a function".
What am I doing wrong?
that service needs to return a promise, not returning the items
populateData: function(data) {
var deferred = $q.defer();
var items = [];
for (i = 0; i < data.length; i++) {
items.push(data[i]);
}
deferred.resolve(items);
return deferred.promise;
}
you might not need this though since you could do
var items = EventService.populateData(result);
//Do something with items here
usually promises are used if you're doing something asynchronously. Like calling an API and waiting for a response. In those cases, the response might take seconds to finish THEN the .then function gets called. in your case if you make that function a promise it will be called almost immediately
EDIT: Here's the link to $q Documentation AngularJS: API: $q
Return something that has a promise or just change your calling code:
populateData: return $http.get("www");
or
EventService.getDataFromApi().then(function () {
EventService.getDataFromDB().then(function (result) {
var response = EventService.populateData(result);
$scope.items = response;
});
});
I have same problem, My device is m1 Mac, I fixed it with npm replace yarn to start

How to make controller wait for service to return a value and how to access the returned value in controller?

I am new to angularjs. I am having trouble in accessing value that is returned from angularjs service in its controller.
Following is the code in controller:
'use strict';
app.controller('AdminTaskCtrl', function ($scope, Search) {
$scope.buildEnquiry = function (collegeId, ) {
Search.getIdByEmail(collegeId).then ( function ( result ) {
$scope.uId = result;
console.log($scope.uId);
});
};
});//controller ends here
And the code in Search service is as follows:
'use strict';
app.factory('Search',function ($firebase, FIREBASE_URL, $rootScope) {
var simpleuser = "";
getIdByEmail: function(counsellorEmail) {
var collegeuserArray = ($firebase(new Firebase(FIREBASE_URL+"abc/def/")).$asArray());
collegeuserArray.$loaded(function(collegeuserArray) {
for(var i=0; i<collegeuserArray.length; i++)
{
if((collegeuserArray[i].$value) == counsellorEmail)
{
simpleuser = collegeuserArray.$keyAt(collegeuserArray[i]);
console.log(simpleuser);
return simpleuser;
}
}
}, function(error) {
console.error("Error:", error);
});
}
};
);//service ends here.
When the code executes it gives error for .then function as fallows:
TypeError: undefined is not a function and value in controller is not accessed.
Please help.
'use strict';
app.factory('Search',function ($firebase, FIREBASE_URL, $rootScope, $q) {
var simpleuser = "";
getIdByEmail: function(counsellorEmail) {
var deferred = $q.defer();
var collegeUserArray = ($firebase(new Firebase(FIREBASE_URL+"abc/def/")).$asArray());
collegeUserArray.$loaded(function(collegeUserArray) {
for(var i=0; i<collegeUserArray.length; i++)
{
if((collegeUserArray[i].$value) == counsellorEmail)
{
simpleUser = collegeUserArray.$keyAt(collegeUserArray[i]);
console.log(simpleUser);
//return simpleUser;
deferred.resolve(simpleUser);
}
}
}, function(error) {
console.error("Error:", error);
deferred.reject(error);
});
return deferred.promise;
}
};
);
And your controller
'use strict';
app.controller('AdminTaskCtrl', function ($scope, Search) {
$scope.buildEnquiry = function (collegeId, ) {
Search.getIdByEmail(collegeId).then ( function ( result ) {
$scope.uId = result;
console.log($scope.uId);
}, function(error){
//If an error happened, handle it here
});
};
});
To directly answer your question, $loaded returns a promise, so the simplest answer here would be to return it from your service instead of bothering with $q and all these other things.
It does seem like the whole premise here is flawed and that this is an XY problem. It seems like several hacks intended to subvert the intended usage of these libs, and like a good, solid read of the Angular walkthrough and the AngularFire guide would save a lot of pain and needless complexity here.
The use of factory here is a subversion and heavily coupled. The syntax is invalid and wouldn't compile. And ultimately, the goal is to add a search method to the synchronized array returned from AngularFire, which should be done with $extendFactory.
app.factory('firebaseRef', function(FIREBASE_URL) {
return function(path) {
var ref = new Firebase(FIREBASE_URL);
if( path ) { ref = ref.child(path); }
return ref;
}
});
app.factory('SearchableArray', function($firebase, $FirebaseArray) {
var ArrayWithSearch = $FirebaseArray.$extendFactory({
searchByEmail: function(emailAddress) {
var res = null;
for(var i=0, len=this.$list.length; i < len; i++ ) {
if( this.$list[i].email === emailAddress ) {
res = this.$list[i];
break;
}
}
return res;
}
});
return function(ref) {
return $firebase(ref, {arrayFactory: ArrayWithSearch}).$asArray();
}
});
app.controller('Controller', function($scope, SearchableArray, firebaseRef) {
$scope.data = SearchableArray( firebaseRef('abc/def') );
$scope.search = function(email) {
console.log( $scope.data.searchByEmail(email) );
};
});

Passing page number param for infinite-scroll to return more results from the backend

I'm using infinite-scroll and I want to request more data using $http. So next page / next 10 results etc.
This is my current working code (I put this in a factory as I read on another post somewhere that this was a good idea, I'm now thinking a service might be better but I'm not sure yet):
angular.module('hotels', [])
.factory('hotels', function($http) {
var hotels = {};
hotels.get = function(callback) {
$http.get('/php/hotels.php').success(function(data) {
callback(data);
});
};
return hotels;
});
angular.module('app', ['hotels', 'infinite-scroll'])
.controller('hotelsCtrl', function ($scope, hotels){
hotels.get(function (data) {
$scope.hotels = data.results;
})
});
How do I pass back a param page=3 and have the backend return more results?
I thought it might look something like this but its not working.:
angular.module('hotels', [])
.factory('hotels', function($http) {
var hotels = {};
hotels.get = function(callback) {
$http.get('/php/hotels.php?page='+$scope.page).success(function(data) {
callback(data);
});
};
return hotels;
});
angular.module('app', ['hotels', 'infinite-scroll'])
.controller('hotelsCtrl', function ($scope, hotels){
$scope.page = $scope.page + 1;
hotels.get({page: $scope.page}, function (data) {
$scope.hotels.push.apply($scope.hotels, data.results);
})
});
Any ideas?
This does the job:
angular.module('hotels', [])
.factory('hotels', function($http) {
var hotels = {};
hotels.get = function(params, callback) {
$http.get('/php/hotels.php', {params: {page: params.page}}).success(function(data) {
callback(data);
});
};
return hotels;
});
angular.module('app', ['hotels', 'infinite-scroll'])
.controller('hotelsCtrl', function ($scope, hotels){
$scope.page = 1;
$scope.addMoreItems = function() {
$scope.hotels=[];
hotels.get({page: $scope.page}, function (data) {
//$scope.hotels.push(data.results);
for (var i = 0; i < data.length; i++) {
$scope.hotels.push(data[i]);
}
$scope.page+=1;
})
}
});

angularJS service not getting called

I'm very new to AngilarJS. I am trying to write a service in angularJS.
<script>
var module = angular.module("myapp", []);
module.service('BrandService', function ($http) {
var brands = [];
this.getBrands = function()
{
return $http.get('http://admin.localhost/cgi-bin/brand.pl')
.then(function(response)
{
brands = response.brands;
alert (brands);
});
}
//simply returns the brands list
this.list = function ()
{
return brands;
}
});
module.controller("brandsController", function($scope, BrandService) {
$scope.brandlist = BrandService.list();
alert ($scope.brandlist);
});
</script>
The statement "alert (brands);" is not getting called. What is the issue with this code. Is m missing any thing in implementation?
$http calls are always async. Meaning, even you do a .then at your service, there is no way it will properly the resolved data back into your controller. You will have to write it in your controller.
Your Service:
module.service('BrandService', function($http) {
var brands = [];
this.getBrands = function() {
//do not need the dot then.
return $http.get('http://admin.localhost/cgi-bin/brand.pl')
}
//simply returns the brands list
this.list = function() {
return brands;
}
});
In your controller:
module.controller("brandsController", function($scope, BrandService) {
BrandService.list()
.then(function(response) {
$scope.brandlist = response.brands;
alert($scope.brandlist);
});
});
In service:
this.getBrands = function() {
$http.get('http://admin.localhost/cgi-bin/brand.pl').then(function(response) {
brands = response.brands;
alert(brands);
return brands;
});
}
In controller:
$scope.brandlist = BrandService.getBrands();
alert($scope.brandlist);

How to get data by service and $cacheFactory by one method

I have a factory which get data from server. In the factory method I have used $cacheFactory to cache getting data. My code is as follows..
var buyersService = function ($http, $q,$cacheFactory) {
var serviceBase = '/api/OMData/';
var BuyersFactory = {};
buyersService.cache = $cacheFactory('cacheId');
BuyersFactory.GetBuyers = function () {
var dataList = buyersService.cache.get('BuyerData');
if (dataList != null && dataList.length > 0) {
return dataList;
}
else {
return $http.get(serviceBase + 'GetBuyers').then(
function (results) {
buyersService.cache.put("BuyerData", results.data);
return results.data;
});
}
}
app.factory('OMDataService', ['$http', '$q', '$cacheFactory', buyersService]);
});
Now I have called GetBuyers method from controller. My method is like below..
var BuyerController = function ($scope, BuyersService) {
$scope.Buyers = [];
init();
function init() {
getBuyers();
}
function getBuyers() {
BuyersService.GetBuyers()
.then(function (data) {
$scope.Buyers = data;
}, function (error) {
alert(error.message);
});
}
};
app.register.controller('BuyersController', ['$scope', 'OMDataService', BuyerController]);
When I have executed my controller method second time I have got an error message in promise part.
Object doesn't support property or method 'then'
The issue here is that your function returns two different things: either a promise or plain data. To remedy this, use another promise to control the flow and return that one as the result of the function.
Update your code to
var buyersService = function ($http, $q,$cacheFactory) {
var serviceBase = '/api/OMData/';
var BuyersFactory = {};
buyersService.cache = $cacheFactory('cacheId');
BuyersFactory.GetBuyers = function () {
var buyersDataIsAvailable = $q.defer();
var dataList = buyersService.cache.get('BuyerData');
if (dataList != null && dataList.length > 0) {
buyersDataIsAvailable.resolve(dataList);
}
else {
$http.get(serviceBase + 'GetBuyers').then(
function (results) {
buyersService.cache.put("BuyerData", results.data);
buyersDataIsAvailable.resolve(results.data);
});
}
return buyersDataIsAvailable.promise;
}
app.factory('OMDataService', ['$http', '$q', '$cacheFactory', buyersService]);
});

Resources