I have this factory which is called multiple times by directives. Since it returns a lot of data the rendering at the end is slow. How can i call it only once or save it in a cashe when its called the second time and n time?
appBMdata.factory('Trends',['$http','Config','$q',
function($http,Config,$q){
function getAllData() {
var source1 = $http.get(Config.api_server + 'bizmonitor/indicators/get/2016');
var source2 = $http.post(Config.api_server + 'trends');
return $q.all([source1, source2]);
};
return {
getAllData : getAllData,
};
}]);
You can save the promise in a var, and return it if it has been already set:
appBMdata.factory('Trends',['$http','Config','$q',
function($http,Config,$q){
var _cacheGetAllData;
function getAllData() {
var source1 = $http.get(Config.api_server + 'bizmonitor/indicators/get/2016');
var source2 = $http.post(Config.api_server + 'trends');
_cacheGetAllData = _cacheGetAllData || $q.all([source1, source2]);
return _cacheGetAllData;
}
return {
getAllData : getAllData,
};
}]);
If you want successive calls to force to update, you can edit it to something like this:
appBMdata.factory('Trends',['$http','Config','$q',
function($http,Config,$q){
var _cacheGetAllData;
function getAllData(ignoreCache) {
var source1 = $http.get(Config.api_server + 'bizmonitor/indicators/get/2016');
var source2 = $http.post(Config.api_server + 'trends');
if (ignoreCache) {_cacheGetAllData = undefined;}
_cacheGetAllData = _cacheGetAllData || $q.all([source1, source2]);
return _cacheGetAllData;
}
return {
getAllData : getAllData,
};
}]);
I'm resolving it in the service and then store data, if it has data, returning data in a promise. If you want to fetch data again just add true as first arguement.
appBMdata.factory('Trends', ['$http', 'Config', '$q', function($http, Config, $q) {
var data;
function getAllData(nocache) {
var deferred = $q.defer();
if (data.length && !nocache) {
deferred.resolve(data);
} else {
var source1 = $http.get(Config.api_server + 'bizmonitor/indicators/get/2016');
var source2 = $http.post(Config.api_server + 'trends');
$q.all([source1, source2])
.then(function (values) {
data = values;
deferred.resolve(data);
})
.catch(function (err) {
deferred.reject(err);
});
}
return deferred.promise;
}
return {
getAllData : getAllData
};
}]);
Yes you can keep the data on $rootScope and return the data from there when its called multiple times.
appBMdata.factory('Trends',['$http','Config','$q','$rootScope'
function($http,Config,$q,$rootScope){
function getAllData() {
var source1 = $http.get(Config.api_server + 'bizmonitor/indicators/get/2016');
var source2 = $http.post(Config.api_server + 'trends');
return $q.all([source1, source2]);
};
if($rootScope.data){ // check if data already present
$rootScope.data=getAllData(); // assign data to rootscope
}
return {
getAllData : $rootScope.data, //return data from rootscope
};
}]);
Related
app.service("service1", function ($q) {
this.number1 = function (num1) {
var deferred = $q.defer();
var num1 = Math.floor((Math.random() * 5) + 1);
var result1 = num1;
deferred.resolve(result1);
return deferred.promise;
}
});
app.service("service2", function ($q) {
this.number2 = function (num2) {
var deferred = $q.defer();
var num2 = Math.floor((Math.random() * 5) + 1);
var result2 = num2;
deferred.resolve(result2);
return deferred.promise;
}
});
app.service("addservice", function ($q) {
this.addition = function (num1, num2) {
var deferred = $q.defer();
var result = num1 + num2;
deferred.resolve(result);
return deferred.promise;
}
});
app.controller('myCtrl', function ($scope, addservice, service1, service2) {
service1.number1().then(function (data) {
$scope.result1 = data;
})
service2.number2().then(function (data) {
$scope.result2 = data;
})
addservice.addition($scope.result1, $scope.result2).then(function (data) {
$scope.result = data;
});
});
service1 is used to pass num1 and service2 for num2. Service3 is used for the sum of those numbers.
How to use $q.all() here to sync multiple promises.?
You can use $q.all() method, it takes either an object or an array of promises and waits for all of them to resolve() or one of them to reject() and then executes the provided callback function.
//In $q as dependecies
app.controller('myCtrl', function ($scope, addservice, service1, service2, $q) {
//Persist reference of promomise
var p1 = service1.number1().then(function (data) {
$scope.result1 = data;
});
//Persist reference of promomise
var p2 = service2.number2().then(function (data) {
$scope.result2 = data;
});
//Pass references
$q.all([p1, p2]).then(function () {
//Now pass result 1 and 2
addservice.addition($scope.result1, $scope.result2).then(function (data) {
$scope.result = data;
})
});
});
You can return both values and use them in a synced promise:
app.controller('myCtrl', function($scope, addservice, service1, service2) {
var promise1 = service1.number1().then(function(data) {
$scope.result1 = data;
return data;
})
var promise2 = service2.number2().then(function(data) {
$scope.result2 = data;
return data;
})
$q.all([promise1, promise2]).then(function(res) {
addservice.addition(res[0], res[1]).then(function(data) {
$scope.result = data;
});
})
});
I have written Angular Service as shown below.
getPropertyDetailsByUsingApiService.js
(function () {
appModule.service('getPropertyDetailsByUsingApiService', ['$http', function ($http) {
this.propertyDetails = function (token, number, street, county, zip) {
var endpointUrl = 'http://my.com/api/Matcher?Token=';
var url = endpointUrl + token + '&Number=' + number + '&Street=' + street + '&County=' + county + '&Zip=' + zip;
return $http.get(url).then(function (data) {
var result = data;
if (result.data[0].Status == 'OK') {
$http.get(endpointUrl + token + '&Krp=' + result.data[0].Result[0].KRP + '&County=' + county)
.then(function (finalData) {
return finalData;
});
}
});
};
}
]);
})();
This is the consuming method :
propertyForm.js
//to call Api
vm.callApi = function () {
var county = _.find(vm.counties, function (c) { return c.id == vm.property.countyId; });
var city = _.find(vm.cities, function (c) { return c.id == vm.property.address.cityId; });
getPropertyDetailsByUsingApiService.propertyDetails(vm.getMd5Hashbytes(), vm.property.address.streetNumber, vm.property.address.streetName,
county.name, city.zipCode).then(function (result) {
vm.propertyDetails = result;
});
};
Q : Issue here is before resolving the promise on service method,it goes to the main method's promise.In other words before resolving 2nd promise on the service method where it goes to the calling method's promise.Can you tell me where is the issue ?
You can rewrite:
return $http.get('url').then(function(r) => { return r;})
as:
var defer = $q.defer();
$http.get('url').then(function(r) => { defer.resolve(r);})
return defer.promise;
It does not make sense in usual case, but in special cases, you can do whatever with this construction:
var defer = $q.defer();
if (smth) {
defer.resolve('test1');
} else {
$http.get('url').then(function(r) => {
if (smth2) {
defer.resolve(r);
} else {
$http.get(..., function(r) => {
defer.resolve(r);
})
}
})
|
return defer.promise;
So, in the first promise on the service, you arent returning the second promise, so the first promise just finishes up and resolves as the method completes, and doesnt wait for the second promise.
return $http.get(url).then(function (data) {
var result = data;
if (result.data[0].Status == 'OK') {
//Add a return here
return $http.get(endpointUrl + token + '&Krp=' + result.data[0].Result[0].KRP + '&County=' + county)
.then(function (finalData) {
return finalData;
});
}
});
I cannot seem to set my product variable properly from within my getProduct() function...
If I console.log(product) immediately after angular.copy(products.data[i], product);, it is set there correctly, however I cannot figure out why var product = {}; on line 3 remains an empty object.
Any ideas what is going?
angular.module('APP').factory('productFactory', function($q, $http) {
var products = [];
var product = {};
var getAllProducts = function(){
return $http.get('./data/product.json')
.then(function(response) {
angular.copy(response, products);
var deferred = $q.defer();
deferred.resolve(response);
return deferred.promise;
});
};
var getProduct = function(id) {
getAllProducts()
.then(function(){
for (var i = products.data.length - 1; i >= 0; i--) {
if(products.data[i].id == id){
angular.copy(products.data[i], product);
}
};
})
};
return {
getAllProducts: getAllProducts,
getProduct: getProduct,
products: products,
product: product
};
})
I’m really struggling to write a complex function in Angular that depends on promises. This is my first time writing a promise and I'm still not sure I fully understand how to do what I want to do with my code.
I have a variable var query = searchQuery.getQuery() in a controller ProfileNavCtrl. Then in my searchQuery service, getQuery fetches the value of localStorage.getItem('searchQuery') and checks if it’s an empty string or null. If it’s not empty or null, it simply returns the value to the controller. The value should be an array of slugs like ['foo','foo-bar','foo-bar-baz'].
If it is null or empty, it executes an $http.get call to fetch a JSON object and parse it. This is where things break down for me. I need getQuery() to return the value from $http.get (if the initial value of query is null) so that the controller variable query is assigned that value. As it is now, query (in the controller) is always set to null or undefined.
The $http.get call also calls setQuery() so that the query is persisted and future calls are avoided.
Here is my controller:
app.controller('ProfileNavCtrl', ['$scope', '$http', '$location', '$q', 'searchQuery',
function($scope, $http, $location, $q, searchQuery){
var query = searchQuery.getQuery;
// do something with query
And here is my service:
app.service('searchQuery', ['$http', '$timeout', '$q', function($http, $timeout, $q){
var query = [];
this.getQuery = new Promise(function(){
var query = localStorage.getItem('searchQuery');
if(query == "" || query == [""] || query == null){
var slugArray = [];
var query = $http.get('/companies.json')
.then(function(resp) {
if(resp && resp.data) {
for(var i in resp.data) {
var result = resp.data[i];
if(resp.data[i].name){
slugArray.push(resp.data[i].name.toLowerCase().split(' ').join('-'));
}
}
setQuery(slugArray);
} else {
resetQuery();
}
}, function(err) {
resetQuery();
}).then(function(resp){
return resp;
})
return query;
} else {
return query;
};
}).then(function(success){
return success;
});
UPDATE: 2nd Attempt
Here is my controller code:
var getQuery = searchQuery.getQuery();
getQuery.then(function(query){
query = searchQuery.getQuery();
// Check if user is on main site or portal
if(location.pathname.split('/')[3] == null){
var currentProfile = location.pathname.split('/')[1];
} else {
var currentProfile = location.pathname.split('/')[3];
};
// Get the next/prev query element (if any)
console.log('6: ');
console.log(query);
var prev = query.slice(query.indexOf(currentProfile)-1)[0];
var next = query.slice(query.indexOf(currentProfile)+1)[0];
// Check if next/prev is undefined and if so, set to first/last element in query array
if(prev){
var prevProfile = prev;
} else {
var prevProfile = query.pop();
};
if(next){
var nextProfile = next;
} else {
var nextProfile = query[0];
};
$scope.goToPrev = function() {
// Check if user is on main site or portal
if(location.pathname.split('/')[3] == null){
var profileUrl = location.origin + '/' + prevProfile;
// window.location = profileUrl;
console.log(profileUrl);
} else {
var profileUrl = location.origin + '/' + location.pathname.split('/').slice(1,3).join('/') + '/' + prevProfile;
// window.location = profileUrl;
console.log(profileUrl);
}
};
$scope.goToNext = function() {
// Check if user is on main site or portal
if(location.pathname.split('/')[3] == null){
var profileUrl = location.origin + '/' + nextProfile;
// window.location = profileUrl;
console.log(profileUrl);
} else {
var profileUrl = location.origin + '/' + location.pathname.split('/').slice(1,3).join('/') + '/' + nextProfile;
// window.location = profileUrl;
console.log(profileUrl);
}
};
});
Here is my updated service:
this.getQuery = function(){
return new Promise(function(){
var query = localStorage.getItem('searchQuery');
if(query == "" || query == [""] || query == null){
var slugArray = [];
return $http.get('/companies.json')
.then(function(resp) {
if(resp && resp.data) {
for(var i in resp.data) {
var result = resp.data[i];
if(resp.data[i].name){
slugArray.push(resp.data[i].name.toLowerCase().split(' ').join('-'));
}
}
setQuery(slugArray);
} else {
resetQuery();
}
return slugArray;
}, function(err) {
resetQuery();
});
} else {
return query;
};
});
};
In Angular promises are provided through the $q service. See the documentation for more detail.
The basic outline to implement $q promise in your service is outlined below, I'll leave the detail on how to save to local storage etc to you:
this.getQuery = function(){
var deferred = $q.defer();
var query = localStorage.getItem('searchQuery');
if(query == "" || query == [""] || query == null){
$http.get('yoururl').then(function(resp) {
// assuming resp is an array, else do your parsing to get array
query = resp;
deferred.resolve(query);
}, function(err) {
query = null;
deferred.reject(err);
});
} else {
deferred.resolve(query);
};
return deferred.promise;
};
You can then use this in your controller like:
var query = null;
searchQuery.getQuery().then(function(result) {
query = result;
}, function(err) {
// Error occured
});
Given the following code:
function Ctrl($scope, $http, $q) {
var search = function(name) {
if (name) {
$http.get('http://api.discogs.com/database/search?type=artist&q='+ name +'&page=1&per_page=5').
success(function(data3) {
$scope.clicked = false;
$scope.results = data3.results;
});
}
$scope.reset = function () {
$scope.sliding = false;
$scope.name = undefined;
};
};
$scope.$watch('name', search, true);
var done = $scope.getDetails = function (id) {
$scope.clicked = true;
$scope.sliding = true;
var api = 'http://api.discogs.com/artists/';
return $q.all([$http.get(api + id),
$http.get(api + id + '/releases?page=1&per_page=100')]);
};
done.then(function (){
$scope.releases = data2.releases;
$scope.artist = data;
return $http.get('http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=e8aefa857fc74255570c1ee62b01cdba&artist=' + name + '&album='+ title +'&format=json');
});
I'm getting the following console error:
TypeError: Object function (id) {
$scope.clicked = true;
$scope.sliding = true;
var api = 'http://api.discogs.com/artists/';
return $q.all([$http.get(api + id),
$http.get(api + id + '/releases?page=...<omitted>... } has no method 'then'
at new Ctrl (file:///C:/Users/Zuh/Desktop/AngularJS%20Discogs/js/services.js:27:9)
Can anybody point me to where might the error be? I'm defining the .then after getDetails is executed...
Here's a working Plunker.
Here is your updated plunkr http://plnkr.co/edit/lTdnkRB1WfHqPusaJmg2?p=preview
angular.module('myApp', ['ngResource']);
function Ctrl($scope, $http, $q) {
var search = function(name) {
if (name) {
$http.get('http://api.discogs.com/database/search?type=artist&q='+ name +'&page=1&per_page=5').
success(function(data3) {
console.log(arguments)
$scope.clicked = false;
$scope.results = data3.results;
});
}
$scope.reset = function () {
$scope.sliding = false;
$scope.name = undefined;
};
};
$scope.$watch('name', search, true);
var done = $scope.getDetails = function (id) {
$scope.clicked = true;
$scope.sliding = true;
var api = 'http://api.discogs.com/artists/';
var q = $q.all([$http.get(api + id),
$http.get(api + id + '/releases?page=1&per_page=100')])
.then(function (ret){
//console.log(arguments)
$scope.releases = ret[1].data.releases;
$scope.artist = ret[0];
return $http.get('http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=e8aefa857fc74255570c1ee62b01cdba&artist=' + name + '&album='+ title +'&format=json');
})
return q
};
}
To sum up fixes:
move $q.all().then() part into done method
pay more attention to what parameters handlers received in then part.