My app had this .run
.run(function ($rootScope, $http) {
$rootScope.server = "http://127.0.0.1:5000/rawDemo";
var call = $rootScope.server + "/frontEnd/GetStructure";
var texts = {};
texts.languages = {};
$http.get(call).then(function (response) {
for (var i = 0; i < response.data.languages.length; i++) {
texts.languages[response.data.languages[i].iso2] = {
'title': response.data.languages[i].title,
'description': response.data.languages[i].description,
'keywords': response.data.languages[i].keywords,
'frontEndTexts': response.data.languages[i].frontEndTexts
};
}
$rootScope.texts = texts;
$rootScope.webshop = response.data;
$rootScope.webshop.language = response.data.culture.language;
$rootScope.webshop.numberFormat = "";
$rootScope.carouselData = response.data.frontEndConfig.customConfiguration.mjCarousel;
console.log('end run');
});
})
.And some of my resolvers perform a call in a service...
angular
.module('app')
.factory('Products',['$http', '$rootScope', function($http, $rootScope){
return {
listByCategories : function (categories){
console.log('begin service');
var call = $rootScope.server + '/products/list/categories/' + categories.fullPath +'?page1&recordsPerPage=2' ;
return $http.get(call).then(function(response) {
return response.data;
});
}
}
}])
My expected result in console.log supposed to be:
- end run
- begin service
but instead..begin service starts before the end run. It's because the .run finish and keep the $http executing asynchronously and go on to the next stages like resolvers, for example.
According to my research on this website, it's impossible to make the $http works synchronous. So, my question is...any hint about how to handle the scenario ? My service depends from data that's must be loaded before anything.
I cannot "merge" the .run and the service because all the others views, services, must have the .run executed before. in my .run I load dozen of global configurations basically.
UPDATE : I'm still stucked...but I'm trying something...I changed the way I'm doing the things..
so, on my .run
$rootScope.loadWebshop = function(callBack) {
if($rootScope.loaded) { return ; }
var call = $rootScope.server + "/frontEnd/GetStructure";
var texts = {};
texts.languages = {};
$http.get(call).then(function (response) {
for (var i = 0; i < response.data.languages.length; i++) {
texts.languages[response.data.languages[i].iso2] = {
'title': response.data.languages[i].title,
'description': response.data.languages[i].description,
'keywords': response.data.languages[i].keywords,
'frontEndTexts': response.data.languages[i].frontEndTexts
};
}
$rootScope.texts = texts;
$rootScope.webshop = response.data;
$rootScope.webshop.language = response.data.culture.language;
$rootScope.webshop.numberFormat = "";
$rootScope.carouselData = response.data.frontEndConfig.customConfiguration.mjCarousel;
$rootScope.loaded = true;
console.log('end .run');
callBack();
});
}
..And on my service :
listByCategories : function (categories){
return $rootScope.loadWebshop (function () {
console.log('begin service');
var call = $rootScope.server + '/products/list/categories/' + categories.fullPath +'?page1&recordsPerPage=2' ;
return $http.get(call).then(function(response) {
return response.data;
});
});
}
Now I'm facing a different issue...it's returns undefined because it's asyncronous, even using premise. Any clue ??
Related
I'm using a service in order to pass data between different instances of an AngularJS controller. I know that this is not the best way to do it but it's the way that fits my case. The problem is that I cannot get data out of that Service.
var app = angular.module('MovieApp', ['ngResource']);
app.factory('factMovies', function($resource) { //this returns some movies from MongoDB
return $resource('/movies');
});
app.service('SnapshotService', function(factMovies) {
//this is used to pass data to different instances of the same controller
//omitted getters/setters
this.snapshots = [];
this.init = function() {
var ctrl = this;
var resp = factMovies.query({}, function() {
if (resp.error) {
console.log(resp.error)
} else {
tempDataset = []
//do stuff and put the results in tempDataset
ctrl.snapshots.push(tempDataset);
console.log(tempDataset); //prints fine
return tempDataset;
}
});
};
});
app.controller('TileController', function(SnapshotService) {
this.dataset = [];
this.filters = [];
this.init = function() {
var ctrl = this;
var data = SnapshotService.init(function() {
console.log(ctrl.data); //doesn't even get to the callback function
});
};
});
I really can't figure out what I'm doing wrong..
SnapshotService.init() doesn't take any parameters - meaning the anonymous function you pass in with the SnapshotService.init() call in TileController does nothing.
What you need to do is add the parameter to the init function definition and then call it in the code:
app.service('SnapshotService', function(factMovies) {
//this is used to pass data to different instances of the same controller
//omitted getters/setters
this.snapshots = [];
this.init = function(cb) {
var ctrl = this;
var resp = factMovies.query({}, function() {
if (resp.error) {
console.log(resp.error)
} else {
tempDataset = []
//do stuff and put the results in tempDataset
ctrl.snapshots.push(tempDataset);
console.log(tempDataset); //prints fine
cb(ctrl.snapshots);
}
});
};
});
I have a simple to-do app I'm working on, which is using Angular and then PHP/MySQL for the backend.
I now have a simple app that works, where I can add new todos, and add the "percentage completed" per day to the Database, using a simple $http post.
However now what I'm looking to do is, populate the $scope.historicalDailyPercentages array, with data from the database.
At the start of the script, I init the object like so:
$scope.historicalDailyPercentages = []; //TODO, this should be initialised with data from the database.
I understand I'll need to have some sort of $http get loop in there, to check for the data and fill the object, but I'm a little unclear on how to get started there.
The entire goalzy.js script is below for reference. Thanks in advance!
angular.module('goalzy', [])
.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.headers.post['Content-Type'] = 'application/json; charset=utf-8';
}])
.controller('TodoController', ['$scope', '$http', function($scope, $http) {
$scope.todos = [];
$scope.historicalDailyPercentages = []; //TODO, this should be initialised with data from the database.
$scope.addTodo = function() {
if ($scope.todoText != "") {
if ($scope.todos.length < 3) {
$scope.todos.push({
text: $scope.todoText,
done: false
});
$scope.todoText = '';
//Save to DB
} else {
alert("You can only have 3 todos per day!");
$scope.todoText = '';
}
} else {
alert("you must write something");
}
};
$scope.remaining = function() {
var count = 0;
angular.forEach($scope.todos, function(todo) {
count += todo.done ? 0 : 1;
});
return count;
};
$scope.percentComplete = function() {
var countCompleted = 0;
angular.forEach($scope.todos, function(todo) {
countCompleted += todo.done ? 1 : 0; //Simply calculates how many tasks have been completed
console.log(countCompleted);
});
var totalCount = $scope.todos.length;
var percentComplete = countCompleted / totalCount * 100;
return percentComplete;
}
$scope.finaliseDay = function(percentComplete) {
alert("You're finalising this day with a percentage of: " + percentComplete);
var today = new Date();
var alreadyPresent = $scope.historicalDailyPercentages.some(function(item) {
return item.date.getFullYear() === today.getFullYear() &&
item.date.getMonth() === today.getMonth() &&
item.date.getDate() === today.getDate();
});
//Confirm that nothing has alreayd been posted for today
if (!alreadyPresent) {
// Simple POST request example (passing data)
$http.post('/postDailyPercentage.php', {
user_id: 1,
percent: percentComplete,
date: today
}).
success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
if (data) {
$scope.historicalDailyPercentages.push({
user_id: 1,
percent: percentComplete,
date: today
});
} else {
alert("Something went wrong" + data);
}
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
console.log("Post failure");
});
} else {
alert("You're all set for today - see you tomorrow!");
}
//console.log($scope.historicalDailyPercentages);
}
}]);
To populate that object with an $http.get you can do it as follows:
function getHistoricalDataSuccess(data) {
$scope.historicalDailyPercentages = data;
}
function getHistoricalDataError(error) {
//handle the error
}
$http.get('path/to/api')
.success(getHistoricalDataSuccess)
.error(getHistoricalDataError);
var TodoController = function($scope, HistoricalDailyPercentageService) {
HistoricalDailyPercentageService.get().then(function(percentages) {
$scope.historicalDailyPercentages = percentages;
}, function(error) {
alert(error);
});
};
var HistoricalDailyPercentageService = function($http) {
this.get = function() {
return $http.get('yourUrl')
.then(function(xhr) {
var data = xhr.data;
// Transform the data as you see fit
return data;
}, function(xhr) {
// xhr contains the error message - modify this as you see fit.
return xhr.code;
});
};
};
angular.module('goalzy')
.controller('TodoController', ['$scope', 'HistoricalDailyPercentages', TodoController])
.service('HistoricalDailyPercentageService', ['$http', HistoricalDailyPercentageService]);
I would recommend doing it this way; this will make it easier to test by taking the logic of getting the data out of your already busy controller. #RVandersteen's example will only work inside of your controller, which is fine, but it really does make your controller very busy; controllers should really only assign things to a scope, everything else should be handled in a directive (for example, binding events to methods) or a service/factory/provider (for business logic).
After you have finished up your code, could you post on CodeReview? There's a few improvements I could suggest but they are merely review-based things and not appropriate for the scope of this question.
It's worth noting by the way that because I am using then in the controller I must use then in the service, too. If I use success in the service then my changes will not be reflected when I call then in the controller.
I'm using a controller to load product data into an $rootScope array. I'm using $http service and works fine, but now I have a new function which fetch the number of products to be loaded. I can't use the function cause the response is slow.
I was wondering if I could use a provider to load the number of products to fetch in the config method before the apps start. And if I could move the $rootScope array to one service. I don't understand Angular docs, they are not really useful even the tutorial at least in providers and services...
app.controller('AppController', [ '$rootScope', '$http', function ( $rootScope,$http) {
$rootScope.empty = 0;
$rootScope.products = [];
$rootScope.lastId = 0;
$rootScope.getLastID = function () {
$http.get("app_dev.php/api/products?op=getLastId").success(function (data) {
$rootScope.lastId = data.lastId;
});
};
$rootScope.getProducts = function () {
if ($rootScope.empty === 0) {
for (i = 1; i < 100; i++) {
$http.get("app_dev.php/api/product/" + i).success(function (data) {
$rootScope.products.push(data);
});
}
}
$rootScope.empty.productos = 1;
};
}
I have done this with factory and service but is not working.
app.factory('lastProduct', ['$http', function lastProductFactory($http) {
this.lastId;
var getLast = function () {
$http.get("app_dev.php/api/products?op=getLastId").success(function (data) {
lastId = data.lastId;
});
return lastId;
};
var lastProduct = getLast();
return lastProduct;
}]);
function productList($http, lastProduct) {
this.empty = 0;
this.lastId = lastProduct();
this.products = []
/*this.getLast = function () {
lastId = lastProduct();
};*/
this.getProducts = function () {
if (empty === 0) {
for (i = 1; i < lastId; i++) {
$http.get("app_dev.php/api/product/" + i).success(function (data) {
products.push(data);
});
}
}
empty = 1;
return products;
};
}
app.service('productsList', ['$http', 'lastProduct' , ProductsList]);
services are not availables during configuration time, only providers hence you can not use $http to get a value inside the configuration block, but you can use the run block,
you can do
angular.module('app',['dependencies']).
config(function(){
//configs
})
.run(function(service){
service.gerValue()
})
setting the retrieved value inside a service or inside a value is a good idea to avoid contaminate the root scope, and this way the value gets retrieved before the services are instantiated and you can inject the retrieved value as a dependency
Making that many small $http requests does not seem like a good idea. But using a factory to store an array of data to be used across controllers would look something like this. To use a factory you need to return the exposed api. (The this style is used when using a service. I suggest googling the different but I prefer factories). And if you need to alert other controllers that data has changed you can use events.
angular
.module('myApp')
.factory('myData', myData);
function myData($http, $rootScope) {
var myArray = [], lastId;
return {
set: function(data) {
$http
.get('/path/to/data')
.then(function(newData) {
myArray = newData;
$rootScope.$broadcast('GOT_DATA', myArray);
})
},
get: function() {
return myArray
},
getLast: function() {
$http
.get('/path/to/data/last')
.then(function(last) {
lastId = last;
$rootScope.$broadcast('GOT_LAST', lastId);
})
}
}
}
And then from any controller you can inject the factory and get and set the data as you see fit.
angular
.module('myApp')
.controller('MainCtrl', MainCtrl);
function MainCtrl($scope, myData) {
$scope.bindableData = myData.get(); // get default data;
$scope.$on('GOT_DATA', function(event, data) {
$scope.bindableData = data;
})
}
I hope this helps. Let me know if you have any questions.
I done this but not working . rootScope total is undefined when set method is called from some controller.
http://imgur.com/qEl5WV5
But using 10 instead rootscope total
http://imgur.com/t6wB3JZ
I could see that the rootScope total var arrive before the others...
(app_dev.php/api/productos?op=ultimaIdProductos) vs (app_dev.php/api/producto/x)
var app = angular.module('webui', [$http, $rootScope]);
app.run (function ($http, $rootScope){
$http.get("app_dev.php/api/products?op=getLastId").success(function (data) {
$rootScope.total = data.ultima;
});
});
function myData($http, $rootScope) {
var myArray = [];
return {
set: function () {
console.log($rootScope.total);
for (i = 1; i < $rootScope.total; i++) {
$http.get("app_dev.php/api/product/" + i).success(function (data) {
myArray.push(data);
})
}
},
get: function () {
return myArray;
}
}
}
app.controller('AppController', ['$http', '$rootScope', 'myData', function ($http, $rootScope, myData) {
$rootScope.productos = [];
$rootScope.getProductos = function () {
console.log($rootScope.total);
myData.set();
$rootScope.productos = myData.get();
};
}]);
I have a factory which returns an object with several properties. But each property value is computed by ajax call and in some cases I do promise chaining in order to set a property value. Before I return the object how do i make sure all ajax calls are done such that property values are assigned
My factory looks something like this
app.factory('Resource', ['$http', '$q', function ($http, $q) {
var Resource = {
masterDB: null,
replicaDB: null,
replicaCluster: null,
masterForests: null,
forestHosts:{}
};
Resource.setMasterDB = function (dbname) {
console.log('inside setMasterDB', dbname);
this.masterDB = dbname;
};
Resource.getResources = function (dbname) {
var url = '/manage/v2/databases/'+ dbname + '?format=json';
$http.get(url).then(function (response) {
Resource.masterForests = getAttachedForests(response.data);
console.warn('Master Forests = ', Resource.masterForests);
return response;
}).then(function (response) {
Resource.replicaCluster = getReplicaClusters(response.data);
console.warn('Replica Cluster = ',Resource.replicaCluster);
}).then(function () {
console.log('final then', Resource.masterForests);
var reqs = function () {
var arr = [];
angular.forEach(Resource.masterForests, function(forestName){
arr.push($http.get('/manage/v2/forests/'+ forestName + '?format=json'));
});
return arr;
}.call();
console.log('reqs = ', reqs);
$q.all(reqs).then(function (results) {
console.warn(results);
angular.forEach(results, function(result){
console.log('HOST', getForestHost(result.data));
});
return results;
});
});
};
console.warn('RESOURCES: ', JSON.stringify(Resource));
return Resource;
}]);
We had scenario where the datas have to be updated from two different ajax responses, we have followed the below approach.
For Example:
function functionname()
{
var First_Response = $http.$get("/test1/test2/...");
var Second_Response = $http.$get("test3/test4/...");
return $q.all([First_Response,Second_Response]); // This function will return only when all the ajax responses are obtained.
}
In the below url:
https://docs.angularjs.org/api/ng/service/$q
It is mentioned that $q.all will return result only when all the requests mentioned in the array gets the ajax response.
We tried this approach and it worked for us. Hopefully it will give some pointers
Here's the working code
getResources: function (dbname) {
var url = '/manage/v2/databases/'+ dbname + '?format=json';
return $http.get(url).then(function (response) {
Resource.masterForests = getAttachedForests(response.data);
Resource.appservers = getAttachedAppServers(response.data);
Resource.replicaClusters = getReplicaClusters(response.data);
console.warn('Master Forests = ', Resource.masterForests);
return Resource.replicaClusters;
}).then(function(replicaClusters) {
var clusterArr = [];
angular.forEach(replicaClusters, function(cluster) {
clusterArr.push($http.get('/manage/v2/clusters/'+ cluster + '?format=json'));
});
return $q.all(clusterArr).then(function(results) {
angular.forEach(results, function(result) {
var cluster = result.data['foreign-cluster-default'].name;
var dbs = getReplicaDBs(result.data);
Resource.replicaDBs[cluster] = dbs;
});
});
}).then(function() {
var forestarr = [];
angular.forEach(Resource.masterForests, function(forestName){
forestarr.push($http.get('/manage/v2/forests/'+ forestName + '?format=json'));
});
return $q.all(forestarr).then(function (results) {
angular.forEach(results, function(result){
var host = getForestHost(result.data);
var forest = result.data['forest-default'].name;
Resource.forestHosts.push(host);
// group forest by hosts
groupForestsByHost(Resource, host, forest);
});
});
});
}
In controller
Resource.getResources($scope.db).then(function() {
$scope.masterForests = Resource.masterForests;
$scope.replicaClusters = Resource.replicaClusters;
$scope.forestHosts = Resource.forestHosts;
$scope.forestsOnHosts = Resource.forestsOnHosts;
$scope.replicaDBs = Resource.replicaDBs;
$scope.appservers = Resource.appservers;
}
So my readingController calls getRomanization in the main body the first time and it in turn calls the Romanize factory service (shown below controller). This all works fine the fist time but the second time when round when getRomanization is called at the end of checkRomanization the Romanize service comes up as undefined in Chrome. What happens to the Romanize service? why does it only run successfully once?
Please help me with this as I have been stuck for ages.
var readingController = function ($scope, Romanize){
$scope.getRomanization = function(Romanize){
$scope.currentMaterial = $scope.sections[$scope.sectionNumber].tutorials[$scope.tutorialNumber].material[$scope.questionNumber];
Romanize.get($scope.currentMaterial).then(function(d){
$scope.romanized = d;
});
$scope.$apply();
};
$scope.getRomanization(Romanize);
$scope.$apply();
$scope.checkRomanization = function(userRomanization){
if ($scope.romanized === userRomanization){
$scope.questionNumber++;
$scope.getRomanization();
};
}
}
app.factory('Romanize', ['$http', 'Position', function($http, Position){
return{
get: function(query){
var url= Position.sections[Position.sectionNumber].romanizeService + "?korean=" + query;
var promise = $http.get(url).then(function(d) {
var parts = $(d.data).find("span");
var array = [];
for (var x = 0; x<parts.length; x++){
array.push(parts[x].title);
}
var result = array.join("");
return result;
});
return promise;
}
};
}]);
Your second call to getRomanize is missing the service name as an argument
$scope.checkRomanization = function(userRomanization){
if ($scope.romanized === userRomanization){
$scope.questionNumber++;
$scope.getRomanization(Romanize);
};
};