There is something I don't fully understand with AngularJS. If I declare the controllers this way:
angular.module('ngApp', []).controller('AboutCtrl', ['$scope', function ($scope) {
$scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma'];
}]);
angular.module('ngApp', []).controller('ContactCtrl', ['$scope', function ($scope) {
$scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma'];
}]);
angular.module('ngApp', []).controller('MainCtrl', ['$scope', function ($scope) {
$scope.awesomeThings = ['HTML5 Boilerplate', 'AngularJS', 'Karma'];
}]);
...whenever I try to test them only one of them pass the test case(s):
describe('MainCtrl', function () {
var scope;
beforeEach(module('ngApp'));
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
$controller('MainCtrl', { $scope: scope });
}));
it('should attach a list of `awesomeThings` to the scope', function () {
expect(scope.awesomeThings.length).toBe(3);
});
});
(I have also three files with the same test case repeated, the only thing that changes is the controller name: AboutCtrl and ContactCtrl in the apprpriate locations).
On the other hand, if I say: var app = angular.module('ngApp', []); and then "attach" the controllers to it, everything works fine. The same if I attached them to the same chain in one angular.module('ngApp', []).controller(/* ... */).controller(/* ... */);
The reason I want them separate is because I want to have them in (three...N) different files.
Anyone has any clue?
You are re-declaring your ngApp module each time, rather than getting the already declared module.
From John Papa's Angular Style Guide:
•Only set once and get for all other instances.
Why?: A module should only be created once, then retrieved from that point and after.
/* recommended */
// to set a module
angular.module('app', []);
// to get a module
angular.module('app');
By calling angular.module('ngApp', []) you are creating a new module, not reusing the same one. In order to do that just call .module with the app name and no other argument
angular.module('ngApp')
file1.js
angular.module('ngApp').controller(...);
file2.js
angular.module('ngApp').controller(...);
you could also just save the module to a global variable and just use that variable
main.js
var app = angular.module('ngApp',[]);
file1.js
app.controller(...);
file2.js
app.controller(...);
Related
I have some javascript and html files: User.js, index.html, Door.js
I want use any function in User.js file.
My user.js file
My door.js file
I call getUserInfo in user.Js from Door.js file in function
doorApplicationLoginPage.service
Error: [$injector:unpr] Unknown provider: UserServiceProvider <- UserService <- PerioMainController
var doorApplicationLoginPage = angular.module("PerioLoginPage", []);
doorApplicationLoginPage.service('UserService', function () {
this.getUserInfo = function () {
alert("getUserInfo");
}
this.reLoginUser = function () {
alert("reLoginUser");
}
});
var doorApplication = angular.module("PerioDoorApplication", []);
doorApplication.controller("PerioMainController", function ($scope, $http, $sce, UserService) {
UserService.getUserInfo();
});
Thank you.
You are injecting a service which is not referenced to your module.
See:
The UserService service is referenced in PerioLoginPage module
The PerioMainController controller is referenced in PerioDoorApplication module.
You've got either to:
reference the service in the same module as your controller.
inject the module where service is referenced to the module where controller is referenced.
In this case I can see you have two modules Periodicloginpage and periodicdoorapplication. So two services are defined in two modules. So you have to put the Periodicloginpage as dependency of periodicdoorapplication.
var doorApplication = angular.module("PerioDoorApplication", ["PerioLoginPage"]);
As I said in my comments you have two things to do at least:
You need to return the functions you want to use from the service (UserService):
return{
getUserInfo: getUserInfo,
reLoginUser: reLoginUser
};
Your module needs to reference the one the service is defined on:
angular.module('PerioDoorApplication', ['PerioLoginPage']);
And in your 'PerioMainController', a better way to write it would be:
doorApplication.controller('PerioMainController', ['$scope', '$http', '$sce', 'UserService',
function($scope, $http, $sce, UserService){
[...]
}
]);
When I have one controller attached to a module, I can use mocha, karma to test it successfully. But when I add two controllers to the same module, the tests fail. Why is that?
I have 2 controllers defined on the same module. I can manually test the controllers and they work.
src/app/itemListController.js
angular.module('lazyLoad', [])
.controller('ItemListController', ['$scope', function ($scope) {
...
}]);
src/app/invoiceController.js
angular.module('lazyLoad', [])
.controller('InvoiceController', ['$scope', function ($scope) {
...
}]);
And 2 unit-tests:
test/app/itemListController.mocha.js
'use strict';
describe('testing movies', function () {
var scope;
var fixture;
beforeEach(module('lazyLoad'));
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
fixture = $controller('ItemListController', {$scope: scope});
}));
it('....', function() {});
});
test/app/invoiceController.mocha.js
'use strict';
describe('testing movies', function () {
var scope;
var fixture;
beforeEach(module('lazyLoad'));
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
fixture = $controller('InvoiceController', {$scope: scope});
}));
it('....', function() {});
});
I get:
PhantomJS 1.9.8 (Mac OS X 0.0.0) testing movies "before each" hook: workFn FAILED
the object {
"line": 1761
"message": "[ng:areq] Argument 'ItemListController' is not a function, got undefined
http://errors.angularjs.org/1.4.1/ng/areq?p0=ItemListController&p1=not%20a%20function%2C%20got%20undefined"
"name": "Error"
Now if I change the module-name for the invoiceController.js and invoiceController.mocha.js to say invoiceM then both tests work.
I must be doing something wrong...
You define your modules twice. When you use brackets [] to pass empty dependencies, you actually create a module and replace an old one if exists with the same name. What you need to do is:
// Create the module, maybe in a separate place.
angular.module('lazyLoad', []);
// Attach controllers to that module:
angular.module('lazyLoad') // HEY! SEE THIS? NO BRACKETS.
.controller('ItemListController', ...);]
angular.module('lazyLoad') // HEY! SEE THIS? NO BRACKETS.
.controller('InvoiceController' ...);
I have a very simple controller that looks like this.
timeInOut.controller('timeInOutController', function($scope, $filter, $ionicScrollDelegate){
...
});
Whenever I try to create a unit test for it like so...
(function() {
'use strict';
var scope, controller, filter;
describe('timeInOutController', function () {
beforeEach(module('common.directives.kmDateToday'));
beforeEach(inject(function ($rootScope, $controller, $filter) {
scope = $rootScope.$new();
filter = $filter;
controller = $controller('timeInOutController', {
$scope: scope
});
}));
describe('#date setting', function(){
...
});
});
})();
I get the error:
[$injector:unpr] Unknown provider: $ionicScrollDelegateProvider <- $ionicScrollDelegate
Obviously in my example here I'm not trying to inject the $ionicScrollDelegate into the test, that's just because I've tried it any number of ways with no success and don't know which failed attempt to include.
Also in my karma.conf.js file I am including the ionic.bundle.js and angular-mocks.js libraries/files.
I can successfully unit test anything that doesn't use anything $ionic in it, so I know my testing framework is set up correctly, the issue is injecting anything ionic related.
You need to pass in all the parameters if you're going to instantiate your controller via angular. By adding the parameters you are telling angular that any time you create one of these controllers I need these things too because I am dependent upon them.
So my suggestion is to mock up some representation of these dependencies and inject them in when you are creating the controller. They do not have to be (and should not be) the actual services for your unit tests. Jasmine gives you the ability to create spy objects that you can inject so you can verify the the behavior of this unit.
(function() {
'use strict';
var scope, controller, filter, ionicScrollDelegate;
describe('timeInOutController', function () {
beforeEach(module('common.directives.kmDateToday'));
beforeEach(inject(function ($rootScope, $controller, $filter) {
scope = $rootScope.$new();
filter = $filter;
// func1 and func2 are functions that will be created as spies on ionicScrollDelegate
ionicScrollDelegate = jasmine.createSpyObj('ionicScrollDelegate', ['func1', 'func2']
controller = $controller('timeInOutController', {
$scope: scope,
$filter: filter,
$ionicScrollDelegate: ionicScrollDelegate
});
}));
describe('#date setting', function(){
...
});
});
})();
You can find more about spies via jasmine's documentation
You need to create mock objects for all dependencies your controller is using.
Take this controller as an example:
angular.module('app.module', [])
.controller('Ctrl', function($scope, $ionicLoading) {
$ionicLoading.show();
});
Here you are using the $ionicLoading service, so if you want to test this controller, you have to mock that object specifying the methods you're using in the controller:
describe('Test', function() {
// Mocks
var $scope, ionicLoadingMock;
var ctrl;
beforeEach(module('app.module'));
beforeEach(function() {
// Create $ionicLoading mock with `show` method
ionicLoadingMock = jasmine.createSpyObj('ionicLoading', ['show']);
inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
ctrl = $controller('Ctrl', {
$scope: $scope,
$ionicLoading: ionicLoadingMock
});
});
});
// Your test goes here
it('should init controller for testing', function() {
expect(true).toBe(true);
});
});
I'm trying to test my application with Karma but I get following Errors:
minErr/<#/home/usr/webui-ng/src/client/app/bower_components/angular/angular.js:78:5
loadModules/<#/home/usr/webui-ng/src/client/app/bower_components/angular/angular.js:3859:1
forEach#/home/usr/webui-ng/src/client/app/bower_components/angular/angular.js:325:7
loadModules#/home/usr/webui-ng/src/client/app/bower_components/angular/angular.js:3824:5
createInjector#/home/usr/webui-ng/src/client/app/bower_components/angular/angular.js:3764:3
workFn#/home/usr/webui-ng/src/client/app/bower_components/angular-mocks/angular-mocks.js:2150:9
These are my files:
hello.js
angular.module('myApp', [])
.controller('MainController', function($scope) {
$scope.name = "Ari";
$scope.sayHello = function() {
$scope.greeting = "Hello " + $scope.name;
}
})
hello.js - test file :
describe('Unit: MainController', function() {
// Load the module with MainController
beforeEach(module('myApp'));
var ctrl, scope;
// inject the $controller and $rootScope services
// in the beforeEach block
beforeEach(inject(function($controller, $rootScope) {
// Create a new scope that's a child of the $rootScope
scope = $rootScope.$new();
// Create the controller
ctrl = $controller('MainController', {
$scope: scope
});
}));
it('should create $scope.greeting when calling sayHello',
function() {
expect(scope.greeting).toBeUndefined();
scope.sayHello();
expect(scope.greeting).toEqual("Hello Ari");
});
});
As you can see, I've already loaded the module, as the solution was in Controller undeclared in Jasmine test.
What else could it be? I haven't found much about these errors on the web. I would be very happy to finally find an answer for that.
As runTarm mentioned, the path in the karma.conf.js was not set to the actual path of the script file (hello.js).
angular.module('app.services', []).service("test", function($http, $rootScope){
this.test=function(){
$rootScope.name="test1";
};
};
angular.module('app.controllers', []).controller('TestController', function ($scope, test) {
test.send();
})
I dont get an error but the changes don't get applied to the UI. I tried $scope.apply() and got an error.
We need to tell Angular which modules your module depends on, In our case the main module is app.controllers.
To call service from different model we need tell to controller where is our service:
['app.services']
JS
var appServices = angular.module('app.services', []);
var appCtrl = angular.module('app.controllers', ['app.services']);
appServices
.service("test", function ($http, $rootScope) {
this.send = function () {
$rootScope.name = "test1";
};
});
appCtrl.controller('TestController', function ($scope, test) {
test.send();
});
Demo Fiddle
I think you should change ".service" by ".factory".
As I can see in the creating services docs there are 3 ways of creating custom services. One of then is using factory way, as the following:
var myModule = angular.module('myModule', []);
myModule.factory('serviceId', function() {
var shinyNewServiceInstance;
//factory function body that constructs shinyNewServiceInstance
return shinyNewServiceInstance;
});
Hope to help.