Angular variable in factory? - angularjs

In a Angular app i have a couple of calls to a WebAPI service.
The first call, lets call it call1, should determine a true/false value of a variable.
So i have this
vm.call1.$promise.then(function (data) {
$rootScope.variable1 = data.variable;
});
This variable is to be used inside a factory i have;
myApp.factory('myStore', function ($http, $q, $rootScope) {
var showAll = $rootScope.variable1;
var get = function () {
if (showAll) {
//do this
} else {
//do this
}
};
return {
get: get
};
});
My problem is, that the factory above is loaded on pageload, and before the promise, and therefore the value is undefined.
How can i get the value, over to my factory, after the promise is complete?

No rootscope example: You can set the showAll flag manually:
vm.call1.$promise.then(function (data) {
myStore.setShowAll(data.variable);
});
and in the Factory:
myApp.factory('myStore', function ($http, $q) {
var showAll = 'true or false to be a default';
var get = function () {
if (showAll) {
//do this
} else {
//do this
}
};
var setShowAll = function(value) {
showAll = value;
};
return {
get: get,
setShowAll: setShowAll
};
});
Rootscope example: If you for some reason really need to use $rootScope for this you can just check for $rootScope value when you get, you don't have to store the value in a varibale.
myApp.factory('myStore', function ($http, $q, $rootScope) {
var get = function () {
if ($rootScope.variable1) {
//do this
} else {
//do this
}
};
return {
get: get
};
});

Related

How to update angularjs.module.value with array and retrieving it through service?

I'm trying to update a global angularjs module.value in one controller with an array, and then retrieving that global array through in a service. But the array doesn't exist.
app.js
app.factory('featureClaims', function($q) {
var featureClaims = {};
featureClaims.init = function() {
featureClaims.claims = [];
}
featureClaims.get = function() {
return $q.when(featureClaims.claims);
}
featureClaims.set = function(data) {
featureClaims.claims = data;
return $q.when(featureClaims.claims); // I'm using the $q library to return a promise.
}
return featureClaims;
});
loginController
let loginController = function($scope, loginService, toastrObj, featureClaims) {
$scope.login = function(){
featureClaims.init();
featureClaims.set(result.data.FeatureClaims); // updating ok here
}
}
app.controller("loginController", ["$scope", 'loginService', 'toastrObj', 'featureClaims',loginController]);
home service
let homeService= function(featureClaims) { // featureClaims.claims is null
return{
validateUser: function(expectedClaim) {
if(expectedClaim !== ""){
featureClaims.get().then(function(data){
return data.includes(expectedClaim); // data is return as undefined
})
}
return false;
}
}
};
app.factory('homeService',['featureClaims', homeService]);
I don't think you can use a value service this way. From this post the author states: "Note: Make sure that you never overwrite the value service/object as a whole otherwise your assignment is lost. Always reassign the property values of the value object. The following assignment is wrong and does not lead to the expected behavior"
Instead why don't you convert your value to a factory like so:
app.factory('featureClaims', function($q) {
var featureClaims = {};
featureClaims.init = function() {
featureClaims.claims = [];
}
featureClaims.get = function() {
return $q.when(featureClaims.claims);
}
featureClaims.set = function(data) {
featureClaims.claims = data;
return $q.when(featureClaims.claims); // I'm using the $q library to return a promise.
}
return featureClaims;
});
In your controller:
featureClaims.init();
// you need to wait for the promise to resolve with 'then'
featureClaims.set(['foo', 'bar']).then(function(response) {
console.log(response); // logs ["foo", "bar"]
});
featureClaims.get().then(function(response) {
console.log(response); // logs ["foo", "bar"]
});
Tested and working. You will want to create a get method that simply returns the data instead of setting it first.

TypeError: Cannot read property 'then' of undefined at m.$scope.profile

I am trying to call a child service function in Main Controller.
But I am getting error on than in profile function.
This is my profile function inside main controller.
App.controller("globalCtrl", function($scope,$rootScope, $window, $location, $rootScope, $cookieStore, toastr, ClientService,) {
var theClient = $cookieStore.get('UserData') || {};
$scope.profile = function(theClient) {
var getCurrentClient = theClient;
var getOrganizationId = $rootScope.globalSession.OrganizationId;
if($rootScope.globalSession.UserRole == "Admin")
{
if (getOrganizationId) {
ClientService.getClient(getOrganizationId).then(function(aGetClientResponse) {
if(getClientResponse[0] == $scope.RESPONSE_CODE.CM_SUCCESS) {
$scope.myprofile = getClientResponse[1];
$location.path("/profile");
}
else {
toastr.warning($scope.USER_MESSAGE.SERVICE_NOT_AVAILABLE, '');
}
});
}
}
};
)};
This is Service of another controller whom function "getClient" I am calling in profile function.
App.factory('ClientService', function($http, $cookieStore, uuid2, API_URL, REQUEST_HEADER, RESPONSE_CODE) {
var theClient = $cookieStore.get('UserData') || {};
return {
getClient: function() {
if(theClient.OrganizationId) {
//API Call
var promise = $http.get(API_URL.GET_CLIENT+theClient.OrganizationId+"&CorrelationId="+uuid2.newuuid()+"&ContextOrganizationId=009", REQUEST_HEADER).then(
function(aGetClientResponse) { //Success Callback
return [aGetClientResponse.data.GetClientResponse.Result.ResponseCode, aGetClientResponse.data.GetClientResponse];
},
function(aGetClientResponse) { //Error Callback
return [aGetClientResponse.status,''];
});
}
return promise;
},
setClient: function(aClient) {
theClient = aClient;
$cookieStore.put('UserData', theClient);
}
}
});
Need Help. cant figure out the Problem. Thanks in advance!
Change your service to just return a promise and handle that promise in your controller using then
The service directly returns API call, no then in service
var promise = $http.get(API_URL.GET_CLIENT+theClient.OrganizationId+"&CorrelationId="+uuid2.newuuid()+"&ContextOrganizationId=009", REQUEST_HEADER)
Here is your service:
App.factory('ClientService', function($http, $cookieStore, uuid2, API_URL, REQUEST_HEADER, RESPONSE_CODE, $q) {
var theClient = $cookieStore.get('UserData') || {};
return {
getClient: function() {
if(theClient.OrganizationId) {
var promise = $http.get(API_URL.GET_CLIENT+theClient.OrganizationId+"&CorrelationId="+uuid2.newuuid()+"&ContextOrganizationId=009", REQUEST_HEADER)
}
else {
var promise = $q.reject();
}
return promise;
},
setClient: function(aClient) {
theClient = aClient;
$cookieStore.put('UserData', theClient);
}
}
});
Now, the controller handles the success and failure response using then
ClientService.getClient(getOrganizationId).then(function(){},function(){})
Here is your controller, observe then
App.controller("globalCtrl", function($scope,$rootScope, $window, $location, $cookieStore, toastr, ClientService) {
var theClient = $cookieStore.get('UserData') || {};
$scope.profile = function(theClient) {
var getCurrentClient = theClient;
var getOrganizationId = $rootScope.globalSession.OrganizationId;
if($rootScope.globalSession.UserRole == "Admin")
{
if (getOrganizationId) {
ClientService.getClient(getOrganizationId).then(function(aGetClientResponse) {
var response = [aGetClientResponse.data.GetClientResponse.Result.ResponseCode, aGetClientResponse.data.GetClientResponse];
if(response[0] == $scope.RESPONSE_CODE.CM_SUCCESS) {
$scope.myprofile = response[1];
$location.path("/profile");
}
else {
toastr.warning($scope.USER_MESSAGE.SERVICE_NOT_AVAILABLE, '');
}
},
function(aGetClientResponse) { //Error Callback
return [aGetClientResponse.status,''];
});
}
}
};
)};
In getClient() the code looks like this:
if(theClient.OrganizationId) {
//API Call
var promise = ... some stuff ...;
}
return promise;
So if the condition is false you return undefined which doesn't have a .then property.
You should make sure that a function which returns a promise always returns a promise. If you want to signal that a missing id is an error then just return $q.reject(something) for an already rejected promise.
I'm not sure it this works, but instead of returning a object with the functions inside, have you tried to declare them as part of the factory object? I have some declared like this and they are working, so have a try:
App.factory('ClientService', function($http, $cookieStore, uuid2, API_URL, REQUEST_HEADER, RESPONSE_CODE) {
var theClient = $cookieStore.get('UserData') || {};
var function = {
getClient: function() {
if(theClient.OrganizationId) {
//API Call
var promise = $http.get(API_URL.GET_CLIENT+theClient.OrganizationId+"&CorrelationId="+uuid2.newuuid()+"&ContextOrganizationId=009", REQUEST_HEADER).then(
function(aGetClientResponse) { //Success Callback
return [aGetClientResponse.data.GetClientResponse.Result.ResponseCode, aGetClientResponse.data.GetClientResponse];
},
function(aGetClientResponse) { //Error Callback
return [aGetClientResponse.status,''];
});
}
return promise;
},
setClient: function(aClient) {
theClient = aClient;
$cookieStore.put('UserData', theClient);
}
}
return function;
});
You can have a try with this too:
Define the services in the controller declaration (and remove one rootScope, its duplicated):
App.controller("globalCtrl", ['$scope','$rootScope','$window','$location','$cookieStore','toastr','ClientService', function($scope,$rootScope, $window, $location, $cookieStore, toastr, ClientService){
[...]
}]);
Try something like this to ensure that there is always a promise object available.
getClient: function() {
var deferred = $q.defer();
if(theClient.OrganizationId) {
//API Call
$http.get(API_URL.GET_CLIENT+theClient.OrganizationId+"&CorrelationId="+uuid2.newuuid()+"&ContextOrganizationId=009", REQUEST_HEADER).then(
function(aGetClientResponse) { //Success Callback
deferred.resolve(aGetClientResponse);
},
function(aGetClientResponse) { //Error Callback
deferred.resolve(aGetClientResponse);
});
}else{
deferred.reject('Missing OrganizationId');
}
return deferred.promise;
}
In the else part you can return dummy promise with either resolve($q.resolve()) or reject($q.reject()) status.
JS :
getClient: function() {
if(theClient.OrganizationId) {
//API Call
var promise = $http.get(API_URL.GET_CLIENT+theClient.OrganizationId+"&CorrelationId="+uuid2.newuuid()+"&ContextOrganizationId=009", REQUEST_HEADER).then(
///your rest code
return promise;
}else{
return $q.reject();
}
}
With $q.reject() it will call error handler in profile call.So,add error handler callback as second parameter
ClientService.getClient(getOrganizationId).then(function(aGetClientResponse) {
//success code
},
function(){ //add it in your script
//check error here
});

Jasmine + AngularJS: Global services causes Unexpected GET request

I have a service, $language, that gets called in app config (so before every Spec runs). The method called, $language.update(), triggers $translate.use() (which in turn triggers an $http.get()). This causes an Unexpected request: GET /<lang>/i18n.
I've tried a few different things to resolve this, but each seems to cause a new problem:
Globally mock the $translate service
// not inside a describe()
beforeEach(function() {
module(function($provide) {
$provide.value('$translate', {
get: function() { return false; },
storage: function() { return false; },
storageKey: function() {
return {
get: function() { return false; },
set: function() { return false; }
};
},
use: function() { return false; }
});
});
});
But something tries to call $translate(), so I tried making the mock a function returning an object, but that didn't work either.
Mocking the GET request via $httpBackend
// not inside a describe()
beforeEach(function() {
// this already existed to avoid another problem caused by $translate
module('MyApp', function config($translateProvider, $anotherProvider) {
// …
});
// new
inject(function($httpBackend) {
$httpBackend.when('GET', '/<lang>/i18n').respond({});
});
});
But then it complains Injector already created, can not register a module! (order of module and inject doesn't seem to matter).
I thought of globally mocking my $language service, but then I would not be able to test it in its own Spec.
Ideally I'd prefer to globally mock $translate as it seems to cause one problem after another.
The problem was that $translate is a provider; therefore a provider needs to be $provide'd:
// Outside of a describe so it's treated as global
beforeEach(function() {
module('MyModule', function config($providerA, $provide) {
// …
$provide.provider('$translate', function() {
var store = {};
this.get = function() { return false; };
this.preferredLanguage = function() { return false; };
this.storage = function() { return false; };
this.translations = function() { return {}; };
this.$get = ['$q', function($q) {
var $translate = function(key) {
var deferred = $q.defer(); deferred.resolve(key); return deferred.promise;
};
$translate.addPair = function(key, val) { store[key] = val; };
$translate.isPostCompilingEnabled = function() { return false; };
$translate.preferredLanguage = function() { return false; };
$translate.storage = function() { return false; };
$translate.storageKey = function() { return true; };
$translate.use = function() { return false; };
return $translate;
}];
});
});
});

multiple method call same single service with cachefactory in angularjs

I have a service like below... which always get all types of buyers from server.
var buyersService = function ($http, $q,$cacheFactory) {
var serviceBase = '/api/OMData/';
var BuyersFactory = {};
buyersService.cache = $cacheFactory('cacheId');
BuyersFactory.GetBuyers = function (type) {
var buyersDiffer = $q.defer();
var dataList = buyersService.cache.get('BuyerData');
if (dataList != null && dataList.length > 0) {
buyersDiffer .resolve(_getBuyerByType(type,dataList));
}
else {
$http.get(serviceBase + 'GetBuyers').then(
function (results) {
buyersService.cache.put("BuyerData", results.data);
buyersDiffer .resolve(_getBuyerByType(type,results.data));
});
}
return buyersDiffer .promise;
}
app.factory('OMDataService', ['$http', '$q', '$cacheFactory', buyersService]);
});
function _getBuyerByType(Type,dataList) {
try {
var typedBuyer= Enumerable.From(dataList).Where(function (x) {
return x.Type== Type;
}).ToArray();
return typedBuyer;
} catch (e) {
throw e;
}
}
function getLocalBuyer(){
return BuyersFactory.GetBuyers(1);
}
function getForeignBuyer(){
return BuyersFactory.GetBuyers(2);
}
There are two types of buyer in my business. Such as localBuyer and foreignBuyer. In my controller I need to call two service at a time with Q.All. When two service hit in buyersService method then system call server two times. Hence current cash is empty. But I want that system call server one times for one service call and second service get data from cache.
EDIT: Controller code
function loadDefaultData() {
try {
$q.all([
OMDataService.getLocalBuyer(),
OMDataService.getForeignBuyer(),
]).then(function (data) {
$timeout(function () {
//set dropdown list
viewData.local= data[0];
viewData.foreign= data[1];
}, 0);
}).catch(function (e) {
showError(e);
});
} catch (e) {
showError(e);
}
}
I would suggest defining a variable to save the promise returned by $http. The first time the service is called, that variable is initially undefined. It is set to the promise returned by $http.
The logic of your service thus becomes:
if cache, return cache
if promise, return promise
else call server and save promise
I removed you deferred variable. It is no more needed. I did wrap the cache response in $q.when() (doc: https://docs.angularjs.org/api/ng/service/$q)
var buyersService = function ($http, $q, $cacheFactory) {
var serviceBase = '/api/OMData/';
var BuyersFactory = {};
buyersService.cache = $cacheFactory('cacheId');
var serverPromise; // Will be used to store the promise returned by $http on first server call
BuyersFactory.GetBuyers = function (type) {
var dataList = buyersService.cache.get('BuyerData');
function serverCall () { // stores promise receives by $http and return it
serverPromise = $http.get(serviceBase + 'GetBuyers').then(
function (results) {
buyersService.cache.put("BuyerData", results.data);
return _getBuyerByType(type, results.data);
});
return serverPromise;
}
if (dataList !== null && dataList.length > 0) {
return $q.when(_getBuyerByType(type,dataList)); // auto resolving promise
}
// If a first call to the server was already done, serverPromise will exist and no further call to the server will be made
return serverPromise || serverCall();
};
app.factory('OMDataService', ['$http', '$q', '$cacheFactory', buyersService]);
});
function _getBuyerByType(Type,dataList) {
try {
var typedBuyer= Enumerable.From(dataList).Where(function (x) {
return x.Type== Type;
}).ToArray();
return typedBuyer;
} catch (e) {
throw e;
}
}

How to get data by service and $cacheFactory by one method

I have a factory which get data from server. In the factory method I have used $cacheFactory to cache getting data. My code is as follows..
var buyersService = function ($http, $q,$cacheFactory) {
var serviceBase = '/api/OMData/';
var BuyersFactory = {};
buyersService.cache = $cacheFactory('cacheId');
BuyersFactory.GetBuyers = function () {
var dataList = buyersService.cache.get('BuyerData');
if (dataList != null && dataList.length > 0) {
return dataList;
}
else {
return $http.get(serviceBase + 'GetBuyers').then(
function (results) {
buyersService.cache.put("BuyerData", results.data);
return results.data;
});
}
}
app.factory('OMDataService', ['$http', '$q', '$cacheFactory', buyersService]);
});
Now I have called GetBuyers method from controller. My method is like below..
var BuyerController = function ($scope, BuyersService) {
$scope.Buyers = [];
init();
function init() {
getBuyers();
}
function getBuyers() {
BuyersService.GetBuyers()
.then(function (data) {
$scope.Buyers = data;
}, function (error) {
alert(error.message);
});
}
};
app.register.controller('BuyersController', ['$scope', 'OMDataService', BuyerController]);
When I have executed my controller method second time I have got an error message in promise part.
Object doesn't support property or method 'then'
The issue here is that your function returns two different things: either a promise or plain data. To remedy this, use another promise to control the flow and return that one as the result of the function.
Update your code to
var buyersService = function ($http, $q,$cacheFactory) {
var serviceBase = '/api/OMData/';
var BuyersFactory = {};
buyersService.cache = $cacheFactory('cacheId');
BuyersFactory.GetBuyers = function () {
var buyersDataIsAvailable = $q.defer();
var dataList = buyersService.cache.get('BuyerData');
if (dataList != null && dataList.length > 0) {
buyersDataIsAvailable.resolve(dataList);
}
else {
$http.get(serviceBase + 'GetBuyers').then(
function (results) {
buyersService.cache.put("BuyerData", results.data);
buyersDataIsAvailable.resolve(results.data);
});
}
return buyersDataIsAvailable.promise;
}
app.factory('OMDataService', ['$http', '$q', '$cacheFactory', buyersService]);
});

Resources