Monday, May 20, 2024
 Popular · Latest · Hot · Upcoming
61
rated 0 times [  66] [ 5]  / answers: 1 / hits: 67609  / 10 Years ago, sat, march 1, 2014, 12:00:00

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?


More From » angularjs

 Answers
13

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.


[#72217] Friday, February 28, 2014, 10 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
eden

Total Points: 730
Total Questions: 117
Total Answers: 117

Location: Peru
Member since Fri, Oct 14, 2022
2 Years ago
;