In my controller, I'm calling a factory that fetches data from an API, then in the success function, the data is passed to another function that loops through it and calls another factory each round in the loop. The data retrieved from the second factory is then mashed up with the data from the first factory in a new object, each round in the loop, and pushed to an array.
When I add the data to the $scope, the view is being updated as the data is being fetched, you can see that the items in the view are added one by one and not all in a bunch.
I also need to sort the data in the $scope before it hits the view.
Is there a way to do this when all actions are finished? – When all data is fetched.
app.controller('MyCtrl', function($scope, firstFactory, secondFactory) {
var objArray = [];
function doStuff() {
firstFactory.getData().success(function(data) {
doMore(data);
});
}
function doMore(data) {
$.each(data, function(key, value) {
secondFactory.getData(value).success(function(result) {
var obj = {
test: result.test,
test2: value.test
};
objArray.push(obj);
});
});
$scope.data = objArray;
}
});
For the second call you can use $q.all. This would get resolved only when all the calls are complete
function doMore(data) {
var promises = [];
$.each(data, function (key, value) {
promises.push(secondFactory.getData(value));
});
$q.all(promises).then(function (responseArray) {
$.each(responseArray, function (result) {
var obj = {
test: result.test
};
objArray.push(obj);
});
$scope.data = objArray;
});
}
You can chain promises. A promise in the chain is only run when the previous has executed, or you can even run promises in parallel and with $q.all wait for all promises to finish.
Here is a sample that might help you plunker:
var app = angular.module('plunker', []);
app.factory('dataFactory', function($http, $q) {
var getData = function() {
var combinedData = [];
var deferred = $q.defer();
$http.get('first.json')
.then(function(data) {
return data;
})
.then(function(data) {
firstData = data;
$http.get('second.json').then(function(secondData) {
angular.forEach(firstData.data, function(value, key) {
combinedData.push(value);
});
angular.forEach(secondData.data, function(value, key) {
combinedData.push(value);
});
deferred.resolve(combinedData);
});
});
return deferred.promise;
};
return {
getData: getData
};
});
app.controller('MainCtrl', function($scope, dataFactory) {
$scope.items = [];
var onSuccess = function(data) {
$scope.items = data;
};
var onError = function() {
console.log('error');
};
dataFactory.getData()
.then(onSuccess, onError);
});
Related
I understand that the appropriate method to share data between controllers in Angular.js is by using Factories or Services.
app.controller('Controller1', function($scope, DataService) {
DataService.getValues().then(
function(results) {
// on success
console.log('getValues onsuccess');
});
});
app.controller('Controller2', function($scope, DataService) {
DataService.getValues().then(
function(results) {
// on success
console.log('getValues onsuccess');
});
});
app.factory('DataService', function($http) {
var getValues = function() {
console.log('making http request');
return $http.get("/api/getValues");
};
return {
getValues: getValues
}
});
I have two controllers calling the same method in a factory twice
and this is perfectly fine and everything is working as it should. My only concer is that it seems a bit unecessary to make the same request twice? Would the use of $broadcast be a better approach?
Or could i structure my code differenty so that the service is called only once?
You could store the results of the request in the factory and retrieve those instead.
app.factory('DataService', function($http) {
var values;
var requestValues = function() {
console.log('making http request');
$http.get("/api/getValues").then(
function(results){
values = results;
});
};
var getValues = function() {
return values;
};
return {
requestValues : requestValues,
getValues: getValues
}
});
If your data is somekind of static and may not change very often over time you could do something like:
app.factory('DataService', function($http) {
self = this;
this.isLoaded = false;
this.results;
this.getValues = function() {
console.log('making http request');
$http.get("/api/getValues").then(
function(results) {
// on success
console.log('getValues onsuccess');
self.isLoaded = true
this.results = results;
return results;
})
);
};
})
And in the controller:
app.controller('Controller2', function($scope, DataService) {
if(!DataService.isLoaded){
results = DataService.getValues()
}else{
results = DataService.results;
}
});
You should consider caching in your DataService. Add a variable to hold the result from the http service and a time-stamp variable to store the time it was retrieved.
If a second call to the service is within a preset time period (lets say, 5 seconds), then http call is not made and data from the cache is returned.
app.factory('DataService', function($http) {
var cachedValue = null;
var lastGet = null;
var getValues = function() {
var timeNow = new Date();
if (cachedValue == null || ((timeNow - lastGet) < 5000)) {
console.log('making http request');
lastGet = timeNow;
cachedValue = $http.get("/api/getValues");
} else console.log('returning cached value');
return cachedValue;
};
return {
getValues: getValues
}
});
I'm using &http in angular js to make REST calls. And when I make a new call, I want to dismiss the previous call. So I wrap the data call in the service and call the service in the controller. And I'm using the global parameter to save the last call object. So whenever I call the function getsth(), it will replace the lastcall with the new one. But when I debug, it did replace the lastcall with new one, but the previous then still triggers. One solution is the cancel the previous call and I tried it works. But my question is can I overwrite the $http object so that I don't have to handle it. Thanks
Controller:
var lastCall;
$scope.getsth = function(){
lastcall = service.datacall();
lastcall.then()
}
Service:
service.datacall = function(){
var promises = [];
promises.push($http({url:method...}).then(function))
return $q.all(promises);
}
This blogpost explains your use-case pretty well:
http://odetocode.com/blogs/scott/archive/2014/04/24/canceling-http-requests-in-angularjs.aspx
app.factory("movies", function($http, $q){
var getById = function(id){
var canceller = $q.defer();
var cancel = function(reason){
canceller.resolve(reason);
};
var promise =
$http.get("/api/movies/slow/" + id, { timeout: canceller.promise})
.then(function(response){
return response.data;
});
return {
promise: promise,
cancel: cancel
};
};
return {
getById: getById
};
});
app.controller("mainController", function($scope, movies) {
$scope.movies = [];
$scope.requests = [];
$scope.id = 1;
$scope.start = function(){
var request = movies.getById($scope.id++);
$scope.requests.push(request);
request.promise.then(function(movie){
$scope.movies.push(movie);
clearRequest(request);
}, function(reason){
console.log(reason);
});
};
$scope.cancel = function(request){
request.cancel("User cancelled");
clearRequest(request);
};
var clearRequest = function(request){
$scope.requests.splice($scope.requests.indexOf(request), 1);
};
});
This question already has answers here:
Share data between AngularJS controllers
(11 answers)
Closed 2 years ago.
i have tow controller in angularjs. if one controller change data other controller display updated data. in fact first controller has a event that it occur second controller display it. for this propose i wrote a service. this service has tow function. here is my service code.
app.service('sharedData', function ($http) {
var data=[]
return {
setData: function () {
$http.get('/getData').success(function(response){
data = response;
})
},
getData: function(){
return data;
}
}
});
in first controller
app.controller("FirstController", function ($scope, $http,sharedData)
{
$scope.handleGesture = function ($event)
{
sharedData.setData();
};
});
in second controller:
app.controller("SecondController", function ($scope,sharedData) {
var data=[];
data = sharedData.getData();
}
);
in first controller setData work with out any problem but in second controller not work correctly. how to share data dynamically between tow controllers?
You are on the right track with trying to share data between controllers but you are missing some key points. The problem is that SecondController gets loaded when the app runs so it calls sharedData.getData() even though the call to setData in the firstController does not happen yet. Therefore, you will always get an empty array when you call sharedData.getData().To solve this, you must use promises which tells you when the service has data available to you. Modify your service like below:
app.service('sharedData', function ($http, $q) {
var data=[];
var deferred = $q.defer();
return {
setData: function () {
$http.get('/getData').success(function(response){
data = response;
deferred.resolve(response);
})
},
init: function(){
return deferred.promise;
},
data: data
}
})
And the secondController like this:
app.controller("SecondController", function ($scope,sharedData) {
var data=[];
sharedData.init().then(function() {
data = sharedData.data;
});
});
For more info on promises, https://docs.angularjs.org/api/ng/service/$q
You had multiple syntax problems, like service name is SharedData and you using it as SharedDataRange, the service is getting returned before the get function.
What I have done is corrected all the syntax errors and compiled into a plunkr for you to have a look. Just look at the console and I am getting the data array which was set earlier in the setter.
Javascript:
var app = angular.module('plunker', []);
app.controller("FirstController", function ($scope,sharedDateRange)
{
sharedDateRange.setData();
});
app.controller("SecondController", function ($scope,sharedDateRange) {
var data=[];
data = sharedDateRange.getData();
console.log(data);
});
app.service('sharedDateRange', function ($http) {
var data=[];
return {
setData: function () {
data = ['1','2','3'];
}
,
getData: function(){
return data;
}
}
});
Working Example
If you want to keep sharedDataRange as the variable name and service name as sharedData have a look at this example
javascript:
var app = angular.module('plunker', []);
app.controller("FirstController", ['$scope','sharedData', function ($scope,sharedDateRange)
{
sharedDateRange.setData();
}]);
app.controller("SecondController", ['$scope','sharedData', function ($scope,sharedDateRange) {
var data=[];
data = sharedDateRange.getData();
console.log(data);
}]);
app.service('sharedData', function ($http) {
var data=[];
return {
setData: function () {
data = ['1','2','3'];
}
,
getData: function(){
return data;
}
}
});
You can bind the data object on the service to your second controller.
app.service('sharedData', function ($http) {
var ret = {
data: [],
setData: function () {
$http.get('/getData').success(function(response){
data = response;
});
}
};
return ret;
});
app.controller("FirstController", function ($scope, sharedData) {
$scope.handleGesture = function () {
sharedData.setData();
};
});
app.controller("SecondController", function ($scope, sharedData) {
$scope.data = sharedData.data;
});
What you need is a singleton. The service sharedData needs to be a single instance preferably a static object having a static data member. That way you can share the data between different controllers. Here is the modified version
var app = angular.module('app', []);
app.factory('sharedData', function ($http) {
var sharedData = function()
{
this.data = [];
}
sharedData.setData = function()
{
//$http.get('/getData').success(function(response){
this.data = "dummy";
//})
}
sharedData.getData = function()
{
return this.data;
}
return sharedData;
})
.controller("FirstController", function ($scope, $http,sharedData)
{
sharedData.setData();
})
.controller("SecondController", function ($scope,sharedData) {
$scope.data=sharedData.getData();
});
I have removed the event for testing and removed the $http get for now. You can check out this link for a working demo:
http://jsfiddle.net/p8zzuju9/
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);
I'm using the services directive in Angularjs not factory and I need to populate a json file to local variable;
/* Contains projects on the town */
leMaireServicess.service('cityService', function($http) {
// JSON regions and cities loader
this.cities = [];
// initCities
this.initCities = function() {
this.cities = $http.get('data/census/cities.js').success(function(data) {
return data;
});
return this.cities;
};
// Get city info
this.getCity = function() {
return this.cities;
};
});
And in my controller I have
// Saved game controller
leMaireControllers.controller('GameCoreCtrl', function($scope, cityService) {
/* Control the town project slides */
cityService.initCities();
$scope.city = cityService.getCity();
console.log($scope.city);
});
But instead of returning the actual data, it returns;
Object {then: function, catch: function, finally: function, success: function, error: function}
You can use a watch to make this work (see plunker)
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope,cityService) {
//$scope.cities = [];
$scope.service = cityService;
cityService.initCities();
$scope.$watch('service.getCity()', function(newVal) {
$scope.cities = newVal;
console.log(newVal)
});
});
app.service('cityService', function($http) {
var that = this;
this.cities = [];
this.initCities = function() {
$http.get('data.js').success(function(data) {
that.cities = data.cities;
});
};
this.getCity = function() {
return this.cities;
};
});
$http returns a promise which is what you're setting this.cities to.
This might help explain more,
https://stackoverflow.com/a/12513509/89702
In your controller you should be able to do something like this...
cityService.initCity().then(function(data) { $scope.city = data; }
You are working with promises which represent the result of an action that is performed asynchronously. Try it this way:
leMaireServicess.service('cityService', function($http) {
this.promise = {};
// initCities
this.initCities = function() {
this.promise = $http.get('data/census/cities.js');
};
// Get city info
this.getCity = function() {
return this.promise;
};
});
And in the controller you need to put your code in a callback:
// Saved game controller
leMaireControllers.controller('GameCoreCtrl', function($scope, cityService) {
/* Control the town project slides */
cityService.initCities();
cityService.getCity().then(function(result){
$scope.city = result.data;
console.log($scope.city);
});
});