Unit testing directive with controllerAs using Jasmine - angularjs

I am new to Jasmine unit testing and this is the first time I am trying to test a directive with controller. The structure of my directive and controller is little different which I didn't see in any of the blogs or stackoverflow questions. I am struggling a bit to access the function _testSampleFunction() to test in my test cases. The downfall here is that I cannot change the architecture of directive and I want to write test cases accordingly. Any help would be appreciated!
Below is how my controller is defined :
(function () {
'use strict';
angular.module('myApp',[])
.directive('myDirective', myDirective);
function myDirective()
{
var directive ={
template : '<div></div>',
controller : myController,
controllerAs : 'vm',
scope: {},
};
myController.$inject =['$scope'];
return directive;
function myController($scope)
{
var vm = this;
init();
function init()
{
vm.TestSampleFunction = _testSampleFunction;
}
function _testSampleFunction()
{
return 'my directive';
}
}
}})(window.angular);
Here is the plunker (spec.js is included): Demo Plunker
Thanks in Advance!

Directive tests use the real controller, so you don't need to do any controller setup in the test.
I've updated your Plunker with an example of a test through the view, but also how to get hold of the controller.
Directive tests are useful as you know the controller and view are tied up correctly - I mostly (apart from the odd occasion when I can't), test everything through the view (e.g. click on something in the page and check that the view is updated accordingly).

Related

AngularJs 1: How to inject a service to a controller from the directive declaration

I've created a directive for managing (creating/updating/deleting) comments on customers in my application.
Now I'd like the exact same comment functionality (look and feel) on users, so the only thing I need to do is replace my CustomersService with my UsersService.
Both services have the same methods for "crud-ing" comments (I would make both implement an ICommentsService interface if possible in javascript).
My question is how can I in the best possible way reuse my already created View and Controller so I don't have to duplicate code?
My initial approach is to create two separate directives (CustomerComments and UserComments) that reference the same view and controller, but injecting a CustomersService or a UsersService respectively. The problem I'm facing with this, is how to keep the "DI-definition" in the directive declaration whilst having the controller in a separate file...?
This is my directive declaration:
angular.module('myApp').directive('myComments', [
function() {
'use strict';
return {
restrict: 'E',
scope: {
commentId: '='
},
templateUrl:'mycomments.html',
controller: 'CommentsCtrl', //<-- How to define injected objects here?
};
}
]);
...and this is my controller:
angular.module('myApp').controller('CommentsCtrl', [
'$scope',
'$q',
'CustomersService', //<-- These "DI-objects" should be defined in the directive declaration
function ($scope, $q, commentsService) {
$scope.addComment = function(comment){
commentsService.addComment(comment);
};
$scope.getComment = function(commentId){
retur commentsService.getComment(commentId);
};
//...etc...
}
]);
Or are there better ways to solve this?
Usually you do not explicitly register a directive's controller. That is somewhere you would have:
function CommentsCtrl($scope, $q, commentsService) {
And in your directive(s):
controller: ['$scope','$q','CustomersService', CommentsCtrl]

Angular.js 1.2 separating Controllers and Directives using an IIFE

I seem to come across an error when I try to define a controller within a directive that is wrapped in an IIFE. Although I could fixed this by adding ng-controller on the div in tableHelper.html. I was wondering the code below returns tableHelperCtrl as undefined.
Using angular.js 1.2.29
app.module.js
(function () {
'use strict';
angular.module('app', [
]);
})();
tableHelper.controller.js
(function () {
'use strict';
angular
.module('app')
.controller('tableHelperCtrl', tableHelperCtrl);
function tableHelperCtrl() {
var vm = this;
vm.data = 'some data'
}
})();
tableHelper.directive.js
(function () {
'use strict';
angular
.module('app')
.directive('tableHelper', tableHelper);
function tableHelper() {
var directive = {
restrict: 'A',
templateUrl: './src/app/tableHelper/tableHelper.html',
link: link,
controller: tableHelperCtrl,
controllerAs: 'vm'
};
return directive;
}
}
})();
tableHelper.html
<div>
<p>Table Helpers Directive</p>
<table>
<thead></thead>
<td>{{vm}}</td>
</table>
</div>
You should not assign them the same controller. Give them a controller each and make them communicate through scope (using isolate scopes too if needed) or through a service.
There are a couple of issues with your directive code. Suresh's comment about wrapping the name of your controller in quotes seems to be one issue, although I've seen it work without them, I couldn't get it.
You've also got an extra closing curly brace, an you didn't define link although I guess we could assume that you've got it somewhere but left it out.
One more item is since you've defined your controller as 'vm', you want to use vm.data in your html instead of just vm.
Here's a plunker that shows it working with these changes.

AngularJS + RequireJS + angular-ui-grid

I am trying to use angular-ui-grid with AngularJS and RequireJS. See plunker here.
My index31.html has grid and indexController.js defines the gridOptions object. indexController is injected when needed.
When browser loads indexController.js before index31.html, it works fine (i.e. grid is displayed) but when it is the other way round, I get error: $scope.uiGrid is undefined.
How do I specify (in $stateProvider config or elsewhere) to always load indexController.js before index31.html. Or, how do I make all controllers load before the html?
The reason for this is that you require the actual code of the controller asynchronously, i.e. with an inline require:
// app.js
define(function () {
...
app.controller('IndexController', ['$scope', '$injector', function ($scope, $injector) {
// HERE!!!
require(['indexController'], function (controller) {
$injector.invoke(controller, this, { '$scope': $scope });
});
}]);
});
There is no guarantee for the order of loading with this pattern.
What can you do: Require the 'indexController' at the top:
define(['indexController'], function (indexController) {
...
app.controller('IndexController', indexController);
});
It even removes the (horrible IMO) usage of $injector!
(Sidenote: Doing this, the plunk complained about the $scope.$apply() in the last line of indexController.js; I commented it out, it really seems redundant.)
Plunk: http://plnkr.co/edit/fsyljR8FEeZdvXB3SRJP?p=preview

What is the difference between a controller of a directive and a controller of an app

What is the difference in functionality between a controller of a directive and a controller of myApp module defined in below snippet?
How exactly are we supposed to use directive's controller and a module's controller, so that we make maximum utilization of the framework.
In case of differences, 1 or 2 examples showing the difference would really help a lot of newbies around.
JS snippet
angular.module('myApp',[])
.controller('trialCtrl',function($scope){})
.directive('trial',function(){
return{
restrict:'CEAM'
scope:{},
link:function(scope,elem,attr){},
controller:function(){},
template:""
}
})
There is no difference, you could replace this "directive controller" with a string representing another controller.
Example:
angular.module('myApp',[])
.controller('trialCtrl',function($scope){})
.controller('myController',function($scope){})
.directive('trial',function(){
return{
// ...
controller: 'myController'
// ...
}
})
Note: That's even cleaner to do it.

AngularJS: Pass functions and variables to directive's

I have one situation where I have to pass the directive, functions and variables declared in the controller. I can easily do this with $scope. But I read one article which states that we should not populate the scope instead use this. The article had the following example -
//Don't do this
app.controller('MyCtrl', function($scope){
$scope.name = 'Techno Fattie';
});
//Do this
var MyCtrl = function(){
this.name = 'Techno Fattie';
};
app.controller('MyCtrl', MyCtrl);
I liked the idea and I tried implementing the same in my situation which is as follows -
I have a CountryController - which I modified to use this instead of $scope.
I have a countryList.tpl.html - which has only a directive in it and no other code.
Now I have a parent controller which has a stateProvider where I have configuration for country. something like this -
.state('app.country', {
url: "/country",
templateUrl: "countryList.tpl.html",
controller: CountryController
})
I converted the controller's $scope to this but then I observed that I the directive is not receiving the function and variables and the page is not loading properly.
Is it that if the tpl file has a directive then this approach is not useful? If that is not true, then how can we do this? Can anyone help?

Resources