I'm trying to create a directive that would allow an element to be defined as clickable or not, and would be defined like this:
<page is-clickable="true"> transcluded elements... </page>
I want the resulting HTML to be:
<page is-clickable="true" ng-click="onHandleClick()"> transcluded elements... </page>
My directive implementation looks like this:
app.directive('page', function() { return { restrict: 'E', template: '<div ng-transclude></div>', transclude: true, link: function(scope, element, attrs) { var isClickable = angular.isDefined(attrs.isClickable) && scope.$eval(attrs.isClickable) === true ? true : false; if (isClickable) { attrs.$set('ngClick', 'onHandleClick()'); } scope.onHandleClick = function() { console.log('onHandleClick'); }; } }; });
I can see that after adding the new attribute, Angular does not know about the ng-click
, so it is not firing. I tried adding a $compile
after the attribute is set, but it causes an infinite link/compile loop.
I know I can just check inside the onHandleClick()
function if the isClickable
value is true
, but I'm curious how one would go about doing this with dynamically adding an ng-click
event because I may need to do with this with multiple other ng-*
directives and I don't want to add unnecessary overhead. Any ideas?
After reading through the Angular docs I came across this:
You can specify template as a string representing the template or as a function which takes two arguments tElement and tAttrs (described in the compile function api below) and returns a string value representing the template.
So my new directive looks like this: (I believe this is the appropriate "Angular" way to go about this type of thing)
app.directive('page', function() { return { restrict: 'E', replace: true, template: function(tElement, tAttrs) { var isClickable = angular.isDefined(tAttrs.isClickable) && eval(tAttrs.isClickable) === true ? true : false; var clickAttr = isClickable ? 'ng-click="onHandleClick()"' : ''; return '<div ' + clickAttr + ' ng-transclude></div>'; }, transclude: true, link: function(scope, element, attrs) { scope.onHandleClick = function() { console.log('onHandleClick'); }; } }; });
Notice the new template function. Now I am manipulating the template inside that function before it is compiled.
Added replace: true
to get rid of the infinite loop issue when recompiling the directive. And then in the link function I just recompile the element after adding the new attribute. One thing to note though, because I had an ng-transclude
directive on my element, I needed to remove that so it doesn't try to transclude anything on the second compile, because there is nothing to transclude.
This is what my directive looks like now:
app.directive('page', function() { return { restrict: 'E', replace: true, template: '<div ng-transclude></div>', transclude: true, link: function(scope, element, attrs) { var isClickable = angular.isDefined(attrs.isClickable) && scope.$eval(attrs.isClickable) === true ? true : false; if (isClickable) { attrs.$set('ngClick', 'onHandleClick()'); element.removeAttr('ng-transclude'); $compile(element)(scope); } scope.onHandleClick = function() { console.log('onHandleClick'); }; } }; });
I don't think that recompiling the template a second time is ideal though, so I feel that there is still a way to do this before the template is compiled the first time.
You could always just modify your ng-click to look like this:
ng-click="isClickable && someFunction()"
No custom directive required :)
Here is a JSFiddle demoing it: http://jsfiddle.net/robianmcd/5D4VR/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With