angular.module('App').factory('API', ['someAPI', function(someAPI){
var service = {};
service.loadInfo= loadInfo;
return service;
function loadInfo(id) {
var list = [];
var items= [];
someAPI.list.get({id: id}).$promise.then(function (result) {
items= result;
if (items.length === 0) {
items= 'No results';
} else {
for (var i=0; i<items.length; i++) {
list.push(items[i].name);
}
}
console.log('List:' + list); **//This print out 'List: a, b, c, d' in console**
})
console.log('List:' + list); **//This print out nothing**
return list;
}
}
]);
//Controller
angular.module('App').controller('ProductCtrl', function($scope, API) {
$scope.loadInfo = function (id) {
$scope.list = API.loadInfo(id);
console.log($scope.list); // This print out undefined
}
}
This might be a silly question, In my service, the variable inside 'someAPI' function could be visited, but controller get an undefined. Appreciate if any body could explain me. Thanks!
#James In your controller you need to pass $scope to your function (this is called dependency injection) which in simple words means passing an object to a function. Hope it helps!
the list gets returned immediately before the someAPI.list.get({id: id}).$promise is resolved. You need to do it in the promise way. Another problem is that your local variable is called list but you returned List.
I'd suggest that in your service, just do
return someAPI.list.get({id: id}).$promise.then(function (result) {
items= result;
if (items.length === 0) {
items= 'No results';
} else {
for (var i=0; i<items.length; i++) {
list.push(items[i].name);
}
}
return list;
})
}
and put move .then part to your controller.
$scope.loadInfo = function (id) {
someAPI.loadInfo(id).then(function (result) {
$scope.list = result;
});
}
Modified the dependencies and rewrite the factory call.
//Controller
angular.module('App').controller('ProductCtrl', function(API,$scope) {
$scope.loadInfo = function (id) {
API.loadInfo(id)
.success(function (data, status, headers, config) {
$scope.list = data;
console.log($scope.list);
}).error(function (e, status, headers, config) {
//log error here
});
}
Try this it will update you when ajax finish its process.
$scope.$watch('list',function(val){
console.log(val);
});
Related
I have this API that has pages 1-10 and I want to loop through the page numbers to make the API calls
app.factory('companies', ['$http', function($http) {
var i;
for (i = 1; i < 11; i++) {
var data = $http.get('https://examplepage.com/wp-json/wp/v2/categories?per_page=50&page=' + i);
console.log('list', data);
}
return data;
}]);
This is what I get when I console log the data for all 10 API calls
JSON data
My attempt to display all of the data (list of names), but it seems as though it's only taking the last API call and displaying it. How do I combine all of the returned data into one object to display the list of name from pages 1-10?
app.controller('HomeController', ['$scope', 'companies', function($scope, companies) {
companies.success(function(data) {
$scope.companies = data;
console.log('companies', $scope.companies);
});
}]);
view.html
<div class="container" ng-controller="HomeController">
<div ng-repeat="company in companies" class="list">
{{ company.name }}
</div>
</div>
Each call to the $http service returns a promise. You need to use $q.all to consolidate the promises:
app.factory('companies', function($http,$q) {
return { tenPagesPromise: tenPagesPromise };
function tenPagesPromise () {
var indices = Array.from({length:10}).map((x,i)=>i);
var promises = indices.map(i=>pagePromise(i));
return $q.all(promises).then(responseArray => {
var dataArray = responseArray.map(x=>x.data);
return dataArray.reduce((t,x)=>t.concat(x),[]);
});
}
function pagePromise(i) {
var url = "https://examplepage.com/wp-json/wp/v2/categories";
var params = { per_page: 50, page: i };
var config = { params: params }
promise = $http.get(url,config);
return promise;
}
});
Usage:
companies.tenPagesPromise.then(data => {
$scope.companies = data;
}).catch(function(errorResponse) {
console.log(errorResponse);
});
For more information, see AngularJS $q Service API Reference - all.
You need to resolve the promise and then add the data to an array, something like this:
app.factory('companies', ['$http', function($http) {
data = []
for (let i = 1; i < 11; i++) {
$http.get('https://examplepage.com/wp-json/wp/v2/categories?per_page=50&page=' + i)
.then( function(resp) {
console.log(resp.data);
data.push(resp.data);
})
}
return data;
}]);
I'm building Angular/Express app, I load data with controller and try to work with data in a function but I get error in console
Cannot read property 'toLowerCase' of undefined
When I manually write JSON data it works just fine.
Anyone had this error and why is it happening?
Edit: Also I want function to work on click, when I want it not when it's loaded, also I use data from listData in view so I know it's loaded
Controller
var self = this;
self.listData = [];
var self = this;
self.listData = [];
$http.get('/myList')
.success(function (data) {
self.listData = data;
console.log(data);
})
.error(function (data) {
console.log('Error: ' + data);
});
self.myFunc = function(){
var map = self.listData.reduce(function (p, c) {
p.set(c.name.toLowerCase(), c.surname);
return p;
}, new Map());
console.log(...map);
}
HTTP.GET is an asynchronous function
You could call your function which turns the data to lowercase in the .success of your http.get. That way you know that the data has arrived. Now you might be executing this function a bit too early which means that you do not yet have the data in your list.
If you try to run the toLowerCase() on your data, before you actually retrieved the data you will get this error. That is one of the things you learn to deal with when working with web requests.
For example writing your code like this would work.
$http.get('/myList')
.success(function (data) {
self.listData = data;
myFunc(listData);
console.log(data);
})
.error(function (data) {
console.log('Error: ' + data);
});
}
function myFunc(){
var map = self.listData.reduce(function (p, c) {
p.set(c.name.toLowerCase(), c.surname);
return p;
}, new Map());
console.log(...map);
}
Here is your updated code works on click of an element:
jQuery("#a-div-to-click").on("click", function() {
var self = this;
self.listData = [];
$http.get('/myList').success(function (data) {
self.listData = data;
console.log(data);
self.myFunc();
}).error(function (data) {
console.log('Error: ' + data);
});
}
self.myFunc = function(){
var map = self.listData.reduce(function (p, c) {
p.set(c.name.toLowerCase(), c.surname);
return p;
}, new Map());
console.log(map);
}
});
V2) The data is loaded at "onload" phase and the process done at "onclick" phase:
app.controller('yourController', function ($scope, $http) {
$scope.fetchData = funcion(onSuccess) {
$http.get('/myList').success(function (data) {
$scope.aDivlistData = data;
console.log(data);
if (onSuccess != null) {
onSuccess();
}
}).error(function (data) {
console.log('Error: ' + data);
});
}
}();
$scope.onADivClicked = function() {
if ($scope.aDivlistData == null) {
$scope.fetchData($scope.populateMap);
} else {
$scope.populateMap();
}
};
$scope.populateMap = function() {
var map = $scope.aDivlistData.reduce(function (p, c) {
p.set(c.name.toLowerCase(), c.surname);
return p;
}, new Map());
console.log(map);
}
}
//html part:
//<div id="a-div-to-click" ng-click="onADivClicked()">A Div</a>
Just by looking at your code. It looks like "c.name" is undefined. May be you can print that variable out and see what's in it
c.name is undefined for some item in your listData. Checkout JSON which you receive from server, not faked one.
NOTE: $http.get is asynchronous.
Putting self.myFunc = ... into success handler of $http.get suppose to give correct behaviour. You can take a look on Understanding Asynchronous Code in Layman's terms to see how async works.
Good Luck ! :)
I am trying to post a simple binary value (using $http) to a URL which si defined by a value in my HTML.
I have successfully got the "card.id" being passed through (can see it in console log)
<td-card ng-repeat="card in cards"
on-destroy="cardDestroyed($index)"
on-swipe="cardSwiped($index)"
on-swipe-right="$parent.cardSwiped(card.id)"
on-swipe-left="$parent.cardSwiped(card.id)" >
The data I want to post needs to be to a URL which has the card.id in it.
How to I tell it what to post and how to trigger?
.controller('CardsCtrl', ['$scope', 'TDCardDelegate', 'cardsApi', '$http',
function($scope, TDCardDelegate, cardsApi, $http) {
console.log('CARDS CTRL');
$scope.cards = [];
$scope.onSwipeRight=function(product_id){console.log(product_id)}
//Post for swipe right {'like':1, 'uid':21}
$scope.onSwipeLeft=function(product_id){console.log(product_id)}
//Post for swipe left {'like':0, 'uid':21}
for(var i = 0; i < 7; i++) {
cardsApi.getApiData()
.then(function (result) {
$scope.cards.unshift(result.data);
$scope.product_id = result.data.product_id;
})
.catch(function (err) {
$log.error(err);
});
}
$scope.$watchCollection('cards', function (newVal, oldVal) {
if(newVal < oldVal) {
cardsApi.getApiData()
.then(function (result) {
$scope.cards.unshift(result.data);
})
.catch(function (err) {
console.log(err);
});
}
});
$scope.cardSwiped = function(card) {
console.log('here');
console.log(card);
};
//Removes card from top of stack
$scope.cardDestroyed = function(index) {
$scope.cards.splice(index, 1);
};
$scope.addCard = function() {
var newCard = $scope.cards[$scope.cards.length];
//newCard.id = Math.random();
$scope.cards.push(angular.extend({}, newCard));
};
$scope.postRecordLikes = function(product_id){
console.log(product_id)
$http.post('http://test.com/analytic/' + product_id)
.then(function successCallback(product_id) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
};
}
])
postRecordLikes is being defined but never used. You can call it in your html by different ways. If you want to call it when you click it for example, just use ng-click.
By the way, calling $http inside your controller is not a good practice, have a look at this post: https://toddmotto.com/resolve-promises-in-angular-routes/
EDIT
I have forked your plunkr. Take a look at it: http://plnkr.co/edit/uvooKeCtFagAnFjVYnhS?p=preview
You should call postRecordLikes when your event is fired. Modify it as you want.
In my main module I want to create a service that parses a json file and pushes the contents to an array, after I would like service to have the array be returned so it is easily accessible by any of the controllers. The issue is the function is running before the $http request is complete so it always returns an empty array
dashModule.factory('dataFetch', function($http) {
var emailArray = [];
$http.get('../data/emails.json').success(function log(obj) {
for (var i = 0; i < obj.length; i++) {
emailArray[i] = obj[i];
}
});
return {
test: function() {
return emailArray;
}
};
});
use promise, like:
dashModule.factory('dataFetchService', function($http) {
var myReq = function() {
return $http.get('../data/emails.json').then(function(response){
return response.data;
});
};
return { myReq: myReq };
});
and
function getMyReq($scope, dataFetchService) {
var myReqPromise = dataFetchService. myReq();
myReqPromise.then(function(result) {
console.log(result);
});
}
I'm new to AngularJS and am still trying to wrap my head around using services to pull data into my application.
I am looking for a way to cache the result of a $http.get() which will be a JSON array. In this case, it is a static list of events:
[{ id: 1, name: "First Event"}, { id: 2, name: "Second Event"},...]
I have a service that I am trying to use to cache these results:
appServices.service("eventListService", function($http) {
var eventListCache;
this.get = function (ignoreCache) {
if (ignoreCache || !eventListCache) {
eventListCache = $http.get("/events.json", {cache: true});
}
return eventListCache;
}
});
Now from what I can understand I am returning a "promise" from the $http.get function, which in my controller I add in a success callback:
appControllers.controller("EventListCtrl", ["$scope", "eventListService",
function ($scope, eventListService) {
eventListService.get().success(function (data) { $scope.events = data; });
}
]);
This is working fine for me. What I'd like to do is add an event to the eventListService to pull out a specific event object from eventListCache.
appServices.service("eventListService", function($http) {
var eventListCache;
this.get = function (ignoreCache) { ... }
//added
this.getEvent = function (id) {
//TODO: add some sort of call to this.get() in order to make sure the
//eventListCache is there... stumped
}
});
I do not know if this is the best way to approach caching or if this is a stupid thing to do, but I am trying to get a single object from an array that may or may not be cached. OR maybe I'm supposed to call the original event and pull the object out of the resulting array in the controller.
You're on the right track. Services in Angularjs are singeltons, so using it to cache your $http request is fine. If you want to expose several functions in your service I would do something like this. I used the $q promise/deferred service implementation in Angularjs to handle the asynchronus http request.
appServices.service("eventListService", function($http, $q) {
var eventListCache;
var get = function (callback) {
$http({method: "GET", url: "/events.json"}).
success(function(data, status) {
eventListCache = data;
return callback(eventListCache);
}).
}
}
return {
getEventList : function(callback) {
if(eventListCache.length > 0) {
return callback(eventListCache);
} else {
var deferred = $q.defer();
get(function(data) {
deferred.resolve(data);
}
deferred.promise.then(function(res) {
return callback(res);
});
}
},
getSpecificEvent: function(id, callback) {
// Same as in getEventList(), but with a filter or sorting of the array
// ...
// return callback(....);
}
}
});
Now, in your controller, all you have to do is this;
appControllers.controller("EventListCtrl", ["$scope", "eventListService",
function ($scope, eventListService) {
// First time your controller runs, it will send http-request, second time it
// will use the cached variable
eventListService.getEventList(function(eventlist) {
$scope.myEventList = eventlist;
});
eventListService.getSpecificEvent($scope.someEventID, function(event) {
// This one is cached, and fetched from local variable in service
$scope.mySpecificEvent = event;
});
}
]);
You are on the right track. Here's a little help:
appServices.service("eventListService", function($http, $q) {
var eventListCache = [];
function getList(forceReload) {
var defObj = $q.defer(), listHolder;
if (eventListCache.length || forceReload) {
listHolder= $http.get("/events.json", {cache: true});
listHolder.then(function(data){
eventListCache = data;
defObj.resolve(eventListCache);
});
} else {
defObj.resolve(eventListCache);
}
return defObj.promise;
}
function getDetails(eventId){
var defObj = $q.defer();
if(eventId === undefined){
throw new Error('Event Id is Required.');
}
if(eventListCache.length === 0){
defObj.reject('No Events Loaded.');
} else {
defObj.resolve(eventListCache[eventId]);
}
return defObj.promise;
}
return {
eventList:getList,
eventDetails:getDetails
};
});
Then, in your controller, you handle it like this:
appControllers.controller("EventListCtrl", ["$scope", "eventListService",
function ($scope, eventListService) {
var eventList = eventListService.getList();
eventList.then(function(data){
$scope.events = data;
});
$scope.getEventsList = function(reloadList){
eventList = eventListService.getList(reloadList);
eventList.then(function(data){
$scope.events = data;
});
};
$scope.getEventDetails = function(eventID){
var detailsPromise = eventListService.getDetails(eventID);
detailsPromise.then(function(data){
$scope.eventDetails = data;
}, function(reason){
window.alert(reason);
});
}
}
]);
This way, your events are loaded when the controller first loads, and then you have the option to request a new list by simply passing in a boolean. Getting event details is also handled by an internal promise to give you some error handling without throwing a disruptive error.