I am an angular novice and am working on an app that gets data from an accounting software and visualises different things using google charts.
Since the api of the accounting software doesn't give me the data the way I need it I have to process it before passing it to the google charts api.
No the problem I ran into is that I can't access the data that is returned inside of the http get request function due to scope I guess. I have tried quite a few things, but nothing seems to work. I feel like there should be an obvious solution to this, but can't put my finger on it.
Would be great if someone can help me with a method to expose http request data to make it usable outside of the http function itself.
Here is a code example:
myApp.controller("dataFetch", ['$http', '$scope',function($http, $scope){
var self = this;
self.project = {};
self.TSproject;
self.TShours;
//PASSING AUTHORIZATION
var config = { headers: { 'Authorization': 'Bearer 1lFASlwgM3QwSyZfJVJPO6776X5wlZtogdg8RN-Lt',} };
//GET DATA
$http.get('https://api.freeagent.com/v2/projects/941562', config).then(function(response) {
//SAVING PROJECT DATA
self.project = {
name: response.data.project.name,
url: response.data.project.url
};
return self.project.url;
}, function(errResponse) {
console.error("Get project request failed");
}).then(function(pUrl) {
return
$http.get('https://api.freeagent.com/v2/timeslips?' + 'from_date=2015-09-21&to_date=2015-09-28' + 'user=' + pUrl, config).then(function(response) {
self.TSproject = response.data.timeslips[0].project;
self.TShours = response.data.timeslips[0].hours;
});
});
//GOOGLE CHARTS
$scope.data1 = {};
$scope.data1.dataTable = new google.visualization.DataTable();
$scope.data1.dataTable.addColumn("string","User")
$scope.data1.dataTable.addColumn("number","Qty")
//INSERTING DATA FROM SERVER HERE
$scope.data1.dataTable.addRow([self.TSproject, self.TShours]);
$scope.data1.title="Daniels Timeslips";
}]);
Thanks a lot!
The problem with your code is that you're trying to initialize your charts even before your GET call is complete.
Even though it looks like $http.get() comes first in your code, it's an Asynchronous operation, so JS interpreter will not wait for that call to complete. It will simply fire the AJAX call using $http.get() and continue with the remaining statements in code i.e Google Charts initialization in your case.
When the response is available, we have promises to be invoked after completion of your AJAX call and that is where you're ideally supposed to initialize your charts as they're dependent on that AJAX call response.
Having said that, you can make this work by simply moving your charts initialization to the callback of your second GET request like below.
$http.get('https://api.freeagent.com/v2/projects/941562', config).then(function(response) {
// removing your code for brewity
}, function(errResponse) {
console.error("Get project request failed");
}).then(function(pUrl) {
$http.get('https://api.freeagent.com/v2/timeslips?' + 'from_date=2015-09-21&to_date=2015-09-28' + 'user=' + pUrl, config).then(function(response) {
self.TSproject = response.data.timeslips[0].project;
self.TShours = response.data.timeslips[0].hours;
//GOOGLE CHARTS
$scope.data1 = {};
$scope.data1.dataTable = new google.visualization.DataTable();
$scope.data1.dataTable.addColumn("string","User")
$scope.data1.dataTable.addColumn("number","Qty")
//INSERTING DATA FROM SERVER HERE
$scope.data1.dataTable.addRow([self.TSproject, self.TShours]);
$scope.data1.title="Daniels Timeslips";
});
});
I think you should create the google chart object inside the last http promise.
//GET DATA
$http.get('https://api.freeagent.com/v2/projects/941562', config).then(function(response) {
//SAVING PROJECT DATA
self.project = {
name: response.data.project.name,
url: response.data.project.url
};
return self.project.url;
}, function(errResponse) {
console.error("Get project request failed");
}).then(function(pUrl) {
return
$http.get('https://api.freeagent.com/v2/timeslips?' + 'from_date=2015-09-21&to_date=2015-09-28' + 'user=' + pUrl, config).then(function(response) {
self.TSproject = response.data.timeslips[0].project;
self.TShours = response.data.timeslips[0].hours;
//GOOGLE CHARTS
$scope.data1 = {};
$scope.data1.dataTable = new google.visualization.DataTable();
$scope.data1.dataTable.addColumn("string","User")
$scope.data1.dataTable.addColumn("number","Qty")
//INSERTING DATA FROM SERVER HERE
$scope.data1.dataTable.addRow([self.TSproject, self.TShours]);
$scope.data1.title="Daniels Timeslips";
});
});
Now you should be able to access in your template {{ data1 }} with all the info.
Related
I want to construct a mechanism that would access a database via POST requests. So far, I do received the desired data, but am have issues with the timing. Here are three pieces of code that I'm using (simplified to keep the focus of the question).
First, a factory handling the HTTP request vis-à-vis a servlet:
var My_Web = angular.module('My_Web');
My_Web.factory('DB_Services', function($http , $q) {
var l_Result ;
var DB_Services = function(p_Query) {
var deferred = $q.defer();
var url = "http://localhost:8080/demo/servlets/servlet/Test_ui?";
var params = "data=" + p_Query ;
var Sending = url + params ;
$http.post(Sending).
success(function(data, status, headers, config) {
deferred.resolve(data);
}).
error(function(data, status, headers, config) {
deferred.reject(status);
});
return deferred.promise;
}
return DB_Services;
});
Second, a general purpose function handling the promise (or so I meant) exposed to all the controllers that would need to extract data from the remote DB:
$rootScope.Get_Data_from_DB = function(p_Query) {
DB_Services(p_Query).then(function(d) {
console.log("In Get_Data_from_DB; Received data is: " + JSON.stringify(d));
$scope.data = d;
});
};
Third, one example within one of the controllers:
$scope.Search_Lookups = function () {
console.log ("search for lookup data...") ;
var l_Lookup_Type = document.getElementById("THISONE").value ;
var l_Send_Request_Data = '{"Requestor_ID":"4321" , "Request_Details" : { "Data_type" : "' + l_Lookup_Type + '" } }' ;
console.log("Sending Request: " + l_Send_Request_Data) ;
l_Data = $rootScope.Get_Data_from_DB(p_Query) ;
console.log ("Received response: " + l_Data) ;
Deploy_data(l_Data) ;
}
The function Deploy_data(l_Data) is responsible of dismembering the received data and put the relevant pieces on screen.
What happens is that I get on the console the string Received response: undefined and immediately after the result of the retrieval as In Get_Data_from_DB; Received data is: (here I get the data).
The Received response: undefined is printed from the invoking function (third piece of code), whereas the output with the actual data is received and printed from within the second piece of code above. This means that the invocation to Deploy_data would not receive the extracted data.
Once again, the same mechanism (i.e. the factory $rootScope.Get_Data_from_DB) would be vastly used by many controllers.
I thought of using $scope.$watch but I'm not sure because the same user might be triggering several queries at the same time (e.g. request a report that might take few seconds to arrive and, in the meantime, ask for something else).
I think I found a solution (at least it appears to be ok for the time being). The global function Get_Data_from_DB accepts a second parameter which is a callback of the invoking controller.
The invoking controller creates a private instance of the Get_Data_from_DB function and triggers a request providing the callback function.
I'll need to test this with parallel queries, but that is still a long way to go...
I have an issue on my application when I get an API request from members belongs to a particulary group.
GET /api/organizations/1234/members?group=4321
If I start my navigation with this request, I have the right members but if I navigate in other page with other groupe before, the $http response is full of parasite members whereas the response form API is right (check from network tab in Chrome developper tools).
I think about some cache but I can not find it ! For info, I use jsData for mounting my data but it's not seems to be the problem.
Here the code of my function to send Api Request :
var loadGroupMembers = function() {
return $q(function(resolve, reject) {
var callParams = {
organizationId: $stateParams.OrganizationId,
groupId: $stateParams.groupId
};
sendApiCall('groupMembers', 'get', callParams)
.success(function(data) {
resolve(data);
})
.error(function(data) {
});
});
};
var sendApiCall = function(requestId, method, params, data, queryStringParams) {
params = params || {};
data = data || {};
var apiCallConfig = {
params: config.params,
method: config.method,
url: "/api/organizations/1234/members?group=4321",
data: data,
cache : false
};
$rootScope.fn.setHistory($state.current.name, 'apiCall', 'sendManualApiCall:' + requestId);
return $http(apiCallConfig);
};
Please tell me if you have questions or need more details.
Thanks for your help ! :)
Edit : I add the function that call sendApiCall and I made a little apiary to show you how the data from api are : http://private-52326-groupmember.apiary-mock.com/organization/1234/members?group=4321
It was kind of link with Jsdata and an interceptor created by an other developer. For each api request, the interceptor add some data on the response with the same type ... So the issue is closed.
Thanks for your help anyway :)
I am just trying FB JS api and want to know whether or how I can still use "response" out of FB.api. For example:
var picture;
FB.api('/me/picture?width=180&height=180', function (response) {
picture = response.data.url;
console.log(picture);
});
alert(picture);
The above code will show "undefined" in alert window.
Is there a way to use "response.data.url" out of FB.api?
Thanks
Update:
Here is the big picture: I need retrieve some information from FB user account, such as /me/name, /me/address/city, /me/picture.data.url and group them together and then send the information to server through AJAX.
var name;
var city;
var picture;
FB.api('/me', function (response) {
name = response.name;
FB.api('/me/address', function (adrresponse) {
city = adrresponse.city;
}
FB.api('/me/picture', function (imgresponse) {
picture = imgresponse.data.url;
}
//since FB API is async, the following is not correct!!!
var params = "name="+name+"&city="+city+"&picture="+picture;
//send out through AJAX.
var http = new XMLHttpRequest();
http.open("POST", url, true);
}
Is there a better way to finish the above job?
Update 2:
The best way is to use fields expansion
https://developers.facebook.com/docs/graph-api/using-graph-api/v2.3#fieldexpansion, as shown by the answer of this question.
Thanks
Derek
The problem is the picture variable is not populated at the time that the alert fires. It will only be populated after the FB.api callback completes.
var picture;
FB.api('/me/picture?width=180&height=180', function (response) {
picture = response.data.url;
// this would work correctly
alert(picture);
});
What are you attempting to do with the picture variable? Perhaps you should call a function do something with the picture inside your callback:
var picture;
FB.api('/me/picture?width=180&height=180', function (response) {
picture = response.data.url;
doSomethingWithPicture(picture);
});
Update
The simple way to achieve what you are after is this:
FB.api('/me', function (response) {
var name = response.name;
FB.api('/me/address', function (adrresponse) {
var city = adrresponse.city;
FB.api('/me/picture', function (imgresponse) {
var picture = imgresponse.data.url;
doAjax(name, city, picture);
}
}
}
function doAjax(name, city, picture) {
//since FB API is async, the following is not correct!!!
var params = "name="+name+"&city="+city+"&picture="+picture;
//send out through AJAX.
var http = new XMLHttpRequest();
http.open("POST", url, true);
}
However, this is not ideal as you have to wait for /me/address before you can call /me/picture.
Here are some other options
you need to call /me first.
you fire off both api calls and execute code when the both complete
Ways to accomplish #2
You could then use a promise library to chain the /me/address and /me/picture/. See: https://github.com/kriskowal/q or https://api.jquery.com/category/deferred-object/ to get started
Call a callback after each that conditionally fires the ajax if both address and picture are set
I am sure there are a number of other ways:
How to chain ajax requests?
How to chain ajax calls using jquery
Update #2
This is the best way to accomplish what you are after (no additional callbacks required)
FB.api('/me', {fields: ['first_name', 'last_name', 'picture', 'address']}, function(response) {
// response will now have everything you need
console.log(response);
});
I did not give this answer originally as it was not the topic of the question which seemed to be scoping.
I am trying to use the Forecast.io weather API to build a weather application with Ionic. I am having a hell of a time getting the AJAX response data delivered to my controller for use in my view.
My Factory Service:
.factory('WeatherService', function($cordovaGeolocation) {
var posOptions = { timeout: 10000, enableHighAccuracy: false };
return {
// Get current Geolocation position with the configured /posOptions/
getPosition : $cordovaGeolocation.getCurrentPosition(posOptions),
// Query the result of /getPosition/ for /lat/, /long/, and /accuracy/
getCoords : function(pos) {
var loc = {
lat : pos.coords.latitude,
long : pos.coords.longitude,
accuracy : pos.coords.accuracy
};
return loc;
},
// Build the API request URI
getApi : function(lat, long) {
var url = 'https://api.forecast.io/forecast/';
var apiKey = 'foo';
var forecastApi = url + apiKey + '/' + lat + ',' + long + '?callback=?';
return forecastApi;
},
// Execute a request against the API URI to recieve forecast data
getForecast : function(api) {
var forecast;
$.ajax({
url : api,
dataType : 'json',
async : false,
success : function(res) {
forecast = res;
}
});
return forecast;
}
};
})
My Controller Method:
.controller('DashCtrl', function($scope, WeatherService) {
WeatherService.getPosition.then(function(pos) {
var pos = pos;
return pos;
}).then(function(pos) {
var coords = WeatherService.getCoords(pos);
return coords;
}).then(function(coords) {
var api = WeatherService.getApi(coords.lat, coords.long);
return api;
}).then(function(api) {
$scope.forecast = WeatherService.getForecast(api);
console.log($scope.forecast);
});
})
There's probably a lot of things inherently wrong with the above code. From my reading I have been made aware that then() methods really shouldn't be used in the controller method, and all of that logic should be isolated to the Service Method. I will be refactoring to that pattern when I get this working.
I am using the jQuery $.ajax() instead of $http because of CORS issues with Forecast.io when developing locally. $jsonp was throwing syntax errors on the response, so I had to resort to jQuery for the call to get this working locally.
I know I am getting a successful response because if I console.log(forecast) inside the $.ajax call I can explore the weather data. For whatever reason, I am unable to save the response value to the forecast var saved in the parent scope of the ajax call and then return that to the controller for use in my view with the $scope.forecast variable. It is always returning undefined.
I have looked at plenty of SO questions while trying to get this working on my own, and have yet to have any success..
How do I return the response from an asynchronous call?
Get Data from a callback and save it to a variable in AngularJS
Well, if you really really feel the need to use ajax (probably better to track down and fix the jsonp issue) then you should probably wrap the forcast in your very own promise.
.factory('WeatherService', function($q,$cordovaGeolocation) {
...
getForecast : function(api)
{
var deferred = $q.defer();
$.ajax({url : api, dataType : 'json', async : false,
success : function(res) {
defereed.resolve(res);
}
});
return defereed.promise;
}
You already know how to handle promises in your controller code so I won't post those changes.
I can't see what the problem with this is.
I'm trying to fetch data on a different server, the url within the collection is correct but returns a 404 error. When trying to fetch the data the error function is triggered and no data is returned. The php script that returns the data works and gives me the output as expected. Can anyone see what's wrong with my code?
Thanks in advance :)
// function within view to fetch data
fetchData: function()
{
console.log('fetchData')
// Assign scope.
var $this = this;
// Set the colletion.
this.collection = new BookmarkCollection();
console.log(this.collection)
// Call server to get data.
this.collection.fetch(
{
cache: false,
success: function(collection, response)
{
console.log(collection)
// If there are no errors.
if (!collection.errors)
{
// Set JSON of collection to global variable.
app.userBookmarks = collection.toJSON();
// $this.loaded=true;
// Call function to render view.
$this.render();
}
// END if.
},
error: function(collection, response)
{
console.log('fetchData error')
console.log(collection)
console.log(response)
}
});
},
// end of function
Model and collection:
BookmarkModel = Backbone.Model.extend(
{
idAttribute: 'lineNavRef'
});
BookmarkCollection = Backbone.Collection.extend(
{
model: BookmarkModel,
//urlRoot: 'data/getBookmarks.php',
urlRoot: 'http://' + app.Domain + ':' + app.serverPort + '/data/getBookmarks.php?fromCrm=true',
url: function()
{
console.log(this.urlRoot)
return this.urlRoot;
},
parse: function (data, xhr)
{
console.log(data)
// Default error status.
this.errors = false;
if (data.responseCode < 1 || data.errorCode < 1)
{
this.errors = true;
}
return data;
}
});
You can make the requests using JSONP (read about here: http://en.wikipedia.org/wiki/JSONP).
To achive it using Backbone, simply do this:
var collection = new MyCollection();
collection.fetch({ dataType: 'jsonp' });
You backend must ready to do this. The server will receive a callback name generated by jQuery, passed on the query string. So the server must respond:
name_of_callback_fuction_generated({ YOUR DATA HERE });
Hope I've helped.
This is a cross domain request - no can do. Will need to use a local script and use curl to access the one on the other domain.