$http.get to synchronous AngularJS - javascript

Can you help me please im new in angularJS,
i have a problem with Asynchrounous $http.get , i explain : i got data that i show in ngTable but in my html i got a empty table until i click to filter or to sort then i see my data.
i think its because i have a promise in my http.get. here my code to understand more ( sorry for my english)
'use strict';
(function() {
angular
.module('timeShareApp')
.controller('homeController', homeController);
function homeController($http, $scope, $filter, NgTableParams) {
$scope.users =[];
$http.get('/api/users/').success(function(response) {
$scope.users = response;
});
$scope.usersTable = new NgTableParams({
page: 1,
count: 10
}, {
total: $scope.users.length,
getData: function ($defer, params) {
$scope.data = params.sorting() ? $filter('orderBy')($scope.users, params.orderBy()) : $scope.users;
$scope.data = params.filter() ? $filter('filter')($scope.data, params.filter()) : $scope.data;
$scope.data = $scope.data.slice((params.page() - 1) * params.count(), params.page() * params.count());
$defer.resolve($scope.data);
}
});
}
homeController.$inject = ["$http", "$scope", "$filter", "NgTableParams"];
})();
for info : code works perfectly except that promise that i want to convert to synchonous if you can help me please.
Thank you in advance

In most cases, there is no reason to keep any data outside the ng-table's scope. My advice is to not modify or reference any scope variables because this can cause some pretty hard to track timing issues.
Have a look at the really good ng-table docs, which mostly have a working sample for your use case. See the docs here
Depending on the place where your filtering / sorting happens, you need to adapt this, but the following should basically work:
$scope.loadData = function() { return $http.get('/api/users/') };
$scope.usersTable = new NgTableParams(
{
page: 1,
count: 10
}, {
total: 0, // just the inital value - will be updated later on
getData: function ($defer, params) {
$scope.loadData().success(function (result) {
// assuming your result has a total and a data field...
// update the table params with the total amount of results
// could also be result.length...
params.total(result.total);
// sort and filter your data using the params object here
var data = result.data;
// give back the control to the table
$defer.resolve(data);
});
}
}
);
Please be aware to also set the params.total whenever your server responds. Otherwise the pagination will not be visible.

I think there is no problema on adding $scope.usersTable to me defined inside your promise resolve method. Have you tried that?

Related

Dynamically setting up a chain of $http calls to load sequentially numbered json files in Angular 1.2

I've got a sequence of json files (page1.json, page2.json, etc) that contain the layout details for our pages.
Up until now I've been loading the data for each page when it's needed and everything has been working well, but it's now clear that the page data for every page is needed when the angular app starts - and each page must be in a separate file (to please the designers).
Here's what I've been trying:
var app = angular.module("templatesApp", ['bulletComponent', 'popupComponent', 'imageComponent', 'paragraphComponent', 'ngRoute']);
var numberOfPages = 0;
var pageData = [];
var pageIndex = 0;
/*Factory that runs http.get on content.json, used by multiple controllers */
app.factory('content', ['$http', 'completions', 'pages', function($http, completions, pages) {
var url = 'json/content.json';
return $http.get(url)
.success(function(data) {
// loop through pages and add corresponding page objects to completions.js service
for (var i=0; i<data.length; i++) {
completions.addPage(data[i].title);
numberOfPages += 1;
}
loadPageData(pages, completions);
return data;
})
.error(function(data) {
return data;
});
}]);
var loadPageData = function(pages, completions) {
console.log("loadPageData called when pageIndex = "+pageIndex);
pages.success(function(loadedData) {
pageData.push(loadedData);
completions.addPageComponents(pageIndex + 1, pageData[pageData.length-1]);
pageIndex += 1;
if (pageIndex < numberOfPages) {
loadPageData(pages, completions);
}
});
}
app.factory('pages', ['$http', function($http) {
console.log("pages factory called with pageIndex = "+pageIndex);
var url = 'json/page'+ (pageIndex + 1) +'.json';
return $http.get(url)
.success(function(loadedData) {
return loadedData;
})
.error(function(loadedData) {
return loadedData;
});
}]);
/*Controller for menu, data fetched from factory*/
app.controller('menuCtrl', ['$scope', '$http', 'content', function($scope, $http, content) {
content.success(function(data) {
$scope.pages = data;
});
}]);
/*Controller for individual pages. Makes XHR to json page for pages data.
Has function for calculating column widths*/
app.controller('pageCtrl', ['$scope', '$routeParams', '$http', 'content', 'completions', function($scope, $routeParams, $http, content, completions) {
$scope.page = pageData[$routeParams.pageId-1];
$scope.getStyle = function(singleCase, device) {
if (singleCase == undefined)
return '';
return assignBootstrap(singleCase, device);
}
$scope.pageId = $routeParams.pageId;
}]);
So hopefully you can see, the 'content' factory gets called by 'menuCtrl'. It first loads the content.json file (a list of the page titles) then it calls 'loadPageData', a recursive function that should call the 'pages' factory on every required page, as it loads.
The problem is, those console.logs show that the 'pages' factory is getting called just once and BEFORE even the loadPageData function. Each html page is then being passed the same (first) set of data.
I presume I'm not using the 'pages' factory correctly, but I don't know what the correct options are.
If anyone has any pointers, I'd be keen to hear them.
EDIT
Thanks for the comments. I think I've realised what I need to know is: How do I set up a chain of promises ($http calls) dynamically? The number of pages will vary, so I can't hard-code them.
Here's what I ended up doing. Not sure if this is 100% best practise, but it seems to be working well:
app.factory('content', ['$http', 'completions', function($http, completions) {
var url = 'json/content.js';
return $http.get(url)
.success(function(data) {
// loop through pages and add corresponding page objects to completions.js service
for (var i=0; i<data.length; i++) {
completions.addPage(data[i].title);
numberOfPages += 1;
}
// load page .js files
var tempData = getData(0, completions);
for (var i = 1; i < numberOfPages; i++) {
(function (i) {
tempData = tempData.then(function() {
return getData(i, completions);
});
}(i));
}
return data;
})
.error(function(data) {
return data;
});
}]);
// loads individual page data, stores in pageData and sets up SCORM objectives from completable components (in completions service)
function getData(id, completions) {
return $.ajax({
url: 'json/page'+ (id + 1) +'.js',
dataType: 'json'
}).done(function(d) {
// push data into pageData array, for use later when pages are routed to
pageData.push(d);
// register this page's completable components in SCORM
completions.addPageComponents(id + 1, d);
}).fail(function() {
console.log('ERROR loading page index '+id);
});
}
I couldn't make sense of $q.all or promises, but a colleague pointed me to another SO answer that used something similar to the above.
When the 'content' factory is called, it calls the getData function to load the first .js file. Looping through up to the number of pages, I'm guessing the line...
tempData = tempData.then(...
...means that it waits for that load to complete before starting the next. The key thing for storing the currently loaded data is what happens in the getData.done function; The loaded data is pushed into pageData array for use later and the data is also passed into the completions service (to set up our objectives in SCORM).
All working as intended!

Can't pass data between controllers?

I don't get what I'm doing wrong, I am trying to globally store and pass data from one controller to another via a service. I stored the data in one controller and confirmed that it was stored at the beginning of my buildReportController. Then, when I click a button on my UI, it opens reportResultsController. However, issue is, I can store the data correctly in buildReportController via locationHistoryService.store() but when I go to reportResultsController and calllocationHistoryService.get(), thepreviousLocationvariable inlocationHistoryService` is empty as if the data was never set. Any ideas on how why or how I can "globally" store data and pass it between controllers? Below is my attempt. Thanks!
In reportView.js
angular.module('reportView', [])
.service('locationHistoryService', function(){
var previousLocation = "";
return {
store: function(location){
previousLocation = location;
},
get: function(){
return previousLocation;
}
};
});
In buildReport.js
angular.module('buildReport', ['reportView'])
.controller('buildReportController', ['$rootScope', 'locationHistoryService'], function($rootScope, locationHistoryService){
$rootScope.$on('$locationChangeSuccess', function(e, newLocation, oldLocation){
locationHistoryService.store(oldLocation);
console.log("Old location: ", oldLocation);
});
}
In reportResults.js
angular.module('reportResults', ['reportView'])
.controller('reportResultsController', ['$rootScope', 'locationHistoryService'], function($rootScope, locationHistoryService){
console.log("location : ", locationHistoryService.get());
}
The locationHistoryService.get() method in reportResults.js is called before it is set in buildReport.js.
It would be better if you announce when the previousLocation variable has been set.
In reportView.js
angular.module('reportView', [])
.service('locationHistoryService',['$rootScope'] ,function($rootScope){
var previousLocation = "";
return {
store: function(location){
previousLocation = location;
$rootScope.$broadcast('previous-location-set');
},
get: function(){
return previousLocation;
}
};
});
In reportResults.js
angular.module('reportResults', ['reportView'])
.controller('reportResultsController', ['$rootScope', 'locationHistoryService'], function($rootScope, locationHistoryService){
$rootScope.$on('previous-location-set', function(){
console.log("location : ", locationHistoryService.get());
});
}
Your webapp should have only one module which is automatically bootstrapped by angular, and other modules as dependencies. The syntax of writing service is incorrect. You wrote .service but returning object which .factory should return. Here is working example of your code http://codepen.io/Chyngyz/pen/NxbdpW?editors=101
Also you wrote the safe for minification syntax of controllers incorrect, the function block of controller should be the last item in the array.

AngularJS multiples http.get

Hi guys im a newbie in AngularJS I have a problem calling multiples http.get. $scope.countries is getting values from cities. What happend?
How can calling multiple http.get?
$scope.getInfo = function(){
$scope.refreshing=true;
//cities
$http.get(baseUrl+'cities/GET_INFO/ALL').success(function(data) {
$scope.cities = data[0];
$scope.cities.signal = $scope.getSignal(data[0].status);
$scope.refreshing=false;
alert('city');
});
//countries
$http.get(baseUrl+'countries/GET_INFO/ALL').success(function(data) {
$scope.countries = data[0];
$scope.countries.signal = $scope.getSignal(data[0].status);
$scope.refreshing=false;
// alert('countries');
});
}
Also I tried with:
$scope.getInfo2 = function(){
$scope.refreshing=true;
alert ('start');
$scope.urlcities = $http.get(baseUrl+'cities/GET_INFO/ALL');
$scope.urlcountries = $http.get(baseUrl+'cities/GET_INFO/ALL');
$q.all([$scope.urlcities, $scope.urlcountries]).then(function(values) {
alert('finish');
$scope.refreshing=false;
});
}
But this code get an error.. Thanks so much for your help !
Carlos,
You may have a race condition with the AJAX calls. Try chaining them together using promises:
$scope.getInfo = function(){
$scope.refreshing=true;
//cities
$http.get(baseUrl+'cities/GET_INFO/ALL').then(function(data) {
$scope.cities = data[0];
$scope.cities.signal = $scope.getSignal(data[0].status);
$scope.refreshing=false;
alert('city');
return $http.get(baseUrl+'countries/GET_INFO/ALL');
}).then(function(data) {
// countries
$scope.countries = data[0];
$scope.countries.signal = $scope.getSignal(data[0].status);
$scope.refreshing=false;
// alert('countries');
});
};
To learn more, watch the screencast:
https://egghead.io/lessons/angularjs-chained-promises
You can also learn more about promises here:
https://docs.angularjs.org/api/ng/service/$q
NOTE:
It is a best practice to move your data preparation, business logic and calculations out of the controller and into a service. Consider revising your code to encapsulate your AJAX request (using the $http service) into a service and then inject that service into the controller that is being used to present the data to the view.
You need to make Syncronous calls.
And in Angular world it is achieved using $q or promise.
Good article on that http://haroldrv.com/2015/02/understanding-angularjs-q-service-and-promises/
Hope it helps..

Angular.js render data in controller

I have a rather simple question. I have a simple controller and its $scope.coords = []; renders JSON in HTML:
[24.43359375, 54.6611237221]
[25.2905273438, 54.6738309659]
[25.3344726562, 54.6102549816]
[25.2685546875, 54.6801830971]
[25.2960205078, 54.6611237221]
How can I render that JSON not in html, but in my controller itself ? The code looks like that. Please see the comment in code:
propertyModule.controller('propertyController', ['$scope', 'Property', function ($scope, Property) {
// Query returns an array of objects, MyModel.objects.all() by default
$scope.properties = Property.query();
// Getting a single object
$scope.property = Property.get({pk: 1});
$scope.coords = [];
$scope.properties = Property.query({}, function(data){
console.log(data);
angular.forEach(data , function(value){
$scope.coords.push(value.coordinates);
});
});
$scope.positions = //$Resource('realestate.property').items();
[
[54.6833, 25.2833], [54.67833, 25.3033] // those coordinates are hardcoded now, I want them to be rendered here by $scope.coords
];
}]);
First off, you're showing us a bunch of arrays, not a JSON document. But since your code seems to be working, I'll assume you do have a valid JSON to work with.
You need to consider the fact that you are making an asynchronous request here :
$scope.properties = Property.query({}, function(data) {
console.log(data);
angular.forEach(data , function(value){
$scope.coords.push(value.coordinates);
});
});
This means you won't be able to fetch data from $scope.coords before anything has arrived.
There are several ways to solve that :
You could simply fetch data while you're still in the loop :
angular.forEach(data , function(value) {
$scope.coords.push(value.coordinates);
if('your condition') {
$scope.positions.push(value.coordinates);
}
});
You could use a promise, see the angular doc.
Or you could watch over $scope.coords with $scope.$watch.

Passing data between controllers using service and confusion with using [ '$scope' , 'service' , function($scope, service){}]

I am badly stuck with this problem of passing data from one controller to other in angularJS. Before my code was: whenever I click on templateController's div, it would trigger setTemplate with the help of ng-click... Now my objective was to send templateController's selected data to ReplyController...
After reading forum posts here, i decided to create a service called 'selectionService', so that i can transmit data between 2 controllers...
//Defined Service
proApp.service('selectionService', function() {
var selected_template;
addTemplate = function(newObj) {
selected_template = newObj;
};
getTemplate = function(){
return selected_template;
};
});
//Controller 1... where templates are selected
proApp.controller('TemplateController',function($scope, selectionService)
{
$scope.templates = [
{template_name:"Template 1", template_text:"We apologize for the interruption."} ,
{template_name:"Template 2", template_text:"Thank you for contacting us."} ,
} ,
];
// on ng-click
$scope.setTemplate = function(tmp)
{
selectionService.addTemplate(tmp);
}
});
// Controller 2 ... supposed to catch the selected template.
proApp.controller('ReplyController', function($scope, selectionService)
{
$scope.template = selectionService.getTemplate();
});
So whenever i run this code, i started getting this error
Object [object Object] has no method addTemplate...
Again I tweeked the code where they were suggesting to use Squarebrackets and put $scope , servicename and then write function with same parameters.. I don't understand why I should do this? Event after doing some changes like
[ '$scope' , 'service' , function($scope, service){}] ,
I am still not able to figure out the solution to pass data from one controller to other using service.
Could you help? What am I missing? I am very new to angularJS way of doing stuff.
I think it's actually quite simple. You just need to add your methods to the service by using this.. Currently they are declared on window. Change your service declaration to use this...
proApp.service('selectionService', function() {
var selected_template;
this.addTemplate = function(newObj) {
selected_template = newObj;
};
this.getTemplate = function(){
return selected_template;
};
});
As for using the array notation for dependencies, it's a good practice but it's not required. It'll save you from headaches if you ever run your code through a minifier.

Categories

Resources