Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add ng-click dynamically in directive link function

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?

like image 336
DRiFTy Avatar asked Mar 01 '14 15:03

DRiFTy


2 Answers

Better Solution (New):

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.

Alternative solution (Old):

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.

like image 176
DRiFTy Avatar answered Sep 27 '22 23:09

DRiFTy


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/

like image 25
rob Avatar answered Sep 27 '22 23:09

rob