My web application uses AngularJS to display a list of data from an external API via an $http call. The data is fetched inside an ng-repeat by a function call.
HTML:
// 'events' does not hold all data; call getData() to get the full data
<div ng-repeat="event in events">
{{getFullEventData(event.key)}}
{{full_event_data.name}}
{{full_event_data.information}}
</div>
AngularJS controller:
// Fetch and store data in 'events' scope variable
$scope.events = ...
// Fetch and store data in 'full_event_data' scope variable,
// because 'events' does not have all the data for each event
var allEventData = [];
$http({
url: url,
method: "GET"
}).success(function(data, status, headers, config) {
allEventData = data;
}).error(function(data, status, headers, config) {
// ...
});
$scope.getFullEventData = function(key) {
for (var i = 0; i < allEventData.length; i++) {
var fetchedEvent = allEventData[i];
if (fetchedEvent.key === key) {
$scope.full_event_data = fetchedEvent;
}
}
}
However this does not print the event name and information in the HTML (these fields are just blank). I suspect that getFullEventData() is being called before the HTTP request has finished fetching the additional data. Can anyone advise?
Actually #charlietfl is correct, but the answer can be completed with more code.
The idea is fetching additional data and then merge them with existing data.
// Fetch and store data in 'events' scope variable
$scope.events = ...
var additionalData;
// Fetch full event data
$http({
url: url,
method: "GET"
}).success(function(data, status, headers, config) {
// assign additional data into events
additionalData = data; // keep a copy of data for future use
assignData(data);
}).error(function(data, status, headers, config) {
// ...
});
function assignData() {
angular.forEach($scope.events, function(event) {
// find matching fetched event for each event in $scope.events
var fetchedEvent = additionalData.find(function(item) {
return event.key === item.key;
});
if (fetchedEvent) {
// additional info is merged into original object
angular.extend(event, fetchedEvent);
}
});
}
<div ng-repeat="event in events">
{{event.name}}
{{event.information}}
</div>
Your function doesn't make much sense.
You should be able to simply do:
<div ng-repeat="event in data">
{{event.name}}
{{event.information}}
</div>
IMO, your requeseted data from $http doesn't notify the angular to trigger change detection. So when the data arrives, getFullEventData() will not be triggered at that moment.
You have to find a way to trigger detection. like $scope.$apply or $timeout.
Related
This question already has answers here:
AngularJS : returning data from service to controller
(2 answers)
Why are Callbacks from Promise `.then` Methods an Anti-Pattern
(2 answers)
Why are AngularJS $http success/error methods deprecated? Removed from v1.6?
(2 answers)
Closed 4 years ago.
About 1 in every 10 times I refresh my project, the data from my database is not retrieved and I have to refresh the page again to get it. I tried adding a 101ms timeout onto the function which retrieves the data, and this worked- but also removed a lot of other functionality of the site.
The problem is that sometimes the page loads before the data is retrieved.
Is there a better way to solve this than using a timeout? My code for the POST is below:
get_data: function (kpi, date_from, date_to, callback) {
var config = {
method: 'POST',
url: '/getData',
data: {
kpi: kpi,
date_from: date_from,
date_to: date_to
}
};
$http(config)
.success(function (data) {
callback(data);
})
.error(function (data, status, headers, config) {
console.log(data, status, headers, config);
});
}
And then where this method is called (currently with the timeout, which is breaking other site functionality):
$scope.update_all_data = $timeout(function(){
$scope.show_loading = true;
var date_from = findDateUnix($scope.myDateFrom, $scope.availableDates);
var date_to = findDateUnix($scope.myDateTo, $scope.availableDates);
UpdateSvc.get_data($scope.kpi_selected, date_from, date_to, function(res){
raw_data = [];
if(res.raw_data != null) {
if(res.raw_data.length > 0){
raw_data = res.raw_data;
skey_data = [];
if(res.skey_data != null) {
skey_data = res.skey_data;
}
var num = $scope.show_tab.indexOf(true);
$scope.filtering = generateCategories();
$timeout(function(){
$scope.display_nodata = false;
resizeObjects();
$('.md-datepicker-input').prop('disabled', true);
//Take out loading
$scope.show_loading = false;
}, 100);
}else{
$scope.display_nodata = true;
$timeout(function(){ $scope.show_loading = false;}, 100);
}
}else{
$scope.display_nodata = true;
$timeout(function(){ $scope.show_loading = false;}, 100);
}
});
}, 101);
The problem is that sometimes the page loads before the data is retrieved.
I'm not sure what you mean by this. In an AngularJS application, the entire page must be loaded before AngularJS even begins to work. So your data (retreived with $http) will ALWAYS be loaded after the page loads, unless it is injected server-side.
I don't think you are having any issues retrieving the data from $http. You can easily confirm this by eliminating or commenting out all the code that does not deal directly with data retrieval. I would also strongly recommend the following changes:
First, using callbacks with $http is an antipattern: Why are Callbacks from Promise `.then` Methods an Anti-Pattern
(Note: newer versions of AngularJS use then/catch instead of success/error)
You should, instead use the built-in promise system. Calls to $http return a promise object that can be used directly like so:
get_data: function (kpi, date_from, date_to) {
var config = {
method: 'POST',
url: '/getData',
data: {
kpi: kpi,
date_from: date_from,
date_to: date_to
}
};
// return the $http call
return $http(config)
.error(function (data, status, headers, config) {
console.log(data, status, headers, config);
});
}
There is not really any reason to call $timeout in update_all_data function. You are adding 101 seconds AFTER your $http call has already returned. All that does is delay the display of the data further. Here, we are also calling the success function on the promise returned from the $http call in the service:
$scope.update_all_data = function(){
var date_from = findDateUnix($scope.myDateFrom, $scope.availableDates);
var date_to = findDateUnix($scope.myDateTo, $scope.availableDates);
UpdateSvc.get_data($scope.kpi_selected, date_from, date_to).success(function(data) {
console.log(data);
});
});
If this works and your data is logged 10/10 times then the issue is not with data retreival, it is with the rest of the code in your update_all_data function.
I have an Angular form connected to a REST API to insert a new row to a MySQL database. My REST API is working, but I am getting a CORS blocked notice in my web console. How can I get it to successfully send to the REST PI? Below is my controller code code:
countryApp.controller('AddLocation', function ($scope, $http) {
$scope.orglocation = {};
//
$scope.submit = function() {
var dataObj = {
location_title : $scope.location.location_title,
location_latitude : $scope.location.location_latitude,
location_longitude : $scope.location.location_longitude
}
//convert data to JSON string
var loc = JSON.stringify(dataObj);
$http.post('http://localhost/slimtest2/add_location', loc).
success(function(data, status, headers, config) {
alert("good");
}).
error(function(data, status, headers, config) {
alert("bye");
});
}
$scope.reset = function() {
$scope.location = angular.copy($scope.orglocation);
}
});
Generally when you hit a CORS problem it is a sign that your server is not configured correctly. I highly suggest looking at the MDN article on HTTP access control. The two headers that are most important to have in this case are:
access-control-allow-origin
access-control-allow-methods
Make sure the first is set to the domain and port you use to access your Angular app and that the second is set to the methods you use (in this case, at least POST).
I have a Service which uses $http to 'get' some JSON data from a REST API. The Controller uses the Service to get this data and initialise it in the $scope. How can I make it so that every time the JSON API changes that change is updated in the $scope, and thus the view?
Controller:
app.controller('MainController', function ($scope, BuildService) {
init();
function init() {
BuildService.getBuilds().then(function() {
$scope.builds = BuildService.data();
});
}
});
Service:
app.service('BuildService', function ($http, $q) {
var BuildService = {};
var deffered = $q.defer();
var data = [];
BuildService.getBuilds = function () {
//return builds;
$http.get('/api').
success(function(d, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
data = d;
deffered.resolve();
}).
error(function(d, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
deffered.reject();
});
return deffered.promise;
};
BuildService.data = function() { return data; };
return BuildService;
});
This question is not AngularJS-specific. What you want to achieve is a real-time app.
Method 1: polling
Use $interval to check JSON API every 30 seconds or so.
Method 2: WebSocket-based notification
If you have control over the JSON API, you can create another WebSocket-based notification API. Whenever JSON API changes, notify client to fetch JSON API again.
I'd say you really have too much unneccessary logic. Keep it simple and just go like that. If you want to reuse the GET you can do it in a getBuilds controller method.
app.controller('MainController', function ($scope, $http, BuildService) {
init();
function init() {
$http.get('/api').
success(function(data) {
$scope.builds = data;
});
}
});
This is my first attempt at using Angularjs framework. I am trying to follow this example: http://jsfiddle.net/SAWsA/11/
I am successfully able to get the data in the Json format and it works fine.
json data:
[{"Activity_Matrix_ID":"163","Activity_ID":"131","Activity_Date":"2062-02-16","Activity_Category":"Maintanence","Activity_Project":"All Projects","Activity_Description":"Json data ","Activity_Hours":"2"},{"Activity_Matrix_ID":"161","Activity_ID":"129","Activity_Date":"2044-02-25","Activity_Category":"Tech Support","Activity_Project":"All Projects","Activity_Description":"Dummy dummy ","Activity_Hours":""}]
So basically, I want to load the data in $scope.items. I am not sure if it is the good method. I can visit the url and the data looks fine. I am stuck at getting the json correctly from the URL to the angular scope.
I tried following
<script type="text/javascript">
var sortingOrder = 'Activity_Projects';
</script>
<script>
function ctrlRead($scope, $filter) {
$scope.myData = function(item, event) {
var responsePromise = $http.get({method: 'GET', url: 'https://url_root_same_domain/timesheet/timesheet_info_table_json.php'}).success(function(data, status, headers, config) {
$scope.items = data;
console.log(data);
}).
error(function(data, status, headers, config) {
alert("No data");
});
}
</script>
Try this:
var responsePromise = $http.get('https://url_root_same_domain/timesheet/timesheet_info_table_json.php').success(...rest of your code here
The $http.get() function's first argument is a URL; not an object; and the method of a get call is already get, so you shouldn't have to do any other changes.
So I have a Resource defined as follows:
angular.module('questionService', ['ngResource'])
.factory('QuestionService', function($http, $resource) {
var QuestionService = $resource('/api/questions/:key', {}, {
query: {
method:'GET',
isArray:true,
},
save: {
method: 'POST',
}
});
return QuestionService
});
And later on I take some form input and do the following
var newQ = {
txt: $scope.addTxt
};
QuestionService.save(newQ)
The server responds to the POST request both by reissuing the JSON for the object and setting the location header with the new unique ID. The problem is that Angular is not saving that returned JSON or the location header into the object and it is not getting set with the ID from the server for future operations. I've tried a number of things such as:
transformResponse: function(data, headersGetter) {
locationHeader = headersGetter().location;
key = locationHeader.split('/').slice(-1)[0];
data.key = key;
return data;
}
However the returned data item doesn't seem to be getting used. Am I missing something? This seems like a pretty common use case for interacting with a REST api.
Thanks!
You need to have a success handler to assign the new id to newQ manually:
QuestionService.save(newQ, function(data) {
newQ.id = data.id;
});
But there is a better to achieve the same. Because your QuestionService is a resource class object, you can instantiate your newQ like this:
var newQ = new QuestionService({text: $scope.addTxt});
Then, call $save() of newQ:
newQ.$save();
Now newQ should have the new id returned by the server.
I have Asp.Net server with WebApi2 running, i use Ok(number) to return content, and it return for example '6' as result. once it return, the angular show an object containing promise and state, and prototypes and a deep repeated hierarchy, but no value, no '6'.
So i went to see where the data goes, for seeing where the data is i define a transform, and i see awo the data, but it's not a json...
later i change it to following, and now i have a obj in my success function, which has sub property called 'returnValue' (as i defined), and this object hold my value.
transformResponse: function(data, header){
var obj={};
obj.returnValue=angular.fromJson(data);
return obj;
},