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
});
I am trying to test a controller. The controller uses a service which is using $http to get the data from a json file (This json file is just a mock up of response returned from server)
My problem is that when I am testing the controller, it creates the controller object and even calls the service. But it doesnt call the $http mocked response. I not sure where I am going wrong. I tried looking at few examples but all of them are using $q.
My service looks like this:
(function(){
angular.module('mymodule')
.factory('MyService', MyService);
MyService.$inject = ['$http'];
function MyService($http) {
var service = {
retrieveData : retrieveData
};
return service;
function retrieveData(containerLabel){
var myGrossData = [];
var isMatchFound = false;
var myindex = containerLabel.slice(-4);
return $http.get('app/myGrossData.json').then(function(response) {
console.log('inside http retrieveData: ');
myGrossData = response.data;
var myindexExists = false;
var mydataObject = [];
var defaultdata = [];
angular.forEach(myGrossData, function (myGrossData) {
if (myindex === myGrossData.myindex) {
mydataObject = myGrossData;
isMatchFound = true;
}
if(!isMatchFound && myGrossData.myindex === '2006')
{
mydataObject = myGrossData;
}
if(myGrossData.myindex === '2006'){
defaultdata = myGrossData;
}
});
if (isMatchFound && response.status === 200)
{
return mydataObject;
}
else if(!isMatchFound && (response.status === 200 || response.status === 201)){
return defaultdata;
}
else //all other responses for success block
{
return 'Incorrect Response status: '+response.status;
}
},
function(error){
return 'Error Response: '+error.status;
}
);
}
};
})();
The controller calling it is :
(function () {
'use strict';
angular
.module('mymodule', [])
.controller('MyCtrl', MyCtrl);
MyCtrl.$inject = ['$scope', 'MyService'];
function MyCtrl($scope, MyService) {
var vm = this;
vm.datafromsomewhere = datafromsomewhere;
vm.displayData = [];
vm.disableBarCode = false;
vm.childCount = 0;
vm.headertext="Master Container Builder";
init();
function init() {
console.log('MyCtrl has been initialized!');
console.log(vm.headertext);
}
function myfunctionCalledByUI(input) {
processData(input);
}
function processData(containerLabel){
MyService.retrieveMasterContainer(containerLabel).then(function(data){
vm.displayData = data;
});
vm.disableBarCode = true;
vm.childCount = (vm.displayData.childData === undefined) ? 0: vm.displayData.childData.length;
vm.headertext="Myindex "+vm.displayData.myindex;
if ( vm.displayData.masterDataId.match(/[a-z]/i)) {
// Validation passed
vm.displayData.masterDataId ="No Shipping Label Assigned";
}
else
console.log('else: '+vm.displayData.masterDataId);
console.log('length of childData: '+vm.childCount);
}
}
})();
and finally my spec looks like this:
var expect = chai.expect;
describe('Test Controller', function () {
var rootScope, compile; MyService = {};
var $scope, $controller;
beforeEach(module('ui.router'));
beforeEach(function() {
module('mymodule');
inject(function ($rootScope, _$compile_,_$controller_) {
rootScope = $rootScope;
compile = _$compile_;
$scope = $rootScope.$new();
MyService = jasmine.createSpyObj('MyService', [
'retrieveData'
]);
$controller = _$controller_('MyCtrl', {
$scope: $scope
});
});
});
it('controller should be initialized and data should also be initialized', function() {
expect($controller).to.not.be.undefined;
expect($controller).to.not.be.null;
expect($controller.disableBarCode).to.equal(false);
expect($controller.childCount).to.equal(0);
expect($controller.headertext).to.equal("Master Container Builder");
});
it(' should process data when containerLabel is called into myfunction', function() {
$controller.handKeyed('12001');
expect(MyService.retrieveData).to.have.been.called;
expect($controller.processData).to.have.been.called;
expect($controller.disableBarCode).to.equal(true);
expect($controller.childCount).to.equal(0);
expect($controller.headertext).to.equal("Master Container Builder");
});
});
I am using following techstack if it helps:
angular 1.5
Ionic
Karma-jasmine
The code works when I run it. My issue is that when i run the test it doesnt populate the data in my vm.displayData variable. how do I make it get some data into the service. I added in some log statements and it skips it completely.
After all the test run including unrelated tests to this one, then I see the log statements from MyService. I am not sure how to approach this.
I think what you are looking for is the $httpBackend service. It will mock the request indicating the result. So, when your service hit the url, it will return what you passed to the $httpBackend configuration.
A simple example would be:
it('should list newest by category', function(){
$httpBackend
.expectGET(url)
.respond(techPosts /*YOUR MOCKED DATA*/);
$stateParams.category = 'tech';
var controller = $controller('HomeCtrl', { PostsResource: PostsResource, $stateParams: $stateParams });
controller.listNewestPosts();
$httpBackend.flush();
expect(controller.posts).toEqual(techPosts.posts);
});
My service populates all items using $http and a cache when the controller is activated. If I create a new item, by doing an $http.post(), what is the best way to refresh the cache?
The problem with the example below is the cached getAll call will return an outdated array:
(function () {
'use strict';
angular
.module('myapp')
.factory('items', itemsService)
.controller('Items', itemsController);
// myCache was created using angular-cache
itemsService.$inject = ['$http', 'myCache'];
function itemsService ($http, myCache) {
var service = {
getAll : function () {
return $http.get('/api/item', myCache);
},
createNew : function (item) {
return $http.post('/api/item', item);
}
};
return service;
}
itemsController.$inject = ['items'];
function itemsController (items) {
var vm = this;
vm.items = [];
vm.item = {};
activate();
function activate() {
items.getAll().then(function(response){
vm.items = response.data || [];
});
}
function createNew() {
items.createNew(vm.item).then(function(response){
vm.items.push(response.data);
});
}
}
})();
Edit #1 : Invalidating Cache
I've modified the code to invalidate the cache when a new item is created. The addition of the $q service, and manually rejecting or resolveing the calls seems very tedious and bloated.
Is there a better way?
(function () {
'use strict';
angular
.module('myapp')
.factory('items', itemsService)
.controller('Items', itemsController);
itemsService.$inject = ['$q', '$http', 'CacheFactory'];
function itemsService ($q, $http, CacheFactory) {
var _cache = CacheFactory.get('items') || CacheFactory('items');
var service = {
getItems : function(refresh) {
var d = $q.defer();
if (refresh) { _cache.invalidate(); }
$http.get('/api/item', _cache).then(function(response){
d.resolve(response.data);
}, function(err){ d.reject(err); });
return d.promise;
},
createNew : function(info){
var d = $q.defer();
$http.post('/api/item', info).then(function(response){
_cache.invalidate();
d.resolve(response.data);
}, function(err){ d.reject(err); });
return d.promise;
}
};
return service;
}
itemsController.$inject = ['items'];
function itemsController (items) {
var vm = this;
vm.items = [];
vm.item = {};
activate();
function activate() {
items.getAll().then(function(response){
vm.items = response.data || [];
});
}
function createNew() {
items.createNew(vm.item).then(function(response){
vm.items.push(response.data);
});
}
}
})();
Controller 1
var promise = UserService.userexists(groupid);
promise.then(
},
function (response) {
}
);
Controller 2
var promise = UserService.userexists(groupid);
promise.then(
},
function (response) {
}
);
Service
app.factory("UserService", function ($q, $timeout) {
return {
userexists: function (groupid) {
var deferred = $q.defer();
//this is just to keep a pointer to parent scope from within promise scope.
IsCurrentUserMemberOfGroup(groupid, function (isCurrentUser) {
if (isCurrentUser) {
deferred.resolve(isCurrentUser);
}
else {
deferred.reject(isCurrentUser);
}
});
return deferred.promise;
}
}
});
function IsCurrentUserMemberOfGroup(groupId, OnComplete) {
var currentContext = new SP.ClientContext.get_current();
var currentWeb = currentContext.get_web();
var currentUser = currentContext.get_web().get_currentUser();
currentContext.load(currentUser);
var allGroups = currentWeb.get_siteGroups();
currentContext.load(allGroups);
var group = allGroups.getById(groupId);
currentContext.load(group);
var groupUsers = group.get_users();
currentContext.load(groupUsers);
currentContext.executeQueryAsync(OnSuccess, OnFailure);
function OnSuccess(sender, args) {
var userInGroup = false;
var groupUserEnumerator = groupUsers.getEnumerator();
while (groupUserEnumerator.moveNext()) {
var groupUser = groupUserEnumerator.get_current();
if (groupUser.get_id() == currentUser.get_id()) {
userInGroup = true;
break;
}
}
OnComplete(userInGroup);
}
function OnFailure(sender, args) {
OnComplete(false);
}
}
I have controller 1 and controller 2 on the same page, both use the same service to check the user exist in a specific group. Problem is the service runs the check user method (IsCurrentUserMemberOfGroup) for each controller. Is there a way so that I execute the method once and other controllers can use it?
This is a sharepoint 2010 environment.
Thanks
You could cache the result in your service, maybe something like
app.factory("UserService", function ($q, $timeout) {
return {
var cache = {};
userexists: function (groupid) {
var deferred = $q.defer();
if (cache[groupif]) {
deferred.resolve(cache[groupif]);
return deferred.promise;
}
IsCurrentUserMemberOfGroup(groupid, function (isCurrentUser) {
if (isCurrentUser) {
cache[groupif] = isCurrentUser;
deferred.resolve(isCurrentUser);
}
else {
deferred.reject(isCurrentUser);
}
});
return deferred.promise;
}
}
});
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]);
});