How to handle callback for JSONP inside angularjs - angularjs

I have a working JSONP call inside a angularjs module to get working I defined the callback outside of the module (see below). I was unable to get the callback working inside the module ( callback not defined ).
How would I get the jsonp_callback(data) inside the angular module ?
function jsonp_callback(data) {
// returning from async callbacks is (generally) meaningless
alert('good');
console.log(data.found);
}
angular.module('me.services', [])
.factory('Products', ['$http', '$q', '$filter', function ($http, $q, $filter) { // Query API and return JSON.
return {
get: function () {
var _deferred = $q.defer(),
funds = [],
products = [];
var murl = "http://localhost:82/api/productlist/getitems";
$http.jsonp(murl + "?callback=jsonp_callback", null, function (result) {
alert('ts');
});
}

Depending on how you want to handle the data inside angular:
function jsonp_callback(data) {
// for binding to a service property
angular.element(document).injector().get('ServiceName').whatever = data
// for binding to a controller scope propery
angular.element('#someDiv').scope().whatever = data
...

Related

How to pass data between controller-Service-controller in angularjs

I am trying to bind id from controller1 to one of myservice function.But I get below error
TypeError: _this.dashboardService.chartid is not a function
Please find my code snippet
Service1.js
var Service1 = function (
$http) {
this.$http = $http;
};
Service1.prototype.chartid = function (id) {
return id;
};
appmodule.service('dashboardService', DashboardService);
controller1.js
Controller1.prototype.Chart = function (data) {
_this.service1.chartid(data.key);
};
You haven't inject your service inside your controller, in more classic way:
var controller1=angular.module('controllermodule',[]);
controller1.controller('{name of the controller}',function({service to inject}){}
Javascript prototype functions can be invoked on its objects.When you add prototype you are defining the class whose functions are available on the object.
e.g.
var Service = function() {};
Service.prototype.callMePlease = function() {
console.log('ok Calling now');
return 'Just called';
};
console.log('callMePlease is undefined on Service or is it ? Service.callMePlease='+Service.callMePlease);
var serviceInstance = new Service();
console.log('callMePlease is NOT undefined on serviceInstance or is it ? serviceInstance .callMePlease='+serviceInstance.callMePlease);
var didYouCall = serviceInstance.callMePlease();
console.log('didYouCall='+didYouCall);
Given this you actually need the instance of service1 in your case.
It is best you follow Angular injection model as per official angularjs DI documentation to allow AngularJS create instances for you.
However you may use programattic approach (not advised). In your case
Controller1.prototype.Chart = function (data) {
var myService1 = $injector.get('service1');
myService1.chartid(data.key);
};

Make data from factory http.get accessible to entire controller AngularJS

I am trying to give access to a json file that contains config information for my project (things like rev number, project name, primary contact, etc) I created a factory that retrieves the json file using http.get, I can then pull that data into my controller but I am unable to access it from anywhere in the controller.
I did not write the factory, I found it as an answer to another person's question and it is copied almost entirely so if it not the right way to accomplish what I am trying to do please correct me.
here is the factory:
app.factory('configFactory', ["$http", function($http) {
var configFactory = {
async: function() {
// $http returns a promise, which has a then function, which also returns a promise
var promise = $http.get('assets/json/config.json').then(function(response) {
// The then function here is an opportunity to modify the response
console.log(response.data.config);
// The return value gets picked up by the then in the controller.
return response.data.config;
});
// Return the promise to the controller
return promise;
}
};
return configFactory;
}]);
and here is my controller:
app.controller('footerController', ['$scope', '$rootScope', 'configFactory', function footerController($scope, $rootScope, configFactory) {
var body = angular.element(window.document.body);
$scope.onChange = function(state) {
body.toggleClass('light');
};
configFactory.async().then(function(d) {
$scope.data = d;
// this console log prints out the data that I am trying to access
console.log($scope.data);
});
// this one prints out undefined
console.log($scope.data);
}]);
So essentially I have access to the data within the function used to retrieve it but not outside of that. I can solve this with rootScope but I am trying to avoid that because I think its a bandaid and not a proper solution.
Any help would be great but this is my first experience with http.get and promises and all that stuff so a detailed explanation would be very much appreciated.
[EDIT 1] The variables from the config file will need to be manipulated within the web app, so I can't use constants.
Don't assign your response data to scope variable , create a property in your factory itself and assign the response to this property in your controller when your promise gets resolved.This way you will get the value in all the other controllers.
I have updated your factory and controller like below
app.factory('configFactory', ["$http", function($http) {
var configFactory = {
async: function() {
// $http returns a promise, which has a then function, which also returns a promise
var promise = $http.get('assets/json/config.json').then(function(response) {
// The then function here is an opportunity to modify the response
console.log(response.data.config);
// The return value gets picked up by the then in the controller.
return response.data.config;
});
// Return the promise to the controller
return promise;
},
config:'' // new proprety added
};
return configFactory;
}]);
app.controller('footerController', ['$scope', '$rootScope', 'configFactory', function footerController($scope, $rootScope, configFactory) {
var body = angular.element(window.document.body);
$scope.onChange = function(state) {
body.toggleClass('light');
};
configFactory.async().then(function(d) {
// $scope.data = d;
configFactory.config=d;
// this console log prints out the data that I am trying to access
console.log($scope.data);
});
// this one prints out undefined
console.log($scope.data);
}]);
Have you looked into using angular constants? http://ilikekillnerds.com/2014/11/constants-values-global-variables-in-angularjs-the-right-way/ You can leverage them as global variables accessible from any controller without the ramifications of assigning the values to rootScope

Can I access method defined in controller like $rootscope.method() inside factory?

How to access the controller method inside factory.
//controller.js
$rootScope.stackTraceReport = function(error){
var exception = errorHandler(error);
//reproting to server
StackTrace.report(exception,serviceUrl);
}
// factory.js
.factory('viewPdfFailure',['scrollTop', '$timeout', function(scrollTop, $timeout) {
return {
viewError: function ($scope, $rootScope, data, status, headers, config, accessForbiddenMessage, genericErrorMessage) {
$timeout(function () {
$scope.errorMessages = [];
$scope.infoMessages = [];
}, 7000);
console.error('Error in viewDocument, status: ' + status);
if (status == 403) {
$scope.errorMessages.push(accessForbiddenMessage);
scrollTop.onErrorScrollTop('id-header');
} else {
$scope.errorMessages.push(genericErrorMessage);
scrollTop.onErrorScrollTop('id-header');
}
$rootScope.stackTraceReport(data);
}
Help me figure out how can I access this controller method inside the factory. I am getting the method not defined error. Do I need to inject the controller inside the factory?
I think you are facing this error because when you call the $rootScope.stackTraceReport(data); method you dont defined it (calling the $rootScope.stackTraceReport = function(error){...}).
Make sure the function definition is called first (you can put two console.log to see witch is called first.
But I think its better you create a singleton Util factory to make all this "globals" functions is the same file and make sure the function is defined when you call it. (http://blog.jdriven.com/2013/03/how-to-create-singleton-angularjs-services-in-4-different-ways/)
The method should be defined in the run method so as to consume it elsewhere:
Code:
.run(['$rootScope', 'StackTrace', function ($rootScope, StackTrace) {
$rootScope.stackTraceReport = function (error) {
var exception = errorHandler(error);
//reproting to server
StackTrace.report(exception, serviceUrl);
}
}]);

ngNewRouter Angularjs 1.4.4 is not able to call component controller methods after callback

Today I was working to migrate my existing Angular js ngRoute base code to ngNewRouter and got stuck in component controller. As per new apporach component controller doesn't have $scope, instead of it uses "this". It works fine for simple usecase. In my usecase for a particular component I make REST API call to get the data. All works but when callback happens then it is not identifying "this" defined object. Following is code snippet for more details.
Component Controller to make call to get data:
var ClubonboardingController = function ($location, ApplicationLoaderService, ApplicationConstants, $window, ResourceImplementation, $rootScope) {
//Launch Application
this.launchApplication = function () {
this.cacheLoaded = true;
};
//Method to initialize
this.init = function () {
//show loading screen
this.showLoadingScreen = true;
//Initilize Application cache
ApplicationLoaderService.initilizeApplicationCache($rootScope, "3252345", true, "en_US", function () {
this.launchApplication(); //This throws function not found exception
});
};
//Call init() to initialze the loading.
this.init();
};
app.controller("ClubonboardingController", ClubonboardingController);
ClubonboardingController.$inject = ['$location', 'ApplicationLoaderService', 'ApplicationConstants', '$window', 'ResourceImplementation', '$rootScope'];
Issue: After callback code thinks "this" as global javascript object, instead of "this" for this controller and not able to find anything defines in this controller and it throw exception at this.launchApplication(); .
Please help.
I think the problem is that what 'this' means changes depending on context. There seems to be a general recommendation to assign 'this' to a local variable and use the variable. Thanks to JavaScript closures, the value of 'this' is then retained.
Using a local variable called vm seems to be common, and replace all 'this.' with 'vm.' Your code would end up changing to
var ClubonboardingController = function ($location, ApplicationLoaderService, ApplicationConstants,
$window, ResourceImplementation, $rootScope) {
var vm = this;
//Launch Application
vm.launchApplication = function () {
vm.cacheLoaded = true;
};
//Method to initialize
vm.init = function () {
...
Hope this helps.

How would I create a factory that only queries the database once, but can be used by many controllers?

I have a 'messages' factory that will query my database for a list of messages.
I'm using the list of messages in two different places. Once to add a message count indicator, and then once to show a list of messages. Since I'm injecting the service into two different controllers, it seems like it's creating two instances of my factory, and hitting the database twice for the list.
How would I set things up to only ask for the list once, and use the list for both display and count purposes in both controllers?
My factory looks like this:
myApp.factory('messagesService', [
'$rootScope',
function($rootScope) {
var messages = [];
function query() {
// Would actually hit the database asyncronously
messages = ['one', 'two', 'three', 'four'];
console.log('query');
$rootScope.$emit('messages.update');
}
function all() {
return messages;
}
return {
query: query,
all: all
}
}
]);
My controllers are using blocks like this to watch for changes:
$rootScope.$on('messages.update', function() {
$scope.messagesCount = messagesService.all().length;
});
But it means i need a messagesService.query(); in each controller for things to be reliable.
So here are a few jsFiddle examples of it as I have things now:
Doesn't work (only updates the header): http://jsfiddle.net/TSLfc/1/
Works but would break if I didn't load the dashboard controller:
http://jsfiddle.net/TSLfc/2/
Works every time, but queries the server twice:
http://jsfiddle.net/TSLfc/3/
Is there a better way to organize my code? Should I build out the messages factory into it's own full module?
Here (Plunkr) is how I would do it:
I have gone back and modified my previous answer, updating with what we discussed in the comments below as well as using promises instead of the timeout as an asynchronous simulation I was showing before (see revision history for reference).
I also removed every variable/function that didn't need to be returned to the controller from the service object, if it doesn't need to be accessed via the controller than it doesn't need to be included on the returned object.
var myApp = angular.module('myApp', []);
myApp.factory('messagesService', [
'$q',
'$rootScope',
'$http',
function ($q, $rootScope, $http) {
var mService = {};
mService.messages = [];
var queryInit = false;
// We don't need to access this function in the controller
// So I am not going to attach to the returned object
var getMessages = function () {
// Stops each controller from getting messages when loaded
if (!queryInit) {
queryInit = true;
// Using the $q promise library we use 'then()' to handle
// What happens after the async call is returned
// The first function parameter is the success/resolve callback
// The second function parameter is the error/reject callback
mService.query().then(function (successResults) {
// Tell all of the controllers that the data has changed
$rootScope.$broadcast('messages.update');
}, function (errorResults) {
console.error(errorResults);
});
}
};
// Used to force an update from the controller if needed.
mService.query = function () {
var deferred = $q.defer();
$http.get('path/to/file.php')
.success(function (data, status, headers, config) {
// assign the returned values appropriately
mService.messages = data;
// this callback will be called asynchronously
// when the response is available
deferred.resolve(data);
})
.error(function (data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
deferred.reject(data);
});
return deferred.promise;
};
mService.getCount = function () {
return mService.messages.length;
};
mService.all = function () {
return mService.messages;
};
// Initialize the messages
// so we don't need to get the messages in each controller
getMessages();
return mService;
}]);
In your html, on your first controller setup an init function (ng-init="init()") that instantiates the factory:
<div ng-app="myApp">
<div ng-controller="HeaderCtrl" class="header" ng-init="init()">
Messages Count: {{ messageCount }}
</div>
<div ng-controller="DashboardCtrl" class="dashboard">
<ul ng-repeat="message in messages">
<li>{{ message }}</li>
</ul>
<button ng-click="getMessages()">Check for new messages.</button>
</div>
</div>
And in your controllers you just have the $rootScope.$on('messages.update' fn) and you can call manually by calling the services query() function which returns the promise:
myApp.controller('HeaderCtrl', [
'$scope',
'$rootScope',
'messagesService',
function ($scope, $rootScope, messagesService) {
$rootScope.$on('messages.update', function () {
$scope.messageCount = messagesService.getCount();
});
// Manual call, if needed
$scope.getMessageCount = function () {
messagesService.query().then(function (successCallback) {
$scope.messageCount = messagesService.getCount();
});
};
}]);
myApp.controller('DashboardCtrl', [
'$scope',
'$rootScope',
'messagesService',
function ($scope, $rootScope, messagesService) {
$rootScope.$on('messages.update', function () {
$scope.messages = messagesService.all();
});
// Manual call, if needed
$scope.getMessages = function () {
messagesService.query().then(function (successCallback) {
$scope.messages = messagesService.all();
$rootScope.$broadcast('messages.update');
});
}
}]);
You can set cache:true on a $http request. There are numerous ways to data bind within angular without needing to use the $broadcast approach you are using. Also note, $broadcast from a scope will be receievd by all descendent scopes, so no need to inject $rootSCope just for that purpose, can listen on $scope.
Here's one approach that controllers use promise of $http to retrieve data. I used a button click to retrive data for DashControl so can see that request does get cached
myApp.factory('messagesService',function($http) {
return{
query:function query(callback) {
/* return promise of the request*/
return $http.get('messages.json',{ cache:true}).then(function(res){
/* resolve what data to return, can set additional properties of the service here if desired*/
return res.data
}).then(callback);
}
}
});
myApp.controller('HeaderCtrl',function($scope, messagesService) {
messagesService.query(function(messages){
$scope.messagesCount = messages.length;
});
});
myApp.controller('DashboardCtrl', function($scope, messagesService) {
/* use button click to load same data, note in console no http request made*/
$scope.getMessages=function(){
messagesService.query(function(messages){
$scope.messages = messages;
})
}
});
Essentially in this scenario, whatever controller calls the factory service first will generate the data cache
DEMO
I would do it like that:
myApp.factory('messagesService', function() {
var expose = {
messages: []
};
expose.query = function () {
// Would actually hit the database asyncronously
expose.messages = ['one', 'two', 'three', 'four'];
console.log('query');
};
// Initialization
expose.query();
return expose;
}
);
And in your controllers:
$scope.messagesCount = messagesService.messages.length;
Model with broadcasting and pre-hitting database looks heavy for me.
So here is code, that can be embedded in service:
var sv = this;
var deferred = sv.$q.defer();
if (sv._running) {
return sv._running;
}
sv._running = deferred;
It based on reusing promise. To make it query database once - just don't set sv._running to false and it will always return first obtained result.

Resources