I started writing unit tests using Jasmine for angularJS applications and stuck when writing a unit test to the controller in model which doesn't have a name.
How can I instantiate the controller in the model and test the controller?
angular.module('test',[])
.controller('alpha', function($scope, $modal){
$scope.openModal = function() {
$modal.open({
templateUrl: 'template.html',
backdrop: true,
windowClass: 'content-modal hbox',
controller: function(
$scope,
$modalInstance
) {
$scope.test = function() {
//test
}
}
));
}
});
Thanks in advance.
I would suggest it is much easier to unit test if you don't use an anonymous function for your controller. If you just write out another full controller you can use it for your modal and test it easily.
That said - here are several approaches to doing what you want to do: http://sirarsalih.com/tag/unit-testing-anonymous-javascript-functions/
Related
Is is possible to use property injection in angularJS?
Scenario
I know this will work
app
.module('myapp')
.config(function($stateProvider) {
$stateProvider.state('the-state', {
url: '/the/url',
templateUrl: 'view.html',
controller: 'ctrl',
controllerAs: 'vm',
resolve: {
'boolFlag': function(service){return service.getBoolean();}
}
});
})
.service('service', function(){
this.getBoolean = function() {return ...};
})
.controller('ctrl', function(boolFlag) {
this.boolFlag = boolFlag;
this.execute = function(){...};
});
<div ng-show="vm.boolFalg">
simple dynamic content
</div>
<button type="button" ng-click="vm.execute()">Click Me</button>
But it feels leaky. boolFlag` is only used in the view to show/hide content. Is there I way I can resolve the controller and set the boolFlag property on the controller instance? I assume providers or factories would be the way to go, but I'm going around in circles trying to make sense of it.
I envision it would look something like
app
.module('myapp')
.config(function($stateProvider) {
$stateProvider.state('the-state', {
url: '/the/url',
templateUrl: 'view.html',
controller: 'ctrl',
controllerAs: 'vm',
});
})
.provider('ctrlProvider', function(ctrlProvider, service) {
var ctrl = ctrlProvider.$get/invoke/build/create();
ctrl.boolFlag = service.getBoolean();
return ctrl;
})
.service('service', function(){
this.getBoolean = function() {return ...};
})
.controller('ctrl', function() {
this.execute = function(){...};
});
I could also be going about this the wrong way. Many controllers will need the boolFlag property. Maybe it should be part of a $parentScope? But I don't know how to code that.
Update
I was thinking about this more last night. The boolFlag doesn't need to be associated to the controller at all. It only needs to be part of the scope. $scope.boolFlag = service.getBoolean();
The question then becomes, how can I populate $scope without the controller?
you could use factory to maintain the value of boolFlag so that it is shared between the controllers. It is better to avoid using $parentScope. This explains how to proceed with that. If you need to set the value of factory initially you could also use it in app.config of your main module to set its value.
I'm trying to test a certain controller functionality with Jasmine unit testing.
I have a function which opens a modal instance with a new controller:
function openFilterModal(data) {
var modalInstance = $uibModal.open({
controller: FilterModalController,
controllerAs: 'vm',
templateUrl: 'modal.html',
resolve: {
data: function () {
return data;
}
}
});
return modalInstance.result;
}
The controller is defined below:
FilterModalController.$inject = ['alertsModalService', '$uibModalInstance', 'data', '$scope', 'logger', '$timeout'];
function FilterModalController(alertsModalService, $uibModalInstance, data, $scope, logger, $timeout) {
// code goes here }
When I try to call 'openFilterModal', I'm unable to get the controller instance, just getting the promise from it.
I've tried to used the $controller(FilterModalController) function in Jasmine, but it couldn't find the controller.
What is the best practice in that case?
Should I add the controller definition to my module? right now it defined like anonymous controller... not sure if that's good behavior or not.
like that:
app.controller('FilterModalController', FilterModalController);
Is it ok to define 2 controllers on the same module?
Or there is another way to get test the controller?
Thanks!
Currently I have a factory method like this:
open_model: function(som_id){
var modelSettings = $aside({
controller: // a very big controller code
size: 'sm',
placement: 'right',
templateUrl: 'views/id/modal.html',
resolve: {
modal_data: function(){
return area_id;
}
}
});
},
I want to separate out the controller from this method and call it by name. Currently this is >100 lines of code in controller section and hence it makes factory dirty. So is there a way to define the controller somewhere else, and call the controller by name instead of writing the code.
In addition to #nikjohn's answer, you can also create a separate angular controller and refer it within you modelSettings. Since you already said your controller code for the modal exceeds 100 lines, this even might be a better solution.
angular.module('myApp').controller('ModalInstanceCtrl', ['$scope', 'modal_data', function($scope, modal_data) {
// modal controller code goes here
}]);
// and refer it in your factory method
open_model: function(som_id){
var modelSettings = $aside({
controller: 'ModalInstanceCtrl'
size: 'sm',
placement: 'right',
templateUrl: 'views/id/modal.html',
resolve: {
modal_data: function(){
return area_id;
}
}
});
}
You can define your controller wherever you want, and inject it as a dependency into your factory (although I don't know what a controller would be doing within a factory).
controllerFn: function(a, b) {
// ......
}
open_model: function(som_id, controllerFn){
var modelSettings = $aside({
controller: controllerFn,
size: 'sm',
placement: 'right',
templateUrl: 'views/id/modal.html',
resolve: {
modal_data: function(){
return area_id;
}
}
});
},
You should never access a controller within a factory. You're mixing concerns by doing that. If there's functionality you want to separate out, create another service and inject that service into your factory. Your controller should be leveraging services and factories, not the other way around.
I need to work with several different directives in angularJS. The directives have different templates and the controllers should work in very similar way, with very small changes each-other.
I was thinking to use just one shared controller that adapt its behaviour to the directive it's included like the idea described in code:
var module = angular.module('app', []);
module.directive('myFirstDirective', function () {
return {
scope: {},
controller: 'MyController',
templateUrl: './directives/first.html'
};
});
module.directive('mySecondDirective', function () {
return {
scope: {},
controller: 'MyController',
templateUrl: './directives/second.html'
};
});
module.controller('MyController', function ($scope) {
$scope.myEvent = function () {
//if it's first directive do this
//if it's second directive do that
};
});
Is there any way in angular to do this?
You can use ng-init="callback1()" and ng-init="callback2()" in your directives. And describe both callbacks in your controller.
Simple way of opening modal with ngDialog is this:
ngDialog.open({
template: 'template.html',
controller: 'someCtrl'
})
How can I send variables to that 'someCtrl'?
Is there such thing as 'resolve' in ngDialog?
Example from angular-bootstrap modal:
$modal.open({
template: "<p>This is template</p>",
controller: "someCtrl",
resolve: {
someVar: function(){
return "Value of someVar"
}
}
})
this would open the modal send the 'someVar' to the responsible Controller.
UPDATE:
It seems like new version of ngDialog added this feature:
ngDialog.open({
controller: function Ctrl(dep) {/*...*/},
resolve: {
dep: function depFactory() {
return 'dep value';
}
}
});
Well looks like ngDialog doesn't support resolve and custom injection in controller. However you can alway do it manually by creating controller instance yourself:
ngDialog.open({
scope: $scope,
template: 'template.html',
controller: $controller('someCtrl', {
$scope: $scope,
name: 'Thomas'
})
});
then in controller you will be able to access injected service/variable:
app.controller('someCtrl', function($scope, name) {
console.log(name); // Thomas
});
However there is a caveat with this approach, because when controller in instantiated by ngDialog itself it also injects $element service in it, which is an angular.element instance of the opened dialog HTML (however I doubt it's even necessary in controller). But you should know it anyway.
Demo: http://plnkr.co/edit/3YpQ2bemk8fntKAPWY9i?p=preview