I'm having trouble properly injecting a bunch of modules in a project. This is the project I’m following
https://thinkster.io/django-angularjs-tutorial
When i try to make a new post - angular throws the below error
Error: Authentication.getAuthenticatedAccount(...) is undefined
submit#http://localhost:8000/static/javascripts/posts/controllers/new-post.controller.js:31:21
$parseFunctionCall#http://localhost:8000/static/bower_components/angular/angular.js:12474:15
This is where the problem seems to occur
function NewPostController($rootScope, $scope, Authentication, Snackbar, Posts) {
var vm = this;
vm.submit = submit;
function submit() {
$rootScope.$broadcast('post.created', {
content: vm.content,
author: {
username: Authentication.getAuthenticatedAccount().username
}
});
$scope.closeThisDialog();
Posts.create(vm.content).then(createPostSuccessFn, createPostErrorFn);
function createPostSuccessFn(data, status, headers, config) {
Snackbar.show('Success! Post created.');
}
function createPostErrorFn(data, status, headers, config) {
$rootScope.$broadcast('post.created.error');
Snackbar.error(data.error);
}
}
}
But I can see that the correct module is being used in the code.
this is my new-post.controller.js file where i've injected the Authentication dependency
angular
.module('thinkster.posts.controllers')
.controller('NewPostController', NewPostController);
NewPostController.$inject = ['$rootScope', '$scope', 'Authentication', 'Snackbar', 'Posts'];
this is a snippet my posts.module.js file
angular
.module('thinkster.posts', [
'thinkster.posts.controllers',
'thinkster.posts.directives',
'thinkster.posts.services'
]);
angular
.module('thinkster.posts.controllers', []);
this is a snippet of the authentication service module
angular
.module('thinkster.authentication.services')
.factory('Authentication',Authentication);
Authentication.$inject = ['$cookies','$http'];
function Authentication($cookies,$http){
var Authentication = {
getAuthenticatedAccount: getAuthenticatedAccount,
isAuthenticated: isAuthenticated,
register:register,
login : login,
logout:logout,
setAuthenticatedAccount: setAuthenticatedAccount,
unAuthenticate: unAuthenticate
};
return Authentication;
function getAuthenticatedAccount(){
if (!$cookies.authenticatedAccount){
return;
}
return JSON.parse($cookies.authenticatedAccount);
}
And a snippet of the authentication module
angular
.module('thinkster.authentication',[
'thinkster.authentication.controllers',
'thinkster.authentication.services'
]);
-finally, the below thinkster module
angular
.module('thinkster', [
'thinkster.config',
'thinkster.routes',
'thinkster.authentication',
'thinkster.layout',
'thinkster.posts',
'thinkster.utils'
]);
the authentication service works fine since I’m able to login and logout of the project. Am i looking in the wrong place for the error?
The code snippets are missing the NewPostController definition. Without seeing that code, I would guess that the Authentication object may not be passed into the function.
function NewPostController($rootScope, $scope, Authentication, Snackbar, Posts) {
}
You can see what methods are available on your Authentication object with the following code in NewPostController:
for (var key in Authentication) {
console.log(typeof Authentication[key], key);
}
You should see "function getAuthenticatedAccount" if it's available on the object.
Related
I'm trying to write a test suite for an Angular app that uses Keycloak for authentication.
However, as Keycloak requires you to manually bootstrap Angular and set up a few interceptors, I'm unable to fire any test due to the following error:
Error: $injector:unpr
Unknown Provider: AuthProvider <- Auth <- authInterceptor <- $http <- $templateRequest <- $route
This is the code for the interceptor that raises the error:
angular.module('MPMReportGenerator')
.factory('authInterceptor', function authInterceptor ($q, Auth, $log) {
return {
request: function (config) {
var deferred = $q.defer()
Auth.updateToken(5).success(function () {
config.headers = config.headers || {}
config.headers.Authorization = 'Bearer ' + Auth.token
deferred.resolve(config)
}).error(function () {
deferred.reject('Failed to refresh token')
})
$log.info(deferred.promise)
return deferred.promise
}
}
})
My thinking is that I should mock the interceptor and just have it return the request.
However, I fail to see how I could do that, since this interceptor is never injected anywhere as a dependency, it's simply declared with the block above and that's it. My understanding of mocked services is that they need to be injected somewhere to be mocked.
My implementation of Keycloak into Angular comes straight from their examples, if that helps.
Edit
I've been trying to inject a mocked Auth module into the service I'm writing a test for, but still no change.
I'm very new to unit testing in general, so I'm a bit lost trying to track this down. I feel like I know where the issue is, but not how to solve it (The Auth service is added during the bootstrap of the app, I need to mock it for things to work, but it seems I don't know how/where to mock it properly)
Here's the whole testing code:
describe('Services', function () {
'use strict'
beforeEach(module('MPMReportGenerator'))
module(function ($provide) {
$provide.factory('Auth', function () {
return null
})
})
var sectionService, $httpBackend, mockAuth
beforeEach(inject(function (_sectionService_, _$httpBackend_, Auth) {
sectionService = _sectionService_
$httpBackend = _$httpBackend_
mockAuth = Auth
}))
it('should get sections', function () {
$httpBackend.expect('GET', '/MPMReportGenerator/api/categories/all').respond(200)
sectionService.getSections()
expect($httpBackend.flush).not.toThrow()
})
})
Edit 2
I've managed to get past my initial error by making a mock version of Auth.
I am now facing issues implementing a mock version of Keycloak's Javascript library.
My current mock code is as follow:
beforeEach(module(function ($provide) {
$provide.factory('Auth', function ($q) {
return {
updateToken: function (minValidity) {
return {
success: function (fn) {
var deferred = $q.defer()
deferred.resolve('')
fn(deferred.promise)
},
error: function (fn) {
var deferred = $q.defer()
deferred.resolve('Error')
fn(deferred.promise)
}
}
},
token: 'thisisafaketokenfortesting'
}
})
}))
And throws this error:
Expected function not to throw, but it threw TypeError: undefined is not an object (near '...}).error(function () {...').
target/MPMReportGenerator-1.0.0/js/app.service.spec.js:42:43
loaded#http://localhost:9876/context.js:151:17
My actual test is this:
it('should get sections', function () {
$httpBackend.expect('GET', '/MPMReportGenerator/api/categories/all').respond(200)
sectionService.getSections()
expect($httpBackend.flush).not.toThrow()
})
I finally figured it out.
Here is the needed code if anyone wants to test an Angular app with keycloak:
beforeEach(
module(function ($provide) {
$provide.factory('Auth', function ($q) {
return {
updateToken: function (minValidity) {
return {
success: function () {
return {
error: function () {
var deferred = $q.defer()
return deferred.promise
}
}
}
}
},
token: 'thisisafaketokenfortesting'
}
})
}))
Note that you will likely need to mock other parts of the keycloak library if you intend to test the interceptors provided in the official examples.
Edit
Don't use the code above, the following works much better:
$provide.factory('Auth', function () {
return {
updateToken: function (minValidity) {
return {
success: function () {
return this
},
error: function () {
return this
}
}
},
token: 'thisisafaketokenfortesting'
}
})
I am building an app using Ionic Framework 7 AngularJS
$ ionic info
Your system information:
Cordova CLI: 6.2.0
Gulp version: CLI version 3.9.1
Gulp local:
Ionic Framework Version: 1.2.4
Ionic CLI Version: 1.7.16
Ionic App Lib Version: 0.7.3
OS: Distributor ID: LinuxMint Description: Linux Mint 17.1 Rebecca
Node Version: v0.12.2
The page I'm building retrieve a list of church services and displays them in a master detail list. So I put the $http code in a Service as per the following code:
services.js :
.service('ServicesService', ['$http', '$sce', function($http, $sce){
var services = [];
$http.get('http://demo0194057.mockable.io/services').then(function(response) {
services = response.data;
window.q = services; //checking value of services var
}, function(error) {
console.error("Error loading Services Endpoint! " + error);
});
return {
getAllServices: function() {
console.log('from inside return obj ' + services);
window.p = services; // checking value of services var
return services;
}
}
}])
controller.js :
.controller('servicesCtrl', function($scope, $http, ServicesService, InfoService) {
$scope.services = [];
$scope.services = ServicesService.getAllServices();
window.r = $scope.services;
$scope.doRefresh = function() {
$http.get('http://demo0194057.mockable.io/services').success(function(response) {
$scope.services = response;
})
.finally(function() {
// stop the ion-refresher from spinning
$scope.$broadcast('scroll.refreshComplete');
});
}
})
So my problem is that the service is returning an empty array instead of an array of objects from the JSON REST API. I put some debugging variables in the code and I can see that from the controller, the service is returning an empty array (var window.r). From within the service, window.p is also empty, however window.q has the correct object data which means that the API call is working fine. I can't figure out where that data is getting lost though, such that it's not being successfully returned from the service.
Please help
try it like this:
Service
.service('ServicesService', ['$http', '$sce', function($http, $sce){
var services = [];
return {
getAllServices: function() {
return $http.get('http://demo0194057.mockable.io/services').then(function(response) {
services = response.data;
return services;
}, function(error) {
console.error("Error loading Services Endpoint! " + error);
});
}
}
}]);
Controller
.controller('servicesCtrl', function($scope, $http, ServicesService, InfoService) {
$scope.services = [];
$scope.doRefresh = function() {
ServicesService.getAllServices().then(function (response) {
$scope.services = response;
});
});
});
In your service you need to do the $http.get request inside the body of getAllServices method. Otherwise, the get request's then clause gets executed after the call to getAllServices is made by the controller which is why the services variable isn't initialized at that point.
$http.get('http://demo0194057.mockable.io/services').then(function(response) {
services = response.data;
window.q = services; //checking value of services var
}, function(error) {
console.error("Error loading Services Endpoint! " + error);
});
This is async function. So services = response.data will be executed somtimes later.
$scope.services = ServicesService.getAllServices(); This will be call just after angular will rise and probably before your async get wil be executed.
So. If you wont to get your services you have to wrap your ServicesService.getAllServices(); as a promise where you will wait your first request. But it seems there are some architecture problems... Try to push your code away for a while and redisign it.
Call function from another Controller Angular Js
Call function from another controller Angularjs
http://tutorials.jenkov.com/angularjs/dependency-injection.html
I've been googling around and trying to adapt but I can't figure out.
I have two files: home.js and auth.js. I created the file auth.js because I will need to call the functions inside of it in lot of files.
I can't create simple functions like:
function isAuthed(){
return true;
}
Because I'll need to use some AngularJS modules, such as HTTP, therefore I believe I need to create a decent angular js file.
For what I've read so far, in order to call a function from another controller the best and correct way is to use Services.
home.js
var app = angular.module('home', [
'ngRoute'
]);
app.controller('home', ['$scope', 'authSERVICE', function($scope, authSERVICE){
console.log(authSERVICE.hasCredentials());
}]);
auth.js
var app = angular.module('auth', []);
app.service('authSERVICE', ['$http', function($http){
this.hasCredentials = function(){
if(window.localStorage.getItem('email') != null && window.localStorage.getItem('password') != null){
return true;
}
}
this.login = function(email, password){
// Base64 function is from a simple JS file.
var auth = Base64.encode('username:password');
$http.get('http://localhost/project/api/webservice/getCategories', {
headers: { 'Authorization': 'Basic ' + auth },
params : {
email: email,
password: password,
}
}).success(function(response){
return true;
}).error(function(response){
return false;
});
}
}]);
This does not work and I receive an error on console poiting to: https://docs.angularjs.org/error/$injector/unpr?p0=authSERVICEProvider%20%3C-%20authSERVICE
I kind of understand what's the error information telling me. Although, I still need and want to have two different files.
EDIT: Btw, in the index.html file I've called the scripts:
<script type="text/javascript" src="application/controllers/home.js"></script>
<script type="text/javascript" src="application/controllers/auth.js"></script>
Thanks to #Ved answer I manage to figure it out. Solved.
var app = angular.module('home', [
'ngRoute',
'auth'
]);
I have been trying, without success, to inject the Angular JS ngCookie module and $cookies service into an Angular Provider. Can someone please explain the lac of success?
When I inject ngCookies into the Provider
angular.module('loginService', [])
.provider('loginService', function () {
var userToken = localStorage.getItem('userToken'),
errorState = 'app.error',
logoutState = 'app.home';
this.$get = function ($rootScope, $http, $q, $state) {
/**
* Low-level, private functions.
*/
var setHeaders = function (token) {
if (!token) {
delete $http.defaults.headers.common['X-Token'];
return;
}
$http.defaults.headers.common['X-Token'] = token.toString();
};
...
});
like so:
angular.module('loginService', ['ngCookies'])
.provider('loginService', ['$cookies', function ($cookies) {
var userToken = localStorage.getItem('userToken'),
errorState = 'app.error',
logoutState = 'app.home';
I get an error:
Unknown provider: $cookies
I suspect that I can't inject Services into Providers? My issue is that I am calling a web service which is returning a session id as a Set-Cookie header. I'd like to grab the ss-id but can't seem to retrieve it from the response header so thought I'd get it from the cookie instead.
I'm open to better alternatives though...
You can only inject other providers into a provider. It is only possible to inject a service in the public functions or config block of the provider.
I'm trying to test a simple service but I'm getting an Unkown Provider error. Here is the service definition:
var sessionManager = angular.module('MyApp.SessionManager', ['ngResource']);
sessionManager.factory('UserService', function($resource) {
var UserService = $resource('/api/users/:key', {}, {
getNewUUID: {
method: 'GET',
params: {
action: 'getNewUserUUID'
}
}
});
return UserService;
});
and here is the test:
describe('Testing SessionManager', function() {
var userService;
beforeEach(function() {
module('MyApp.SessionManager');
inject(function($injector) {
userService = $injector.get('UserService');
});
});
it('should contain a UserService', function() {
expect(userService).toBeDefined();
});
});
I can't seem to see the problem, I know that the UserService javascript file is being called because I can get a console log at the top of the file, however if I put it in the service definition I don't see it get called. So for some reason it's like Angular is not instantiating my service?
I realized that the problem was my module MyApp.SessionManager was being replaced because I thought you could declare dependencies every time it was reopened to add a module. The code above is fine if of course the service is actually surviving up until the tests.