Injecting multiple Services to Controller throws error- AngularJs - angularjs

My code is like this
Services.JS
angular.module('RateRequestApp.services', []).factory('rateRequestService', ['$http', rateRequestService]);
function rateRequestService($http) {
var service = { getData: getData};
return service;
function getData() {
return $http({
method: 'Get',
url: '../services/asas.asmx/GetReadOnlyData?shipmentID=' + getQuery('shipmentId')
});
}
function getQuery(name) {
var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}
}
angular.module('RateRequestApp.services', []).factory('rePrintService', ['$http', rePrintService]);
function rePrintService($http) {
var service = { getReprintData: getReprintData };
return service;
function getReprintData() {
return $http({
method: 'Get',
url: '../services/asas.asmx/RePrint?shipmentID=' + getQuery('shipmentId')
});
}
function getQuery(name) {
var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}
}
Controller.JS
angular.module('RateRequestApp.controllers', []).controller
('ReadOnlyController', [
'$scope', 'rateRequestService', 'rePrintService', '$controller',
function($scope, rateRequestService, rePrintService, $controller) {
$scope.rateData = [];
rateRequestService.getData().success(function(response) {
$scope.rateData = response;
console.log(JSON.stringify(response));
}).
error(function(data, status, headers, config) {});
rePrintService.getReprintData().success(function(response) {
}).
error(function(data, status, headers, config) {});
}
]);
App.js
angular.module('RateRequestApp', [
'RateRequestApp.services',
'RateRequestApp.controllers',
'ui.bootstrap'
]);
Everything looks okay to me, But this throws an error
Error: [$injector:unpr] Unknown provider: rateRequestServiceProvider <- rateRequestService
When I Inject only my first service, It works correct. but when second comes it breaks.
Can any one point out what I am doing wrong?

The problem is that you have two modules
angular.module('RateRequestApp.services', [])
definitions, so the second one overwrites the first one which holds rateRequestService service.
To properly retrieve existent module you need to use getter syntax:
angular.module('RateRequestApp.services').factory('rePrintService', ['$http', rePrintService]);
Note that there is no dependency array [] after module name.
Or you can use chain notation like this:
angular.module('RateRequestApp.services', [])
.factory('rateRequestService', ['$http', rateRequestService])
.factory('rePrintService', ['$http', rePrintService]);

First thing that I am noticing is that the code itself needs to be better organized to be honest, but anyways.
Second thing is that you need to add the module that you created into your "RateRequestApp.controllers" module. So that the code entirely would be something like:
angular.module('RateRequestApp.controllers', ['RateRequestApp.services']).controller
This + the previous answer should do the trick.
good luck

Related

Angular - load data at app start and use in controller in different module

I'm pretty new with Angular and I'm stuck on this for a few days now :(
I have a web app (kind of portal with several web tools available).
I want to load some data from DB when the app being initially accessed and use the data in some controller (.i.e. load the data only once).
This is what I have by now:
Main app
var myApp= angular.module('MyApp',['ngRoute','ngTable','mpcApp','registerApp','forgotPasswordApp','tool1App','loginApp','userManagementApp','init']);
myApp.config(['$routeProvider','$locationProvider',function($routeProvider) {
$routeProvider.
when('/...', {
templateUrl: 'js/....html',
controller: 'tool1Ctrl'
})....
I also have myApp.run - but I will describe it later.
I've created different module for my factory:
(function (angular) {
var initApp = angular.module('init',[]);
initApp.factory('EndPoints', ['$http', function($http) {
var EndPointsList="";
return{
getList: function(){
$http.post("/getEndPoints", {
transformRequest : angular.identity,
headers : {'Content-Type' : undefined}
}).
success(function(data, status, headers, config) {
EndPointsList = data;
console.log(EndPointsList);
return EndPointsList;
}).error(function(data, status, headers, config) {
console.log("Failed to load end-points list");
});
return EndPointsList;
}
};
}]);
})(angular);
What I did next is injecting this factory into myApp.run:
myApp.run(['$rootScope', '$location', 'SessionIdService','EndPoints', function($rootScope, $location, SessionIdService,EndPoints) {
$rootScope.EndPoint= EndPoints.getList();
console.log("Current end-point: " + $rootScope.appEnv);
...
This is just not working! I don't see the print in console at all, and when I try to use the $scope.EndPoint in another controller in another module it appears to be empty.
Controller code:
var Tool1Controllers= angular.module('tool1App',[]);
Tool1Controllers.controller('toolCtrl', ['$scope', '$http','$rootScope', function ($scope, $http,$rootScope) {
console.log("Test: Controller end-point: " + $scope.EndPoint);
Please help! :(
The problem seems to be that you are returning a string before $http promise is fulfilled. You need to wait for the http response before returning data, or return the promise and let the consumers implement the outcome handlers.
Try updating your factory as follows:
initApp.factory('EndPoints', ['$http', function($http) {
return{
getList: function(){
return $http.post("/getEndPoints", {
transformRequest : angular.identity,
headers : {'Content-Type' : undefined}
});
}
};
}]);
And your run assignment as:
EndPoints.getList()
.success(function(data, status, headers, config) {
$rootScope.EndPoint= data;
}).error(function(data, status, headers, config) {
console.log("Failed to load end-points list");
});
UPDATE: An alternative to attaching data to the $rootScope is to have the factory cache the data and offer a method to return the data either from cache or from the remote endpoint if it hasn't already been cached:
initApp.factory('EndPoints', ['$http', '$q', function($http, $q) {
var endpoints = null;
return{
getList: function() {
return endpoints ?
// if data is already cached, return it
$q(function(resolve, reject) { resolve(endpoints); }) :
// otherwise fetch it from the service, cache it and return it
$http.post("/getEndPoints", {
transformRequest : angular.identity,
headers : {'Content-Type' : undefined}
}).then(function(data) { endpoints = data; return data; });
}
};
}]);
And now in your controllers, you can just inject the service and define outcome handlers for the getList promise:
.controller ...
EndPoints.getList()
.then(function(data) {
$scope.someVariable = data;
}, function(error) {
console.log("Failed to load end-points list");
});
...
Since factories are singletons, you can inject the Endpoints service into any number of controllers and the same cached data should be returned so that at most 1 call to the remote endpoint is made.

How to structure an Angular service so it can handle asynchronous calls?

In my Angular application, I have two controllers which both need access to the same data.
Toward that end, I've created a service which will be responsible for holding and providing access to that data:
angular.module("SomeModule").factory( "SomeService", function( $http ) {
var svc = {};
var data = {};
// on initialization, load data from the server
$http.get( "somefile.json" )
.success( function( data ) {
svc.data = data;
} );
svc.getItem = function( id ) {
// find the specified item within svc.data, and return its value
};
return svc;
} );
...and I've injected that service into each of the two controllers:
angular.module("SomeModule").controller( "SomeController", function( $routeParams, SomeService ) {
var ctrl = this;
ctrl.item = null; // set an initial value
// load the item that was requested in the URL
ctrl.item = SomeService.getItem( $routeParams.id );
} );
This almost works - but it has one big flaw. If SomeController calls SomeService.getItem() before SomeService finishes loading somefile.json, then SomeService won't have any data to return.
In practice, if I load the app a few times, some loads will work (i.e., SomeService will finish loading somefile.json first, and the controller will present the data as desired), and other loads don't (i.e., SomeController will try to retrieve data from SomeService before the data has actually been loaded, and everything will crash and burn).
Obviously, I need to find some way to defer the execution of getItem() until SomeService is actually ready to process those calls. But I'm not sure of the best way to do that.
I can think of a some rather hairy solutions, such as building my own call queue in SomeService, and wiring up a bunch of complicated callbacks. But there's gotta be a more elegant solution.
I suspect that Angular's $q service could be useful here. However, I'm new to promises, and I'm not sure exactly how I should use $q here (or even whether I'm barking up the right tree).
Can you nudge me in the right direction? I'd be super grateful.
I would recommend making better use of AngularJS' routing capabilities, which allow you to resolve dependencies, along with the $http services cache, and structuring your application accordingly.
I think you need to, therefore, get rid of your service completely.
Starting with the example below, taken straight from the Angular documentation:
phonecatApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/phones', {
templateUrl: 'partials/phone-list.html',
controller: 'PhoneListCtrl'
}).
when('/phones/:phoneId', {
templateUrl: 'partials/phone-detail.html',
controller: 'PhoneDetailCtrl'
}).
otherwise({
redirectTo: '/phones'
});
}]);
So PhoneListCtrl and PhoneDetailCtrl both need the data from somefile.json. I would inject that data into each controller like so:
(function(){
angular.module('phonecatApp').controller('PhoneListCtrl', ['somefileJsonData', function(somefileJsonData){
this.someFileJsonData = someFileJsonData;
}]);
})();
The same idea for PhoneDetailCtrl.
Then update your routing like so:
phonecatApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/phones', {
templateUrl: 'partials/phone-list.html',
controller: 'PhoneListCtrl',
resolve:{ somefileJsonData: ['$http',function($http){
return $http.get("somefile.json", { cache: true });
}] }
}).
when('/phones/:phoneId', {
templateUrl: 'partials/phone-detail.html',
controller: 'PhoneDetailCtrl',
//same resolve
}).
otherwise({
redirectTo: '/phones'
});
}]);
This way, you are letting angular take care of resolving this dependency as part of the routing process.
Setting cache to true will also cache it so you aren't doing the same Get request twice, and Angular will only show your view when the dependency is resolved.
So, in your app, where SomeController is paired with a view as part of the routing process, use resolve to resolve item, and inject this into the controller.
try this code
angular.module("SomeModule").factory("SomeService", function ($http) {
var svc = {};
svc.getList = function () {
return $http.get("somefile.json");
};
svc.getItem = function (id) {
svc.getList().then(function (response) {
// find the specified item within response, and return its value
});
};
return svc;
});
Here is how i did it in my own project.
Your Service
angular.module("SomeModule").factory( "SomeService", function( $http ) {
var svc = {};
svc.data = {};
// on initialization, load data from the server
svc.getData = function(){
return $http.get( "somefile.json" );
};
return svc;
} );
Your Controllers
angular.module("SomeModule").controller( "SomeController", function( $routeParams, SomeService ) {
ctrl.items = null; // set an initial value
// load the item that was requested in the URL
SomeService.getData().success(function(data){
ctrl.items = data;
}).error(function(response){
console.err("damn");
});
} );
Important point : Promises
In my humble opinion, the responsibility for processing asynchronous call is due to the controller. I always return a $http promiss whenever i can.
svc.getData = function(){
return $http.get( "somefile.json" );
};
You can add some logic in your service but you always have to return the promise. (To know : .success() on a promise return the promise)
The controller will have the logic to know how to behave depending to the response of your asynchronous call. He MUST know how to behave in case of success and in case of error.
If you have more question feel free to ask. I hope it helped you.
ther are 2 good options you can
use callback
use $q return promise
Using Callback:
svc.getItem = function( id,callback ) {
$http.get( "somefile.json" )
.success( function( data ) {
svc.data = data;
callback(svc.data)
} );
};
in controller
SomeService.getItem( $routeParams.id,function(data){
ctrl.item = data
} );
Using Promise:
svc.getItem = function( id) {
var deferred = $q.defer();
$http.get( "somefile.json" )
.success( function( data ) {
svc.data = data;
deferred.resolve(svc.data);
} )
.error(function (error) {
deferred.reject(error);
});
return deferred.promise;
;
};
in controller
SomeService.getItem( $routeParams.id).then(function (data) {
ctrl.item = data
},
function (error) {
//do something with error
});
Here's how we do it, we use $q to defer whatever the async call will provide your service, I then take the data part of the response and resolve it, it sends the required data to the controller (without status, headers...).
I use a try catch statement in my service, to keep error handling away from the controller.
angular.module("JobsService", [])
.factory("JobsService", ['$q', '$http', '$log', function ($q, $http, $log) {
var serviceName = "JobsService";
var url = "http://localhost:8080/path/to/resource/";
var service = {};
service.getAll = function () {
var deferred = $q.defer();
try {
$http.get(url + "/jobs")
.success(function (response, status) {
$log.debug("GET response in " + serviceName + " returned with status " + status);
deferred.resolve(response);
})
.error(function (error, status) {
deferred.reject(error + " : " + status);
});
} catch (err) {
$log.error(err);
deferred.reject();
}
return deferred.promise;
};
return service;
}]);
then in controller
JobsService.getAll()
.then(function (response) {
$scope.jobs = response;
// records are stored in $scope.jobs
}, function (response) {
$scope.jobs = undefined;
})
.finally(function () {
// will always run
});

AngularJS Two way data binding between controller and service

I have a service in angular JS which is defined as follows:
'use strict';
angular.module('Offering.AccountService', [])
.service('Account', function($http, config) {
var account = this;
account.accountDetails = {};
this.getAccountDetails = function(){
var request = $http({
method: 'GET',
url: config.accountURL,
headers: {
'X-Parse-Application-Id': config.applicationID,
'X-Parse-REST-API-Key': config.restAPIKey,
}
})
.success(function(data, status, headers, config) {
console.log('Successfully aqquired account details');
account.accountDetails = data.results[0];
})
.error(function(data, status, headers, config) {
console.warn(status);
});
}
});
This service calls out to an endpoint to retrieve some data, I am calling this method in the run function of the main module as is shown below:
'use strict';
angular.module('Offering', [
'routerRoutes'
])
.run(function(Account){
Account.getAccountDetails();
})
In my controller, I have a variable called balance, in which I want to store the value retrieved from the service, as is shown in the code below:
'use strict';
angular.module('Offering.HomeController', [])
.controller('HomeCtrl', function(Account) {
this.data = {
message: "Account Details",
updateBalance: function() {
Account.getAccountDetails()
},
balance: Account.accountDetails.balance
}
// this.$watch(
// function () { return Account.accountDetails; },
// function (data) {
// $scope.data.balance = data.balance
// },
// true
// );
})
The data from the service is returned after the controller data object has been set up and thus the balance field is not getting populated. If I use the $scope.watch approach that is commented out, then the data object gets populated properly. However, I was under the assumption that this need not be done as there is a two way data binding between the service and the controller. Any help is greatly appreciated. Thanks !
The .run function doesn't stop to wait for the getAccountDetails to return a value. And so, when you assign this.data.balance = Account.accountDetails.balance, you are essentially doing this: this.data.balance = undefined.
As per comment above, the typical way is to have getAccountDetails() to return a promise, like so:
this.getAccountDetails = function(){
return $http({...})
.then(function(response){
console.log("successfully returned balanace", response.data);
return response.data;
});
}
Then, in the controller:
var vm = this;
Account.getAccountDetails()
.then(function(accountDetails){
vm.balance = accountDetails.balance;
});
You can not put a watch on the function which is returning promise, better you could put watch on this.data, Their is different way of put watch while using this keyword inside controller.
CODE
$scope.$watch(angular.bind(this, function () {
return this.data;
}), function (newVal, oldVal) {
// now we will pickup changes to newVal and oldVal
});
Hope this could help you , Thanks.

Why is this factory returning undefined?

I would like assistance in figuring out why this service I have created is returning undefined when I print it to console.
module.js
'use strict';
var Search = angular.module('SearchApp',["ngCookies","ngRoute"]);
Search.run(function ($http, $cookies) {
$http.defaults.headers.common['X-CSRFToken'] = $cookies['csrftoken'];
});
Search.config(function($routeProvider){
$routeProvider
.when('/', {
controller:'searchCtrl',
resolve: {
dota_items: function (InventoryService){
return InventoryService.get('dota');
}
}
})
.otherwise({
redirectTo: '/'
})
});
This is the module file where I have declared a route.
servcie.js
Search.factory('InventoryService', function ($http, $q) {
var api_url = "/api/inventory/";
return {
get: function (inventory) {
var inv_url = api_url + inventory + '/';
var defer = $q.defer();
$http({method: 'GET', url: inv_url}).
success(function (data, status, headers, config){
defer.resolve(data);
})
.error(function (data,status, headers, config){
defer.reject(status);
});
return defer.promise;
}
}
});
As far as I can tell the syntax is correct for my service however I may have missed something.
controller.js
Search.controller('searchCtrl',['$scope', function($scope, dota_items){
console.log(dota_items);
$scope.selected = 'have';
$scope.setSection = function(section){
$scope.selected = section;
};
$scope.isSelected = function(section){
return $scope.selected == section;
};
}]);
Here is the issue, In the console, the variable is always undefined. I have attempted to check if my service is even being called by printing to console however nothing is logged. I'm not sure what the issue could be.
Thanks
When you declare the 'searchCtrl' controller with the inlined array dependency injection syntax
['$scope', function($scope, dota_items) { }]
angular ignores the actual argument names in the function in favor of what you've specified in the array. Consider adding dota_items to the array before the function.
Search.controller('searchCtrl', ['$scope','dota_items',function($scope, dota_items) {
// your code
}]);
You can also remove the array entirely and use angular's implicit dependencies feature like so:
Search.controller('searchCtrl', function($scope, dota_items) {
// your code
});
but it should be noted that this approach only works if you're not minifying or obfuscating your code.

Where in angular should I put this code?

I have an http-method that gets some data from a google spreadsheet. I want to add this to the $scope so I can output it in the DOM. Later I might make a timed loop of this so that the $scope get's updated every 5 seconds or so.
I currently run the code in app.run:
angular.module('spreadsheet2angular', []).
run(function($http){
$http({method: 'GET', url: 'http://cors.io/spreadsheets.google.com/feeds/cells/0Aq_23rNPzvODdFlBOFRYWlQwUFBtcXlGamhQeU9Canc/od6/public/values?alt=json'}).
success(function(data, status, headers, config) {
var entries = data.feed.entry;
var phraces = [];
entries.forEach(function(entry){
var cell = entry.gs$cell;
if(!phraces[cell.row]){
phraces[cell.row] = {};
}
if(cell.col == 1)
{
phraces[cell.row].name = cell.$t;
}
else if(cell.col == 2)
{
phraces[cell.row].value = cell.$t;
}
});
phraces.forEach(function(phrace){
console.log(phrace);
});
}).
error(function(data, status, headers, config) {
console.log('error');
});
});
I'm new to angular, is this the best place to run it? I would like to run it as something that is easily reusable in different projects.
I think from what you've explained, a service would be perfect. Build it out then inject it in your controller. You can then call/use that service object whenever you would like.
I would use service/factory that returns promise. So we call async service method, get back promise and parse response into controller.
If you think to use the same call in the future, you can write generic method.
By the same way, if you are going to parse response by the same way in the future, the part of logic I would put into the service as well and wrap with $q . So the response still will be promise.
And this is an example I use that might help you to understand what I'm meaning:
app.service('apiService', ['$http', '$q', '$rootScope',
function($http, $q, $rootScope) {
var request = function(method, data) {
var deferred = $q.defer();
var configHttp = {
method: 'POST',
url: config.api + '/' + method
};
if (data !== undefined) {
configHttp.data = data;
}
$http(configHttp).success(function(data, status, headers) {
if (data.error === undefined) {
deferred.resolve(data);
} else {
deferred.reject(data);
}
}).error(function(data, status, headers) {
deferred.reject(data);
});
return deferred.promise;
}
return {
getItem: function() {
return request('get_item');
},
getItemByParams: function(id) {
return request('get_item_by_params', {id: id});
}
};
}
]);

Resources