Angularjs controller fails to load http data from cachefactory service - angularjs

Newbie to angular, using service I'm trying to load cachefactory and using the cache from controller.
Here is service methods
codesApp.service('CodeFilterService',function($filter, $http, CacheService,$log){
this.getByCodeType = function (codeTypeValue, codeName) {
if (angular.isUndefined(CacheService.get('Codes'))) {
loadCodes();
}
return $filter('filter')(CacheService.get('Codes'), {codetype: codeTypeValue, name: '!' + codeName});
};
this.getAllCodes = function () {
$log.info("getAllCodes");
if (angular.isUndefined(CacheService.get('Codes'))) {
$log.info("Loading fresh getAllCodes");
this.loadCodes();
}
return CacheService.get('Codes');
};
this.loadCodes = function () {
$http.get("./rest/codes").then(function (response) {
$log.info("Codes Size" +response.data.length );
CacheService.put('Codes', response.data);
});
};
});
codesApp.factory('CacheService',function($cacheFactory){
return $cacheFactory('super-cache');
});
Controller Code :
codesApp.controller('CodeCreateController',function($scope,$state,
Code,CodeFilterService,$log){
...
$scope.codeDropdownList = CodeFilterService.getAllCodes();
$log.info("codeDropdownList = " + angular.isUndefined($scope.codeDropdownList)) ;
});
Here angular.isUndefined($scope.codeDropdownList)) returns true which means data not getting loaded. Is there any way to resolve this issue.

You may use promise inject $q
codesApp.service('CodeFilterService', function ($filter, $http, CacheService, $log, $q) {
this.getByCodeType = function (codeTypeValue, codeName) {
if (angular.isUndefined(CacheService.get('Codes'))) {
loadCodes();
}
return $filter('filter')(CacheService.get('Codes'), {
codetype: codeTypeValue,
name: '!' + codeName
});
};
this.getAllCodes = function () {
$log.info("getAllCodes");
var deferred = $q.defer();
if (angular.isUndefined(CacheService.get('Codes'))) {
$log.info("Loading fresh getAllCodes");
this.loadCodes().then(function (result) {
$log.error('getAllCodes success');
var cachedCodes = CacheService.get('Codes');
deferred.resolve(cachedCodes);
}, function (reason) {
$log.error('getAllCodes error: ' + reason);
deferred.reject();
});
} else {
var cachedCodes = CacheService.get('Codes');
deferred.resolve(cachedCodes);
}
return deferred.promise;
};
this.loadCodes = function () {
$log.info("loadCodes");
var deferred = $q.defer();
$http.get("./rest/codes").then(function (response) {
$log.error('loadCodes success');
$log.info("Codes Size" + response.data.length);
CacheService.put('Codes', response.data);
deferred.resolve();
}, function (reason) {
$log.error('loadCodes error: ' + reason);
deferred.reject();
});
return deferred.promise;
};
});
codesApp.controller('CodeCreateController', function ($scope, $state,
Code, CodeFilterService, $log) {
...
CodeFilterService.getAllCodes().then(function (result) {
$log.info('result: ' + result);
$scope.codeDropdownList = result;
$log.info("codeDropdownList = " + angular.isUndefined($scope.codeDropdownList))
}, function (reason) {
$log.error('error: ' + reason);
});
});

Related

unit test data arrived from service to controller (Jasmine)

Here is part of my code:
angular.module('mine',[]).factory('MyFactory', ['$http','$q', function
MyFactory($http,$q) {
return {
getData: function() {
var deferred = $q.defer(),
url = "http://...";
$http.jsonp(url)
.then(
function (response) {
deferred.resolve(response.data);
},
function (error) {
return $q.reject('Error retrieving data');
}
);
return deferred.promise;
}
};
}]);
function MyController(MyFactory) {
var self = this;
self.getData= function( ) {
MyFactory.getData().then(
function(result) {
self.contacts = result;
},
function(error) {
console.log('Error retrieving data: ', error);
}
);
};
self.getData();
}
angular.module('mine').component('myComponent', {
templateUrl: '..',
controller: MyController
});
I am trying to unit test if data from factory go correctly to controller. Here is my unit testing code using Jasmine:
describe('component', () => {
let $componentController,contactsList,ctrl,$q,$rootScope;
beforeEach(angular.mock.module('mine'));
beforeEach(inject((_$componentController_,_MyFactory_, _$q_, _$rootScope_) => {
$componentController = _$componentController_;
ctrl = $componentController('myComponent',null);
$q = _$q_;
contactsList = _MyFactory_;
$rootScope = _$rootScope_;
}));
it('should ... ', function() {
spyOn(contactsList, "getData").and.returnValue(
$q.when({
message: 'awesome message'
}));
ctrl.getData();
$rootScope.$apply();
expect(ctrl.contacts.message).toBe('awesome message');
});
});
For some reason, the above test is not running; I am getting the following error: Possibly unhandled rejection: Error retrieving data thrown. Do you have any idea why? What is wrong?
You should inject MyFactory to $componentController as second argument in your tests. So instead of this:
ctrl = $componentController('myComponent',null);
use this:
ctrl = $componentController('myComponent', {
MyFactory: _MyFactory_
});
angular.module('mine', []).factory('MyFactory', ['$http', '$q', function
MyFactory($http, $q) {
return {
getData: function() {
var deferred = $q.defer(),
url = "http://google.com";
$http.jsonp(url)
.then(
function(response) {
deferred.resolve(response.data);
},
function(error) {
return $q.reject('Error retrieving data');
}
);
return deferred.promise;
}
};
}
]);
function MyController(MyFactory) {
this.getData = function() {
MyFactory.getData().then(
function(result) {
this.contacts = result;
}.bind(this),
function(error) {
console.log('Error retrieving data: ', error);
}
);
};
this.getData();
}
angular.module('mine').component('myComponent', {
controller: MyController
});
describe('component', () => {
let $componentController, contactsList, $q, $rootScope, ctrlFactory;
beforeEach(angular.mock.module('mine'));
beforeEach(inject((_$componentController_, _MyFactory_, _$q_, _$rootScope_) => {
$componentController = _$componentController_;
ctrlFactory = () => {
return $componentController('myComponent', null, {
MyFactory: _MyFactory_
});
}
$q = _$q_;
contactsList = _MyFactory_;
$rootScope = _$rootScope_;
}));
it('should ... ', function() {
const message = 'awesome message'
spyOn(contactsList, "getData").and.returnValue(
$q.when({
message
}));
const ctrl = ctrlFactory()
ctrl.getData();
$rootScope.$apply();
expect(ctrl.contacts.message).toBe(message);
});
});
<link href="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.11/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.11/angular-mocks.js"></script>

Is there a way to get the real data for karma unit tests with services?

I would like to test an angular service - e.g.:
'use strict';
angular
.module('com.attributes.services', [])
.service('krAdminAttributeService', [
'$rootScope',
'$http',
function ($rootScope, $http) {
var modelData = {
"type": "",
"groupId": "",
"unit": "",
"description": "",
"name": {}
};
var service = {
mapAttributes: function (results, update) {
if (!update) {
modelData.identifier = results.identifier;
}
modelData.type = results.type;
modelData.groupId = results.groupselect;
if (results.unit !== undefined) {
modelData.unit = results.unit;
}
modelData.description = results.description;
//Name
modelData.name = {
i18n: true,
key: "klapi.attribute:" + results.identifier + ".name"
};
modelData = angular.copy(modelData);
},
get: function (params) {
//return the promise directly.
return $http.get($rootScope.globals.API + 'Attributes', {params: params}).then(function (result) {
return result.data;
});
},
getId: function (id) {
//return the promise directly.
return $http.get($rootScope.globals.API + 'Attributes/' + id + "?filter[include]=group").then(function (result) {
return result.data;
});
},
update: function (results) {
this.mapAttributes(results, true);
return $http.put($rootScope.globals.API + "Attributes/" + results.id, JSON.stringify(modelData), $rootScope.globals.CONFIG).then(function (result) {
return result;
});
},
updateName: function (params) {
return $http({
method: 'POST',
url: $rootScope.globals.I18NAPI + "change/de/klapi.attribute",
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
transformRequest: function (obj) {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
},
data: params
}).then(function (result) {
return result;
});
},
add: function (results) {
this.mapAttributes(results, false);
return $http.post($rootScope.globals.API + "Attributes", JSON.stringify(modelData), $rootScope.globals.CONFIG).then(function (result) {
return result;
});
},
delete: function (id) {
// post(url, data, [config]);
return $http.delete($rootScope.globals.API + "Attributes/" + id, $rootScope.globals.CONFIG).then(function (result) {
return result;
});
}
};
return service;
}]);
My test looks like this:
describe("krAdminAttributeService", function () {
var krAdminAttributeService,
$q,
rootScope,
httpBackend;
beforeEach(function () {
module('app');
inject(function($httpBackend, _krAdminAttributeService_, _$q_, $rootScope) {
krAdminAttributeService = _krAdminAttributeService_;
httpBackend = $httpBackend;
rootScope = $rootScope.$new();
$q = _$q_;
});
});
// check to see if it has the expected function
it('should have a get function', function () {
expect(angular.isFunction(krAdminAttributeService.get)).toBe(true);
});
it('should have a getId function', function () {
expect(angular.isFunction(krAdminAttributeService.getId)).toBe(true);
});
it('should have an update function', function () {
expect(angular.isFunction(krAdminAttributeService.update)).toBe(true);
});
it('should get the attributes', function () {
var data;
var response = {id:"12345"};
httpBackend.whenGET('http://localhost:3000/api/Attributes').respond(200,response);
// set up a deferred
var deferred = $q.defer();
// get promise reference
var promise = deferred.promise;
// set up promise resolve callback
promise.then(function (response) {
console.log('data',response);
data = response;
});
krAdminAttributeService.get().then(function(response) {
// resolve our deferred with the response when it returns
deferred.resolve(response);
console.log('response', response);
});
httpBackend.flush();
// make your actual test
//expect(data).toEqual(200);
});
});
The problem is, that this unit test is not retrieving the actual data but just some mocked data (in this case {id:'12345'}). For me, that makes the test less useful. Is there a way to test with real data? (CORS available)

While returning promise it gives me an unknown provider error

This the factory.
latModule.factory('latSvc',
[
"$http", "$scope", "$q",
function($http, $scope, $q) {
console.log("Enter latUserReportDateSvc");
return {
getPromiseForUserReportDate: function () {
$scope.userId = "bpx3364";
var deferred = $q.defer();
$http.get('/api/UserReportStatusApi', { 'userId': $scope.userId }).then(function(reponse) {
deferred.resolve(reponse);
},
function(error) {
deferred.reject(error);
});
return deferred.promise;
},
getPromiseForLocation: function () {
$scope.userId = "bpx3364";
var deferred = $q.defer();
$http.get('api/UserAccountApi/', { 'userId': $scope.userId }).then(function (reponse) {
deferred.resolve(reponse);
},
function (error) {
deferred.reject(error);
});
return deferred.promise;
},
getPromiseForErrorSummary: function (userInfoVm) {
console.log("latErrorSummarySvc getErrorCounts, userInfo: ", userInfoVm);
$scope.userId = "bpx3364";
$scope.serviceTypeCode = 4;
var deferred = $q.defer();
$http.get('/api/UserReportStatusApi', { 'userId': $scope.userId, 'serviceTypeCode': $scope.serviceTypeCode }).then(function (reponse) {
deferred.resolve(reponse);
},
function (error) {
deferred.reject(error);
});
return deferred.promise;
}
};
}
]);
This is the controller
latModule.controller("dashboardController",
["$scope","latSvc",
function ($scope,latSvc) {
console.log("enter dashboard controller");
console.log("scope: ", $scope);
console.log("homeUserInfo: ", $scope.homeLatUserInfo);
var dashboardUserReportDate = function() {
latSvc.getUserReportDateInfo().then(
function(response) {
$scope.dashboardUserReportDateData = response;
}, function(error) {}
);
};
dashboardUserReportDate();
var dashboardErrorCounts = function() {
latSvc.getPromiseForErrorSummary($scope.homeLatUserInfo).then(
function(response) {
$scope.dashboardErrorCountsData = response;
},
function (error) { }
);
};
dashboardErrorCounts();
var dashboardAtmCount = function() {
latSvc.getPromiseForLocation().then(
function(response) {
$scope.dashboardAtmCountData = response;
}, function(error) {}
);
};
dashboardAtmCount();
}]);
after running this code I am getting an unknown provider error while I am trying to implement this promise concept.Because while I was calling through service with out resolving promise and without using then the url was getting hit multiple times.
You can't use / inject $scope into a service.
But as far as I understand your code you should be fine using local variables for your http request query parameters.
And Vineet is right - you should return the $http.get() directly (it's already a promise).

Configure angular $http with a value obtained from server

I'm building my (first) angular app that will have tokens inserted into headers (the content shown is for the most part taken from here)
angular.module('myApp')
.factory('sessionInjector', ['SessionService', function(SessionService) {
var sessionInjector = {
request: function(config) {
config.headers['x-session-token'] = SessionService.getToken();
return config;
}
};
return sessionInjector;
}])
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('sessionInjector');
}])
The trouble I'm having is with SessionService - how can I initialize this with call to the server?
For example, this didn't work:
.factory('SessionService', ['$injector', function($injector){
var token = "";
return {
getToken: function () {
var http = $injector.get('$http');
if (token === "") {
http.get('http://localhost/api/auth/getToken').success(function (ret) {
token = ret;
});
}
return token;
//I can see a $q/deferred/promise should be used somehow here...
//but I'm not sure it solves the problem I'm having...
}
}
}]);
because it just overloads my cpu to 100%...
Since it's my first angular app, I'm sure I'm missing something, but... what?
EDIT:
Another take on the matter... still doesn't work though... (again, uses up cpu, probably infinite loop)
.factory('sessionData', function () {
var currentToken = '[uninitialized-token]';
return {
getToken: function () {
return currentToken;
},
setToken: function (token) {
currentToken = token;
}
}
})
.factory('sessionInjector', ['sessionData', '$injector', '$q', function (sessionData, $injector, $q) {
var sessionInjector = {
request: function (config) {
var deferred = $q.defer();
var http = $injector.get('$http');
http.get('http://localhost/api/auth/getToken').success(function (ret) {
sessionData.setToken(ret);
console.log("successfully authenticated with token " + sessionData.getToken());
config.headers['x-header-sessionID'] = sessionData.getToken();
deferred.resolve(config);
})
.error(function(){
console.log("failed to authenticate");
deferred.resolve(config);
});
return deferred.promise;
}
};
return sessionInjector;
}])
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push('sessionInjector');
}])
.run(['$http', 'sessionData', function ($http, configs, sessionData) {
$http.get('http://localhost/api/auth/testMethod').then(function (ret) {
//do something...
});
}])
Check whether this modified code fragment will solve your issues.
.factory('SessionService', ['$http', '$q', function($http, $q) {
var token = null;
var sessionService = {};
var differred = $q.defer();
sessionService.readToken = function() {
return $http.get('http://localhost/api/auth/getToken')
.success(function (res) {
console.log('Auth Success and token received: ' + JSON.stringify(res.data));
// Extract the token details from the received JSON object
token = res.data;
differred.resolve(res);
}, function (res) {
console.log('Error occurred : ' + JSON.stringify(res));
differred.reject(res);
}
)
};
sessionService.getToken = function() {
return token;
};
sessionService.isAnonymous = function() {
if (token)
return true;
else
return false;
};
return sessionService;
}])
.factory('sessionInjector', ['SessionService', function(SessionService) {
var sessionInjector = {
request: function(config) {
if (!sessionService.isAnonymous) {
config.headers['x-session-token'] = SessionService.getToken();
return config;
}
}
};
return sessionInjector;
}])
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('sessionInjector');
}])
Answer was actually pretty straight forward - if the targeted URL is for login, then don't inject anything (look for the comment the fix):
.factory('sessionData', function () {
var currentToken = '[uninitialized-token]';
return {
getToken: function () {
return currentToken;
},
setToken: function (token) {
currentToken = token;
}
}
})
.factory('sessionInjector', ['sessionData', '$injector', '$q', function (sessionData, $injector, $q) {
var sessionInjector = {
request: function (config) {
//The fix:
if(config.url === 'http://localhost/api/auth/getToken')
return config;
var deferred = $q.defer();
var http = $injector.get('$http');
http.get('http://localhost/api/auth/getToken').success(function (ret) {
sessionData.setToken(ret);
console.log("successfully authenticated with token " + sessionData.getToken());
config.headers['x-header-sessionID'] = sessionData.getToken();
deferred.resolve(config);
})
.error(function(){
console.log("failed to authenticate");
deferred.resolve(config);
});
return deferred.promise;
}
};
return sessionInjector;
}])
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push('sessionInjector');
}])
.run(['$http', 'sessionData', function ($http, configs, sessionData) {
$http.get('http://localhost/api/auth/testMethod').then(function (ret) {
//do something...
});
}])

$scope is not updated after database trigger

I have following controller which is posting a new user and also getting new users.
The problem here is after adding a new user, the scope is not updated so view is not affected. I have also tired returning the function so it expects a promise but didnt update the scope.
myapp.controllers('users', ['usersService', ''$scope',', function(usersService, $scope){
getUsers();
function getUsers(params) {
if (typeof(params) === "undefined") {
params = {page: 1};
}
usersService.getUsers(params).then(function (res) {
$scope.users = res.items;
$scope.usersListTotalItems = res._meta.totalCount;
$scope.usersListCurrentPage = res._meta.currentPage + 1;
});
}
}
$scope.addUser = function (user) {
usersService.adddNewUser(user).then(function (response) {
getUsers();
});
}
}]);
myApp.factory('userService', ['Restangular', '$http', function (Restangular, $http) {
return {
getUsers: function (params) {
var resource = 'users/';
var users = Restangular.all(resource);
return users.getList(params)
.then(function (response) {
return {
items : response.data[0].items,
_meta : response.data[0]._meta
}
});
},
adddNewUser: function (items) {
var resource = Restangular.all('users');
var data_encoded = $.param(items);
return resource.post(data_encoded, {}, {'Content-Type': 'application/x-www-form-urlencoded'}).
then(function (response) {
return response;
},
function (response) {
response.err = true;
return response;
});
}
};
}]);
I think it is a small error however you did not include $scope in the argument for the controller function.
myapp.controllers('users', ['usersService','$scope', function(usersService $scope){
getUsers();
function getUsers(params) {
if (typeof(params) === "undefined") {
params = {page: 1};
}
usersService.getUsers(params).then(function (res) {
$scope.users = res.items;
$scope.usersListTotalItems = res._meta.totalCount;
$scope.usersListCurrentPage = res._meta.currentPage + 1;
});
}
}
$scope.addUser = function (user) {
usersService.adddNewUser(user).then(function (response) {
getUsers();
});
}
}]);

Resources