Directive attribute unit test - angularjs

I have the below directive
return {
restrict: 'E',
scope: {
data: '=',
getLink: '='
},
templateUrl: 'abc.html',
controller: 'abcController'
};
Inside abcController i have this below function that i want to test.
$scope.printData = function()
{
$scope.getLink().then(
function(url) {
$window.open(url);
$window.focus();
},
function(response) {
$log.error('Error opening ' + response);
}
);
};
i am trying to test the printData function, this is the below test i am trying to write.
it('should print the visualizer report', inject(function(mockObjects) {
scope.mockData = mockObjects.mockData;
element = angular.element("<my-directive data='mockData' get-link='test'/>");
compile(element)(scope);
scope.$digest();
var childScope = scope.$$childTail;
childScope.getPdfLink = getPdfLink();
childScope.printData();
}));
I am getting the following error:
“$scope.getLink is not a function
Anything wrong that i am doing ?

Pass the function with '&' as describe on docs:
return {
restrict: 'E',
scope: {
data: '=',
getLink: '&'
},
templateUrl: 'abc.html',
controller: 'abcController'
};
<div your-directive get-link="yourDelegatedMethod()"></div>

Related

Can I use a Directive in the template function?

So this is a simple example i just wrote up for the sake of this question, I'm curious if I'm able to use a directive and pass in an object to the directive's attribute, all in the template, not templateUrl. I would think it would work something like this:
angular.module('myModule')
.directive('someDirective', function() {
return {
scope: {
u: '=',
},
template: '<avatar user="u"></avatar>',
};
});
Yes you can! And it doesn't matter the definition order.
app.directive("directive1", function() {
return {
restrict: 'E',
scope: {
u: '#'
},
template : "<h4>{{u}}</h4><directive2 user='{{u}}'></directive2>"
};
});
app.directive("directive2", function() {
return {
restrict: 'E',
scope: {
user: '#'
},
template : "<h1>{{user}}</h1>"
};
});
See it in action in this jsfiddle
Yes, it's easy to pass your state through scope attributes. Here's a plunkr that demonstrates the concept.
app = angular.module('app',[])
.directive('someDirective', function() {
return {
scope: {
u: '=',
},
template: '<avatar user="u"></avatar>',
};
}).directive('avatar', function() {
return {
scope: {
user: '=',
},
template: '<span>{{user.name}}</span>',
};
})

Directive function parameter is undefined

Directive Template URL:
<div class="filter-input" ng-click="changeVisualization('trocaparaeste')">
Directive:
app.directive('asideFilter', function() {
return {
restrict: 'E',
scope: {
categories: "=",
change: "&onChange",
changeVisualization: '&onChangeVisualization'
},
templateUrl: 'assets/directives/asideFilter/asideFilter.html',
controller: function($scope){
}
};
});;
Directive usage:
<aside-filter change-visualization="onChangeVisualization()"/>
Controller that im trying to get the parameter data:
$scope.onChangeVisualization = function(option) {
console.log('option', option);
}
SOLUTION:
Directive Template URL:
<aside-filter on-change-visualization="onChangeVisualization(option)"/>
Directive:
app.directive('asideFilter', function() {
return {
restrict: 'E',
scope: {
categories: "=",
change: "&onChange",
changeVisualization: '&onChangeVisualization'
},
templateUrl: 'assets/directives/asideFilter/asideFilter.html',
link: function(scope){
// pass 'option' variable so it can be used in the callback
scope.changeVisualization({ option: "worked!" });
}
};
});;
Directive usage:
<aside-filter change-visualization="onChangeVisualization(option)"/>
You have your names switched:
scope: {
// prefixed with 'on'
// so usage: <my-directive on-change-visualization="someFunc(option)"/>
changeVisualization: '&onChangeVisualization'
},
// example:
link: function($scope) {
scope.changeVisualization = scope.changeVisualization || angular.noop;
// pass 'option' variable so it can be used in the callback
scope.changeVisualization({ option: "worked!" });
}
And change your html to:
<!-- with the prefixed 'on-' -->
<aside-filter on-change-visualization="onChangeVisualization(option)" />
In your controller:
$scope.onChangeVisualization = function(option) {
console.log('option', option); // logs: 'option worked!'
}

Controller as with directive not working

I want to accomplish scroll-able content by clicking on Bootstrap module. Its working fine. This is following code of my directive:
'use strict';
angular.module('cbookApp')
.directive('scrollTo', scrollTo);
scrollTo.$inject = ['$anchorScroll'];
function scrollTo($anchorScroll) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.bind('click', function (event) {
event.stopPropagation();
var location = attrs.scrollTo;
if (scope.vm.isEdit || typeof scope.vm.isEdit =="undefined" ) {
$anchorScroll(location);
} else {
$anchorScroll(location+'1');
}
});
}
};
}
But only problem is i am not sure how to apply active class to current affix li. This DEMO way i found to apply class active to current li and remove from other. It was working without Controller as but once i added controller as it stopped working and give some error of scope.
var app = angular.module('app', ['directives']);
app.controller('firstController',[function(){
var vm = this;
vm.model = { value: 'dsf'};
}]);
angular.module('directives', []).directive('toggleClass', function () {
var directiveDefinitionObject = {
restrict: 'A',
template: '<span ng-click="localFunction()" ng-class="selected" ng-transclude></span>',
replace: true,
bindToController: true,
scope: {
model: '='
},
transclude: true,
link: function (scope, element, attrs) {
scope.localFunction = function () {
scope.model.value = scope.$id;
};
scope.$watch('model.value', function () {
if (scope.model.value === scope.$id) {
scope.selected = "active";
} else {
scope.selected = '';
}
});
}
};
return directiveDefinitionObject;
});
Can you please add this in your directive.
element.parent().parent().children().each(function() {
$(this).find('a').removeClass('active');
});
element.addClass('active');
http://jsfiddle.net/hngzxmda/1/
I suggest using controllerAs in your directive too
angular.module('directives', []).directive('toggleClass', function () {
var directiveDefinitionObject = {
restrict: 'A',
template: '<span ng-click="vmd.localFunction()" ng-class="selected" ng-transclude></span>',
replace: true,
bindToController: {
model: '=',
$id: '='
},
scope: {},
transclude: true,
controller: function() {
var _this = this;
this.localFunction = function () {
_this.model.value = _this.$id;
};
},
controllerAs: 'vmd'
};
return directiveDefinitionObject;
});

AngularJS : Change parent scope value from custom directive

For some reason I can't make this work based on the other examples I've seen here on SO.
Here's my directive:
(function () {
angular.module('materialDesign')
.directive('aSwitch', directive);
function directive() {
return {
templateUrl: 'elements/material/switch/switch.html',
transclude: false, // I've tried true here
restrict: 'E',
scope: {
enabled: '=',
toggleState: '=',
},
link: function(scope, element) {
element.on('click touchstart', function() {
scope.toggleState = !scope.toggleState;
});
}
};
}
})();
And the controller scope value that I want to change when toggling the switch/checkbox:
$scope.hideInactive = true;
The html:
<a-switch toggle-state="hideInactive"></a-switch>
and further down in my html page, I have this:
<div ng-show="!hideInactive">
<!-- stuff -->
</div>
EDIT:
This version is "working now", but as soon as I click my switch/checkbox a second time, the element.on fires twice, this flipping my scope value back to what it was.....basically, it's not letting me "un-check" my toggle.
angular.module('material')
.directive('aSwitch', [
'$timeout', function($timeout) {
return {
templateUrl: 'elements/material/switch/switch.html',
transclude: false,
restrict: 'E',
scope: {
enabled: '=',
toggleState: '=',
},
link: function (scope, element) {
element.on('click touchstart', function () {
$timeout(function () {
scope.toggleState.state = !scope.toggleState.state;
scope.$apply();
});
});
}
};
}
]);
EDIT and FINAL SOLUTION:
Here's the updated directive link property that fixed everything. I'd like to add that Oleg Yudovich's answer was also used (passing an object as the property instead of a true/false by itself)
link: function (scope, element) {
element.on('click touchstart', function (event) {
if (event.srcElement && event.srcElement.id && event.srcElement.id === "switch") {
event.stopPropagation();
$timeout(function() {
scope.toggleState.state = !scope.toggleState.state;
});
}
});
}
Try to pass object instead of primitive variable like this:
$scope.hideInactive = {
state: false;
}
html without changes:
<a-switch toggle-state="hideInactive"></a-switch>
in your directive:
scope.toggleState.state = !scope.toggleState.state;
Reed this awesome article: https://github.com/angular/angular.js/wiki/Understanding-Scopes
You need to run digest cycle after changes in scope, because changing scope binding from event will not run angular digest cycle, you need to run it manually by doing scope.$apply()
Directive
(function () {
angular.module('materialDesign')
.directive('aSwitch', directive);
function directive($timeout) {
return {
templateUrl: 'elements/material/switch/switch.html',
transclude: false, // I've tried true here
restrict: 'E',
scope: {
enabled: '=',
toggleState: '=',
},
link: function(scope, element) {
element.on('click touchstart', function() {
$timeout(function(){
scope.toggleState = !scope.toggleState;
});
});
}
};
}
})();
Try below code:
angular.module('material').directive('aSwitch', ['$timeout', function($timeout) {
return {
templateUrl: 'elements/material/switch/switch.html',
transclude: false,
restrict: 'E',
scope: {
enabled: '=',
toggleState: '=',
},
link: function(scope, element) {
element.on('click touchstart', function() {
$timeout(function() {
scope.toggleState.state = !scope.toggleState.state;
scope.$apply();
});
});
}
};
}]);

Scope Isolation & nested directives

Dealing with '&' and isolated scope.
Is it possible to pass a value up through a parent directive? I want to pass id from the textdisp directive to the controller.
HTML:
<body ng-controller="MainCtrl">
<builder removequest="deleteQuestion(id)"></builder>
</body>
ANGULAR:
app.controller('MainCtrl', function($scope) {
$scope.deleteQuestion = function(id) {
alert(id);
}
});
app.directive('builder', function() {
return {
restrict: 'E',
scope: {
removequest: '&'
},
template: '<div>Hello how are you? <textdisp removequest=removequest(id)></textdisp></div>'
}
});
app.directive('textdisp', function() {
return {
restrict: 'E',
scope: {
removequest: '&'
},
template: '<div ng-click="remove()">Click here!</div>',
link: function (scope, el) {
scope.remove = function(id) {
console.log('workin')
scope.removequest(1);
}
}
}
});
I believe there are 2 things going on with your code:
When you're placing removequest="removequest(id)" that is calling the function, and not just referring to the function.
I believe that the &attr binding isn't returning the function that you're expecting.
Try this Plunker; it essentially uses { removequest: '=' } for bi-directional binding, and removequest="deleteQuestion" / removequest="removequest" for function references rather than calling the function.
It's a little confusing, but you can use object parameter when you need to pass values into your function invoked via & binding. Take a look at this code it will make everything clear:
app.controller('MainCtrl', function($scope) {
$scope.deleteQuestion = function(id) {
alert(id);
}
});
app.directive('builder', function() {
return {
restrict: 'E',
scope: {
removequest: '&'
},
template: '<div>Hello how are you? <textdisp removequest="removequest({id: id})"></textdisp></div>'
}
});
app.directive('textdisp', function() {
return {
restrict: 'E',
scope: {
removequest: '&'
},
template: '<div ng-click="remove()">Click here!</div>',
link: function(scope, el) {
scope.remove = function(id) {
scope.removequest({id: 34534}); // <-- 1.
}
}
}
});
Demo: http://plnkr.co/edit/3OEy39UQlS4EyOu5cq4y?p=preview
Note how you specify scope.removequest({id: 34534}) parameter to be passed into <textdisp removequest="removequest({id: id})">.

Resources