Using angularjs JSONP when callback cant be defined - javascript

I'm attempting to use Angularjs to gather data from the USGS Earthquake feed. Typically you would need to tack ?callback=JSON_CALLBACK on to the end of the URL for Angular to use it, however the USGS feed does not recognize this option.
The URL I'm using is http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojsonp and adding ?callback=JSON_CALLBACK (eg. http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojsonp?callback=JSON_CALLBACK) returns a dataset wrapped in a function called eqfeed_callback.
Is there any way to use this data? I've got an eqfeed_callback function but it's not in scope which makes using Angular pointless.
Here's the code that I've got as it stands:
function QuakeCtrl($scope, $http) {
$scope.get_quakes = function() {
var url = 'http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojsonp';
$http.jsonp(url)
}
}
function eqfeed_callback(data) {
return data;
}
Is there any way to either get the data back into the scope, or get angular to use the eqfeed_callback function internally?

Another option would be defining the eqfeed_callback within the scope like this:
function QuakeCtrl($scope, $http) {
$scope.data = null;
$scope.get_quakes = function() {
var url = 'http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojsonp';
$http.jsonp(url)
}
window.eqfeed_callback = function(data) {
$scope.data = data
}
}

The only idea that comes to mind is (blech) to use a global, and then to manually trigger an Angular update, e.g.:
var callback$scope;
function QuakeCtrl($scope, $http) {
$scope.get_quakes = function() {
var url = 'http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojsonp';
callback$scope = $scope;
$http.jsonp(url)
}
}
function eqfeed_callback(data) {
if (callback$scope) {
// 1. Use callback$scope here
// 2. Set callback$scope to undefined or null
// 3. Trigger an Angular update (since it won't be automatic)
}
}
Not pretty, but...

Expanding on #MichaelVanRijn's answer:
In order to keep the "global peace", define the global function when you need it and nullify it right after.
.controller('QuakeCtrl', function($window, $scope, $http) {
$scope.get_quakes = function() {
$window.eqfeed_callback = function(data){
console.log("quake data", data)
};
// load your jsonp data
$http.jsonp('http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojsonp')
.then(function(success) {
console.log(success);
$window.eqfeed_callback = null;
}, function(fail) {
console.log(fail);
$window.eqfeed_callback = null;
})
}
})

Related

Using http requests, promises, ng-options, and factories or services together

I'm trying to retrieve a list of options from our database and I'm trying to use angular to do it. I've never used services before but I know that's going to be the best way to accomplish what I want if I'm going to use data from my object in other controllers on the page.
I followed a couple tutorials and put together a factory that makes an http request and returns the data. I've tried several ways of doing it, but for some reason nothing is happening. It's like it never runs the factory function and I can't figure out why.
Factory:
resortModule= angular.module('resortApp',[]);
resortModule.factory('locaService',['$http', function ($http){
var locaService= {};
locaService.locations = {};
var resorts = {};
locaService.getLocations=
function() {
$http.get('/url/url/dest/').success(function (data) {
locaService.locations = data;
});
return locaService.locations;
};
return locaService;
//This is a function I would like to run in addition to the first one so multiple variables would be stored and accessible
/*getResorts:
function(destination) {
$http.get('/url/url/dest/' + destination.id).success(function (data) {
resorts = data;
});
return resorts;
}*/
}]);
resortModule.controller('queryController',['$scope', 'locaService', function($scope, locaService) {
$scope.checkConditional= function (){
if($("#location").val() == ""){
$("#location").css('border','2px solid #EC7C22');
}
};
$scope.selectCheck= function (){
$("#location").css('border','2px solid #ffffff');
$(".conditional-check").hide();
};
$scope.resort;
$scope.locations= locaService.getLocations();
}]);
I just want the data to be returned and then assigned to the $scope.locations to be used for ng-options in the view. Then I want my other function to run on click for the next field to be populated by the variable resort. How would I do this? Any help would be great! Thanks!
$http service returns a promise, and your function should return that promise. Basically your getLocations function should be something like the following
locaService.getLocations=
function() {
return $http.get('/url/url/dest/');
};
Then in your controller you should retrieve the options using this promise:
locaService.getLocations()
.then(
function(locations) // $http returned a successful result
{$scope.locations = locations;}
,function(err){console.log(err)} // incase $http created an error, log the returned error);
Using jquery in controllers or manipulating dom elements in controllers is not a good practice, you can apply styles and css classes directly in views using ng-style or ng-class.
Here is an example how all it should look wired up:
resortModule= angular.module('resortApp',[]);
resortModule.factory('locaService',['$http', function ($http){
var locaService= {
locations: {}
};
var resorts = {};
locaService.getLocations= function() {
return $http.get('/url/url/dest/');
};
return locaService;
//This is a function I would like to run in addition to the first one so multiple variables would be stored and accessible
/*getResorts:
function(destination) {
$http.get('/url/url/dest/' + destination.id).success(function (data) {
resorts = data;
});
return resorts;
}*/
}]);
resortModule.controller('queryController',['$scope', 'locaService', function($scope, locaService) {
/* Apply these styles in html using ng-style
$scope.checkConditional= function (){
if($("#location").val() == ""){
$("#location").css('border','2px solid #EC7C22');
}
};
$scope.selectCheck= function (){
$("#location").css('border','2px solid #ffffff');
$(".conditional-check").hide();
};
*/
$scope.resort;
locaService.getLocations()
.then(
function(locations) // $http returned a successful result
{$scope.locations = locations;}
,function(err){console.log(err)} // incase $http created an error, log the returned error);
}]);

Best way to store and use data in Angular that updates periodically

I'm new to Angular and I'm trying to create an app that pulls data from a dynamically generated JSON file every 5 minutes and updates the view with the new data. The JSON file contains all the data for the website, which is data for a slider and for a list of events. I've read that global data can be stored in the $rootScope, or retrieved and served using a .service or .factory. I've tried different ways with no success and I'm lost. What is the best way to periodically pull data from an api for the entire app, store it, and use it?
Currently I have this code:
app.factory("Poller", function Poller($http, $timeout){
var data = {'slider' : [], 'activities' : []};
var getData = function(){
$http.get('http://example.com/json.php')
.then(function(r){
data = r.data;
});
$timeout(getData, 1000*60*5);
}
getData();
return{
data: data
}
});
app.controller('ListController', function(Poller){
this.activities = Poller.data.activities;
});
I think you're looking for something like this:
See plnkr
app.factory("Poller", function Poller($http, $interval){
var data = {
'newData': {
'one' : '',
'key' : ''
},
'fetchCount': 0
};
var getData = function() {
data.fetchCount += 1;
$http.get('http://echo.jsontest.com/key/value/one/two')
.then(function(response){
data.newData = response.data;
});
};
var loopGetData = function() {
$interval(getData, 5000);
};
loopGetData();
return{
data: data,
};
});
app.controller('ListController', function($scope, Poller){
$scope.activities = {'one' : '', 'key' : ''};
$scope.Initialize = function () {
$scope.$watch(function () { return Poller.data.newData; },
function(newValue, oldValue) {
if (newValue !== oldValue) {
$scope.activities = newValue;
}
});
};
$scope.Initialize();
});
app.controller('CountController', function($scope, Poller){
$scope.fetchCount = 0;
$scope.Initialize = function () {
$scope.$watch(function () { return Poller.data.fetchCount; },
function(newValue, oldValue) {
if (newValue !== oldValue) {
$scope.fetchCount = newValue;
}
});
};
$scope.Initialize();
});
So the problem here looks like you have defined the function getData() inside the Poller factory that makes the ajax request, but didn't bind that to a key inside the factory's returning object.
Try adding this,
return{
data: data,
getData: getData
}
And then remove the getData() you have right before the returning block.
Now I feel like depending on your app's setup, if you want to achieve long polling effects, you should attach getData() to a variable in some top-level controller that is always present so that you can invoke the getData() periodically using $timeout.
Speaking of $timeout is that you should extract the $timeout outside of the getData() and so that the function itself simply makes the ajax request. More modular, single responsibility design that way, which makes that factory function more usable in other areas of your app if you ever need.
If you want to update the information after you finish the ajax requests every five minutes, you should looking into $broadcast and $rootScope, you can basically tell your entire Angular app that some rootScope variable or service has changed, and they will update accordingly. You need some kind of callback set up after the ajax request inside whichever controller you are invoking the getData() function.

Load ajax data into $scope before controller renders the view

Being new to Angular, I am not able to figure out how to load all the data required in the controller before it starts to compile the view.
I have created a factory to load JSON from server.
app.factory('myData', function ($http) {
return {
getMetaData : function () {
return $http.get('get-metadata').then(function (result) {
return result.data;
});
}
}
});
and a controller which uses that factory
app.controller('MyController', function ($scope, $http, myData) {
$scope.meta_data = {};
myData.getMetaData().then(function (data) {
$scope.meta_data = data.metadata;
});
});
I am also using a $watch in my controller like below
$scope.$watch("my_var.x", function (x, old_x) {
if (x) {
var y = $scope.meta_data.mapping[x] || [];
$scope.meta_data.y = y;
}
});
My problem is, $watch gets called before the myData.getMetaData returns, and $scope.meta_data.mapping isn't available. Due to that an error is thrown.
Any hint in the right direction would suffice.
Also, am I doing it correctly? I mean is this the case where I should be loading all data outside the controller and bootstrap my app manually using angular.bootstrap(document.getElementById('myApp'), ['myApp']);?
If you need to wait until your data is fetched before you start your $watch, just declare it in the resolved promise callback function:
app.controller('MyController', function($scope, $http, myData) {
$scope.meta_data = {};
myData.getMetaData().then(function(data) {
$scope.meta_data = data.metadata;
$scope.$watch("my_var.x", function(x, old_x) {
if (x) {
var y = $scope.meta_data.mapping[x] || [];
$scope.meta_data.y = y;
}
});
});
});
Otherwise, it might be a good practice as #apairet said to read about using resolves with routing.

Service to call and update angular controller $scope from outside of controller?

controller function:
$scope.init = function () {
Words.get('init', $scope.randNum)
.error(function (data) {
console.log('init error: ' + data.message);
})
.success(function (data) {
$scope.board = data.settings.board;
$scope.tray = data.tray;
$scope.scores = data.pointsPerLetter;
$scope.totalScore = data.score.total;
console.log('init: ' + $scope.tray);
})
}
and my service:
angular.module('wordService', [])
.factory('Words', function ($http) {
var id;
return {
get: function (call, num) {
id = num;
return $http.get('http://xxxxx');
},
send: function (call, data) {
console.log('send: ' + data)
return $http.post('http://xxxxx' + call, data);
}
}
});
Now instead of ngAccess = angular.element(document.getElementById('ws')).scope();
to call ngAccess.init() or $scope.init
How would I add this call to a service and thus call it when needed while still updating the scope within the controller? The reason the above will not work is that I am using browserify and I dont yet have access to the scope.
Scenario: I need to be able to click a button and call a function that updates the scope.
Caveat: the button is created and added to a canvas. (shouldnt matter as I still have the click calls etc).
As always thanks in advance!
Move the data object into the service and assign a reference to a controller scope variable...
Your factory might look like:
.factory('Words', function ($http) {
var id;
var results = {};
var get = function (call, num) {
id = num;
return $http.get('http://xxxxx').then(function(response){
results.board = response.data.settings.board;
results.tray = response.data.tray;
results.scores = response.data.pointsPerLetter;
results.totalScore = response.data.score.total;
};
};
var send = function (call, data) {
console.log('send: ' + data)
return $http.post('http://xxxxx' + call, data);
};
return {
get: get,
send: send,
results: results
}
});
While your controller would then look like:
.controller(function($scope, Words){
$scope.words = Words.results;
$scope.init = function () {
Words.get('init', $scope.randNum).then(function(){
console.log($scope.words); // should log the data you want
}, function(response){ console.log(response)});
};
// still calling from controller but you could from any component and still
// have the local scope variable update based on its assignment to the
// service object
$scope.init();
})
Note that I did modify your factory a bit more to use the revealing module pattern. This way, you can make internal calls to your get/set functions in addition to calls from other components.
Now you should be able to add a button virtually anywhere else in the app (ie doesn't need prototypical inheritance from your controller's scope). For example, this directive would make a call and update the results, which would reflect in the controller scope variable
.directive('update', function(Words){
return function(scope) {
scope.update = function(){
Words.get('update', 'somevalue')
}
}
})
where it is declared in the view like this:
<button update ng-click="update()">Update</button>

AngularJS Load Data from Service

I am having a problem getting data from a service populated into my view. I have a service defined as such
app.factory('nukeService', function($rootScope, $http) {
var nukeService = {};
nukeService.nuke = {};
//Gets the list of nuclear weapons
nukeService.getNukes = function() {
$http.get('nukes/nukes.json')
.success(function(data) {
nukeService.nukes = data;
});
return nukeService.nukes;
};
return nukeService;
});
and my controller
function NavigationCtrl($scope, $http, nukeService){
/*$http.get('nukes/nukes.json').success(function(data) {
$scope.nukes = data;
});*/
$scope.nukes = nukeService.getNukes();
}
If I use the $http.get from the controller the data populates fine, however, if I try to call the data from the service, I get nothing. I understand that the query is asynchronous but I am having a hard time understanding how to populate the $scope variable once the data is returned. I could use $rootscope to broadcast an event and listen for it in the controller but this does not seem like the correct way to accomplish this. I would really appreciate any advice on how to do this the correct way.
I think this should solve your problem
app.factory('nukeService', function($rootScope, $http) {
var nukeService = {};
nukeService.data = {};
//Gets the list of nuclear weapons
nukeService.getNukes = function() {
$http.get('nukes/nukes.json')
.success(function(data) {
nukeService.data.nukes = data;
});
return nukeService.data;
};
return nukeService;
});
function NavigationCtrl($scope, $http, nukeService){
$scope.data = nukeService.getNukes();
//then refer to nukes list as `data.nukes`
}
This is a problem with object reference.
when you calls nukeService.getNukes() you are getting a reference to a object a then your variable $scope.nukes refers that memory location.
After the remote server call when you set nukeService.nukes = data; you are not changing the object a instead you are changing nukeService.nukes from referencing object a to object b. But your $scope.nukes does not know about this reassignment and it still points to object a.
My solution in this case is to pass a object a with property data and then only change the data property instead of changing reference to a
This should be as follows. As mentioned by NickWiggill's comment, undefined will be assigned to nukeService.data if we do not return promise.
app.factory('nukeService', function($rootScope, $http) {
var nukeService = {};
//Gets the list of nuclear weapons
nukeService.getNukes = function() {
return $http.get('nukes/nukes.json');
};
return nukeService;
});
function NavigationCtrl($scope, $http, nukeService){
nukeService.getNukes().then(function(response){
$scope.data = response.data;
});
}
What I do is that I expose the data straight from the service, and have a method which initializes this data. What is wrong with this?
Service:
app.factory('nukeService', function($scope, $http) {
var data = {};
data.nukes = [];
//Gets the list of nuclear weapons
var getNukes = function() {
$http.get('nukes/nukes.json').success(function(data) {
data.nukes = data;
});
};
// Fill the list with actual nukes, async why not.
getNukes();
return {
data : data
// expose more functions or data if you want
};
});
Controller:
function NavigationCtrl($scope, nukeService){
$scope.data = nukeService.data;
//then refer to nukes list as `$scope.data.nukes`
}

Categories