calling function inside angular factory - angularjs

I have a factory, how do i call 'getAccountDetails' service inside getAccountInformation() function.
I tried in the following way, but not working.
AccountService.getAccountDetails.getAccountDetailsService
factory
tellerApp.factory('AccountService',['$resource', function ($resource) {
return{
getAccountDetails: $resource(XXX, {}, {
getAccountDetailsService: {}
}),
getAccountInformation: function($scope, number, transaction, index){
AccountService.getAccountDetails.getAccountDetailsService({number : number})
.$promise.then(function(response){});
}
}]);

I suggest you define your code dependencies out of the returned provider:
tellerApp.factory('AccountService',['$resource', function ($resource) {
var getAccountDetails = $resource(XXX, {}, {getAccountDetailsService: {}});
return {
getAccountDetails : getAccountDetails,
getAccountInformation: function($scope, number, transaction, index){
getAccountDetails.getAccountDetailsService({number : number}).$promise.then(function(response){
...
})
}
};
}]);
Or, inside an object, you can also use this to refer to the current object, instead of using AccountService.getAccountDetails, you should use this.getAccountDetails.
tellerApp.factory('AccountService',['$resource', function ($resource) {
return {
getAccountDetails : $resource(XXX, {}, {getAccountDetailsService: {}});,
getAccountInformation: function($scope, number, transaction, index){
this.getAccountDetails.getAccountDetailsService({number : number}).$promise.then(function(response){
...
})
}
};
}]);
Plus, be careful, because your naming convention is confusing (getAccountDetails is not a function, since you don't call it with (), but it is named "get", getAccountServices is first defined as an object, but the same name is used later for a funtion...), especially if you want an accurate answer ;)

This should work, havent tested it though.
tellerApp.factory('AccountService',['$resource', function ($resource) {
var AccountService = {
getAccountDetails: $resource(XXX, {}, {
getAccountDetailsService: {}
}),
getAccountInformation: function($scope, number, transaction, index){
AccountService.getAccountDetails.getAccountDetailsService({number : number}).$promise.then(function(response){
}
};
}
return AccountService
}]);

Related

AngularJS How to access local scope from outside function?

I have this code in my service
orderSewaService.vehicleDetail = function (license_plate) {
//var defer = $q.defer();
var config = {
headers: {
'X-Parse-Application-Id': parseAppId
},
params: {
where: {
vehicle_license_plate: license_plate,
vehicle_status: 'available'
},
limit: 1,
include: 'car_id.car_class_id,pool_id.city_id,partner_id.user_id'
}
}
return $http.get('http://128.199.249.233:1337/parse/classes/vehicle', config).then(function (response) {
var detail = {
license_plate: response.data.results[0].vehicle_license_plate,
photo: response.data.results[0].vehicle_photo,
partner_name: response.data.results[0].partner_id.user_id.full_name,
year: response.data.results[0].vehicle_year,
class: response.data.results[0].car_id.car_class_id.name,
pool_address: response.data.results[0].pool_id.pool_address,
city: response.data.results[0].pool_id.city_id.city_name,
zone_id: response.data.results[0].zone_id.objectId,
car_class_id: response.data.results[0].car_id.car_class_id.objectId
};
return detail;
//defer.resolve(detail);
}, function (error) {
//defer.reject(error);
return error;
});
//return defer.promise;
};
in my controller
$scope.vehicle = {};
orderSewaService.vehicleDetail($routeParams.license_plate).then(function(response){
$scope.vehicle = response;//rendered in view
console.log($scope.vehicle); //log object success
}, function (error) {
console.log(error);
});
console.log($scope.vehicle); //doesn't work //empty object
//My goal is I will call other service function like this
orderSewaService.infoTarif($scope.vehicle.zone_id, $scope.vehicle.car_class_id).then(...);
Already read this access scope data from outside function but looks like to complex or not suit for my simple goal.
How I can access $scope.vehicle outside function or how to achieve my goal ?
And I don't think $rootScope is good solution in this case.
You need to declare $scope.vehicle outside the function call,
somewhere in your controller at the begining,
If it's an array
$scope.vehicle =[];
The problem is with the way this controller code flow works.
$scope.vehicle = {}; //vehicle property is declared and defined as empty obj in the $scope
orderSewaService.vehicleDetail($routeParams.license_plate)
This is an ajax call, js calls this method and then goes to the next line , after the end of this method, i.e.
console.log($scope.vehicle); without waiting for the call to return and populate $scope.vehicle with your response.
So, try this:
In Controller:
`
$scope.vehicle = {};
orderSewaService.vehicleDetail($routeParams.license_plate).then(function(response){
$scope.vehicle = response;//rendered in view
getInfoTarif();
}, function (error) {
console.log(error);
});
function getInfoTarif(){
console.log($scope.vehicle);
orderSewaService.infoTarif($scope.vehicle.zone_id,$scope.vehicle.car_class_id).then(...);
}
`
I think there are two matter of concerns in this question.
Firstly - sync & async methods
Since orderSewaService.vehicleDetail is asynchronous, $scope.vehicle would be null.
If you are not sure what that means, compare the two:
var foo = null;
foo = ['a','b'];
console.log(foo); // ['a','b']
versus
var foo = null;
setTimeout(function(){
foo = ['a','b'];
console.log(foo); // array
}, 500); // or any value including zero
console.log(foo); // null
Conclusively, your code should look like this:
$scope.vehicle = {};
orderSewaService
.vehicleDetail($routeParams.license_plate)
.then(function(response){
$scope.vehicle = response;//rendered in view
console.log($scope.vehicle); //log object success
//My goal is I will call other service function like this
orderSewaService.infoTarif($scope.vehicle.zone_id, $scope.vehicle.car_class_id).then(...);
}, function (error) {
console.log(error);
});
There are a ton of articles and docs that describe this, if you are further interested.
Secondly - load contents before reaching controller
Now, from how you described the problem, it seems like you also want to load the contents of orderSewaService.vehicleDetail based on a URL parameter before it reaches the controller. Otherwise, you will have to call orderSewaService.vehicleDetail and orderSewaService.infoTarif in every controller.
A much cleaner and more common approach is to use ui-router's $stateProvider. Tutorials here
If you run a few examples from their docs, you can inject dependencies into your controller like this:
app.route.js
$stateProvider
.state('vehicles', {
url: '/vehicles',
resolve: {
vehicles: ['VehiclesService', function(VehiclesService){
return VehiclesService.getAll();
}]
},
controller: 'VehiclesListCtrl',
templateUrl: 'vehicles.html'
})
.state('vehicles.detail', {
url: '/vehicles/:vehicleId',
resolve: {
info: ['VehiclesService', '$stateParams', function(VehiclesService, $stateParams){
return VehiclesService.get($stateParams.vehicleId)
.then(function(vehicle){
return orderSewaService.infoTarif(vehicle.zone_id, vehicle.car_class_id)
.then(function(tarif){
return {
vehicle: vehicle,
tarif: tarif
};
});
});
}]
},
controller: 'VehicleDetailCtrl',
templateUrl: 'vehicle.detail.html'
});
vehicle.detail.controller.js
.controller('VehicleDetailCtrl', VehicleDetailCtrl);
VehicleDetailCtrl.$inject = [
'$scope',
'info'
];
function VehicleDetailCtrl(
$scope,
info
) {
console.log('vehicle %o tarif %o', info.vehicle, info.tarif);
}
vehicles.controller.js
.controller('VehiclesCtrl', VehiclesCtrl);
VehiclesCtrl.$inject = [
'$scope',
'vehicles'
];
function VehiclesCtrl(
$scope,
vehicles
) {
console.log('vehicles list %o', vehicles);
}
To access this state, you need to do something like
menu.html
<a ui-sref="vehicles.detail({vehicleId: 1234})">
I purposely did not make vehicles route abstract for illustration purposes. You may want to look into that if you want to create nested state/views.
I hope this helps.

Awaiting for services to return data or converting service into global dataset and passing it into angular controller?

I have simple problem of needing to wait on certain data to return from service calls before executing logic that depends on the data in question.
As confusing as it sounds I have this extract controller that I am working on at the moment which is exhibiting that problem.
// async services: $stateParams, GetTags, GetStrands, GetLessons, GetPlan, UpdatePlan, SavePlan
myApp.controller('EditNewCtrl', ['$scope', '$stateParams', 'GetTags', 'GetStrands', 'GetLessons', 'GetPlan', 'UpdatePlan', 'SavePlan', function ($scope, $stateParams, GetTags, GetStrands, GetLessons, GetPlan, UpdatePlan, SavePlan) {
// $stateParams correspondent to two different routes for this controller
// #/~/
// #/~/:planId
// #/~/:planId/:year/:level
$scope.planId = $stateParams.planId; // only defined when editing a plan
$scope.year = $stateParams.year; // may or may not be defined
$scope.level = $stateParams.level; // may or may not be defined
...
// calls to retrieve stuff from the server
GetTags.get({ groupId: 12, }, function (data) {
$scope.tags = data.tags; // go know when this will return
});
GetStrands.get({ groupId: 12, }, function (data) {
$scope.strands = data.strands; // god knows when this will return
});
GetLessons.get({ groupId: 12, }, function (data) {
$scope.lessons = data.lessons; // god know when this will return
});
// helpers
...
// init select controls
if ($scope.planId != undefined && $scope.planId > 0) {
GetPlan.get({ planId: $scope.planId, groupId: 12, }, function (data) {
var plan = data.plan; // god know when this will return
plan.Year = $scope.getYearById(plan.Year); // convert int to object
plan.Level = $scope.getLevelById(plan.Level); // convert in to object
$scope.plan = plan;
});
} else {
$scope.plan = { Name: '', Strand: {}, Year: {}, Level: {}, Tags: [], Lessons: [], };
}
if ($scope.year != undefined) {
$scope.plan.Year = $scope.getYearObj($scope.year);
}
if ($scope.level != undefined) {
$scope.plan.Level = $scope.getLevelObj($scope.level);
}
}]);
More often then not I run into a problem with $scope.plan.Year = $scope.getYearObj($scope.year); and $scope.plan.Level = $scope.getLevelObj($scope.level); when I enter edit mode. While I understand that service call happens asynchronously but what is the common ways of slowing down subsequent calls? Or perhaps its better to just encapsulate problem loginc in $scope.$watch?
I have another concern with $scope.tags and $scope.strands. Is it possible to have these datasets pre-fetched and managed (when I say managed I mean refreshed every so often in the background) on a more global level and have them passed in as references instead rather than retrieving these in every controller that I come up with. Please advise if there is Angular structure or mechanism for something like this.
In any case it is clear to me that I am doing something wrong here. Please advice what is the best course of action or where I should look.
Complementary notes
Just to complement suggested solution to my dilemma.
Because I am not using the $http services but instead I use AngularJs REST/factory services. Example of such service would look like so:
myApp.factory('GetTags', ['$resource', function ($resource) {
return $resource('/API/Service/GetTagList', {}, {
query: { method: 'GET', params: {}, }, isArray: true,
});
}]);
How to use this in a controller is already shown above but sometimes that's not enough. This is how one would use this service in a situation when you need access to then:
.state('state', {
url: '/url:Id',
templateUrl: '/template.html',
resolve: {
Tags: function (GetTags) {
//TODO: need to make a directive that returns groupId
return GetTags.get({ groupId: 12, }).$promise.then(
function (data) {
if (data.success) {
return data.tags;
} else {
return [];
}
});
},
},
controller: 'EditNewCtrl',
})
Here $promise is used to gain access to the raw $http promise object which allows us to use .then() to await for the call to resolve. Without $promise in other words just return GetTags.get({ groupId: 12, }) would return the promise object to the controller in question which no good.
To gain access to for example $stateParams.Id just pass it into the function call like so:
Tags: function (GetTags, $stateParams) {
return $stateParams.Id;
},
That's about it really. Just don't forget to pass in your resolved data objects/structures into your controller.
PS: Also important to note that definition for controller must come after definition for resolve otherwise it doesn't work.
PSS: I hope that advice that I have received and my example helps to complement the answers given.
As someone already mentioned resolve in the $stateProvider is the way to go.
However what you could also do is this :
'use strict';
angular.module('YourApp')
.service('YourService', function ($http) {
return {
doSomething: function (id, success, error) {
return $http.post(
'rest/bla/' + id, {}
).success(function (response) {
success(response);
}).error(function () {
error();
});
},
doSomethingElse: function (id, success, error) {
return $http.post(
'rest/bla/' + id, {}
).success(function (response) {
success(response);
}).error(function () {
error();
});
},
doSomethingDifferent: function (id, success, error) {
return $http.post(
'rest/bla/' + id, {}
).success(function (response) {
success(response);
}).error(function () {
error();
});
},
};
});
//then in your controller
angular.module('YourApp')
.controller('YourController', function(YourService){
// you add a success and error function for when the data is returned.
YourService.doSomething(id, function(data){
$scope.yourData = data;
YourService.doSomethingElse(id, function(data){
$scope.somethingElse = data;
YourService.doSomethingDifferent(id, function(data){
$scope.somethingDifferent = data;
// al three have been set so you can place initialization code here
}
}
}, function(){console.log('something went wrong'));
});
but what you really should do is this
$stateProvider.state('myState', {
url: 'the/url/you/want',
resolve:{
yourService: 'yourService' // you are dependency injecting it here,
yourFetch: function (yourService) {
return yourService.yourFetch.$promise;
},
yourSecondFetch: function(yourService) {
return yourService.yourSecondFetch.$promise;
},
yourTirthFetch: function(yourService) {
return yourService.yourTirthFetch.$promise;
},
controller: 'YourController'
})
// then your controller can just inject the yourFetch and they will be resolved before your controller loads so it will always be fetched prior
.controller('YourController', function($scope, yourFetch, yourSecondFetch, yourTirthFetch) {
$scope.yourFetch = yourFetch;
$scope.secondFetch = yourSecondFetch;
$scope.tirthFetch = yourTirthFetch;
});
I abstracted the idea of #Arno_Geismar into a reusable component/service, but as stated previously this is probably a bad idea of making asychronous code synchronous.
self.init = function (array, fx) {
if (array.length > 0) {
//The request for data.
if (array[0].data=== null) {
$http.get(array[0].link).success(function ($response) {
window.session.set(array[0].ref, $response);
array[0].data= $response;
check(array);
}).error(function () {
self.init(array, fx);
});
} else {
check(array);
}
} else {
exec(fx);
}
//Check whether the recursive function can stop.
function check(array) {
//Bypass when all the reference data has been set previously.
//All objects are set = exit recursive
if (array.every(function (e) {
return e.data!== null;
})) {
exec(fx);
} else {
self.init(array.slice(1, array.length), fx);
}
}
//Function to execute the fx, if available.
function exec(fx) {
if (fx !== null) {
fx();
}
}
};

AngularJS hide promises while using $resource

I have a simple RESTful service like the following:
services.factory('UserService', ['$resource, function() {
return $resource('...');
}]);
This way I have to invoke like this:
UserService.get({id: userId}, function(response) {
// do something.
});
I wanted to be able to do something like this:
UserService.get(userId).then(function(response) {
// do something with data
});
Is it possible? I am struggling with this and end up always having to use $promise.then() in my controllers. I wanted to "hide" that $promise in my RESTful service.
$resource purposely exposes the promise through ... well ... $promise, so you can use the UserService to abstract this:
services.factory("UserService", ["$resource", function ($resource) {
var userService = $resource("...");
return {
get: function (id) {
return userService.get({id: id}).$promise;
}
}
});
According to Todd Motto you could encapsulate your service somewhat like this :
"We create an Object with the same name inside the function. This can aid documentation as well for comment-generated docs."
function AnotherService () {
var AnotherService = {};
AnotherService.someValue = '';
AnotherService.someMethod = function (idParam) {
return $resource(apiUrl + ":action/:id", {}, {
get: {method: 'GET', params: {id:idParam, action: 'get'}
};
return AnotherService;
}
angular.module('app')
.factory('AnotherService', AnotherService);

Service returning resources. Is it possible?

Im trying a service returning multiple resources to be injected in a controller, but calling resource "query" method I get an error that method doesn't exist. below will find the code example of what I want to do. I do method call at controller side because there is additional logic when promise success. Is there a way to do that?
//Service
ImageRepo.factory('ServerCall', ['$resource', function ($resource) {
return {
Image: function () {
return $resource('/api/image/:id', { id: '#id'});
},
Meta: function () {
return $resource('/api/metadata/:id', { id: '#id'});
}
}
}]);
//Controller function
var ImageCtrl = function ($scope, ServerCall) {
...
$scope.metadefsearch = function () {
ServerCall.Meta.query().$promise.then(function (metasdef) {
$scope.metasdefinition = metasdef;
//Some additional logic.....
});
};
};
Let Meta and Image be objects instead of functions:
ImageRepo.factory('ServerCall', ['$resource', function ($resource) {
return {
Image: $resource('/api/image/:id', { id: '#id'});,
Meta: $resource('/api/metadata/:id', { id: '#id'})
};
}]);
Since your two properties are methods you should do this:
ServerCall.Meta().query().$promise.then(function (metasdef) {

Can I have multiple functions in my AngularJS Factory?

I'm following the Tutorial from the official AngularJS docs and I want to know if I can add another function to the Phone factory so that I can organize code better. They have declared a "query" function, but what if I wanted to add a query2 function that references a different url...say phones2/:phoneName.json for example?
Factory declaration:
var phonecatServices = angular.module('phonecatServices', ['ngResource']);
phonecatServices.factory('Phone', ['$resource',
function($resource){
return $resource('phones/:phoneId.json', {}, {
query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
});
}]);
I have tried a number of things and non of them seem to be working :s
This answer seems to be on the right track, but the syntax for each factory function doesn't quite match up with the above factory.
Something along the lines of:
phonecatServices.factory('Phone', ['$resource',
function($resource){
return {
query: ...
query2: ...
}
}]);
One such example of this is:
Link for Demo
angular.module('services', []).factory('factoryName', ["$filter",
function($filter) {
var method1Logic = function(args) {
//code
};
var method2Logic = function(args) {
//code
};
return {
method1: method1Logic,
method2: method1Logic
};
}
]).controller('MainController', ["$scope", "$rootScope", "$filter", "factoryName", function ($scope, $rootScope, $filter,factoryName) {
$scope.testMethod1 = function(arg){
$scope.val1 = factoryName.method1(arg);
};
$scope.testMethod2 = function(arg){
$scope.val2 = factoryName.method2(arg);
};
}]);
There is even a better version Opinionated version of this: References
function AnotherService () {
var AnotherService = {};
AnotherService.someValue = '';
AnotherService.someMethod = function () {
};
return AnotherService;
}
angular
.module('app')
.factory('AnotherService', AnotherService);
This is the service code:
myServices.factory('Auth', ['$resource',
function($resource){
return {
Login: $resource(serviceURL + 'login', {}, { go: { method:'POST', isArray: false }}),
Logout: $resource(serviceURL + 'logout', {}, { go: { method:'POST', isArray: false }}),
Register: $resource(serviceURL + 'register', {}, { go: { method:'POST', isArray: false }}),
};
}
]);
And from my controller I just have to add the go() function call to make it work:
Auth.Login.go({ username: $scope.username, password: $scope.password },
I guess I could have named the go function after the method and called it "post()" instead for clarity...
Yes, Of course, you can have multiple functions in an object. The only caveat is your service should return an object. You can have all the valid javascript members in that object as long as you follow object's syntax.
So following is possible
phonecatServices.factory('Phone', ['$resource',
function($resource){
return {
query: ... , // NOTICE THE COMMA HERE
query2: ...
}
}]);
You must be missing the comma (,) to separate your object's key values.

Resources