一尘不染

在指令链接功能中动态添加ng-click

angularjs

我正在尝试创建一条指令,该指令允许将元素定义为可点击或不可点击,并且将被定义为:

<page is-clickable="true">
    transcluded elements...
</page>

我希望生成的HTML是:

<page is-clickable="true" ng-click="onHandleClick()">
    transcluded elements...
</page>

我的指令实现如下所示:

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');
            };
        }
    };
});

我可以看到,添加新属性后,Angular不了解ng- click,因此不会触发。我尝试$compile在设置属性后添加一个,但它会导致无限的链接/编译循环。

我知道我可以只检查onHandleClick()函数中的isClickable值是否为true,但是我很好奇如何动态添加ng- click事件来执行此操作,因为我可能需要使用多个其他ng-*指令来执行此操作,而我不想这样做添加不必要的开销。有任何想法吗?


阅读 238

收藏
2020-07-04

共1个答案

一尘不染

更好的解决方案(新):

阅读了Angular文档后,我发现了这一点:

您可以将template指定为表示模板的字符串,或者指定为使用两个参数tElement和tAttrs(在下面的编译函数api中进行描述)并返回表示模板的字符串值的函数。

因此,我的新指令如下所示:(我相信这是处理此类问题的适当“角度”方式)

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');
            };
        }
    };
});

注意新的模板功能。现在,我正在编译该函数之前的模板。

替代解决方案(旧):

添加replace: true该函数可消除重新编译指令时的无限循环问题。然后在链接函数中,我只是在添加新属性后重新编译元素。不过要注意一件事,因为我ng- transclude在元素上有一条指令,所以我需要删除该指令,以便它不会尝试在第二次编译中包含任何内容,因为没有要包含的内容。

这是我的指令现在的样子:

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');
            };
        }
    };
});

我认为第二次重新编译模板并不是理想的选择,因此我觉得在第一次编译模板之前仍然有一种方法可以这样做。

2020-07-04