【问题标题】:How to invoke ng-click from a directive partial?如何从指令部分调用 ng-click?
【发布时间】:2014-05-03 11:02:45
【问题描述】:

我有一个指令,它的局部作用域包含 ng-click。

小提琴在那儿:http://jsfiddle.net/stephanedeluca/QRZFs/13/

不幸的是,由于我将代码移动到指令中,ng-click 不再触发。

控制器和指令如下:

var app = angular.module('myApp', ['ngSanitize']);

app.directive('plantStages', function ($compile) {
    return {
        restrict: 'E',
        transclude: true,
        template: '<figure class="cornStages">\
                        <p ng-transclude style="color: skyblue"></p>\
                        <hr/>\
                        <p ng-bind-html="title"></p>\
                        <p ng-bind-html="subtitle">{{subtitle}}</p>\
                        <ul>\
                            <li ng-repeat="stage in stages" ng-click="changePage(stage)">{{stage}}</li>\
                        </ul>\
                    </figure>',
        scope: {
            stages:"=",
            title:'@'
        },
        link: function (scope, element, attrs, ctrl, transclude) {
            if (!attrs.title) scope.title = "Default title";
        }
    };
});

app.controller('myCtrl', function ($scope, $location, $http) {
    $scope.stages = ['floraison', 'montaison'];
    $scope.changePage = function (page) {
        var url = "corn.page.html#/"+page;
        console.log("Change page "+page+" with url "+url);
        alert("about to change page as follows: document.location.href = "+url);
    };

});

调用它的html如下:

<div ng-controller="myCtrl">
    Stages, 
    <p ng-repeat="stage in stages">{{stage}}</p>
    <hr/>
    Plant stages
    <plant-stages 
        title="<b>Exploration<br/>du cycle</b>"
        subtitle="<em>This is a<br/>sub title</em>"
        stages="stages"
    >
        Inner<br/>directive
    </plant-stages>
</div>

有什么想法吗?

【问题讨论】:

标签: angularjs angularjs-directive angularjs-ng-click


【解决方案1】:

您不能直接从指令访问控制器范围内定义的changePage(),因为您的指令具有隔离范围。但是,仍然有几种方法可以做到:

选项 1:

选项 1 是最简单的选项。但是,它很像一种解决方法,我不建议广泛使用它。您可以从传递给链接函数的元素获取控制器的范围,并在那里调用changePage

link: function (scope, element, attrs, ctrl, transclude) {
    if (!attrs.title) scope.title = "Default title";
    scope.changePage = element.scope().changePage; // <= Get parent scope from element, it will have changePage()
}

选项 2:

如果您没有任何涉及在外部控制器中定义的范围的逻辑(如您的示例中所示),您可以为您的指令定义内部控制器并在那里执行它:

app.directive('plantStages', function ($compile) {
    return {
       ...
       controller: ['$scope', function($scope) {
           $scope.changePage = function(page) {
               var url = "corn.page.html#/"+page;
               console.log("Change page "+page+" with url "+url);
               alert("about to change page as follows: document.location.href = "+url);
           }
       }]
    };
});

选项 3:

如果您想在不同的指令和控制器中重用changePage() 中定义的逻辑,最好的方法是将逻辑移动到可能同时注入到控制器和指令的某些服务中:

app.service('changePageService', function() {
    this.changePage = function(page) {
        var url = "corn.page.html#/"+page;
        console.log("Change page "+page+" with url "+url);
        alert("about to change page as follows: document.location.href = "+url);
    }
});

app.controller('myCtrl', function ($scope, $location, $http, changePageService) {
    ...
    changePageService.changePage('page');
    ...
});

app.directive('plantStages', function ($compile) {
    ...
    controller: ['$scope', 'changePageService', function($scope, changePageService) {
        $scope.changePage = changePageService.changePage;
    }]
    ...
});

选项 4:

您可以将changePage(page) 之类的代码作为指令的某些属性的值传递,而内部指令使用'&amp;' 定义作用域属性,这将创建一个函数,该函数将在外部控制器的作用域中执行,并将参数传递给该函数功能。示例:

JavaScript

app.directive('plantStages', function ($compile) {
    return {
        restrict: 'E',
        transclude: true,
        template: '<figure class="cornStages">\
                        <p ng-transclude style="color: skyblue"></p>\
                        <hr/>\
                        <p ng-bind-html="title"></p>\
                        <p ng-bind-html="subtitle"></p>\
                        <ul>\
                            <li ng-repeat="stage in stages" ng-click="changePage({page: stage})">{{stage}}</li>\
                        </ul>\
                    </figure>',
        scope: {
            stages:"=",
            title:'@',
            changePage:'&'
        },
        link: function (scope, element, attrs, ctrl, transclude) {
            if (!attrs.title) scope.title = "Default title";
        }
    };
});

HTML

<div ng-controller="myCtrl">
    Stages, 
    <p ng-repeat="stage in stages">{{stage}}</p>
    <hr/>
    Plant stages
    <plant-stages 
        title="<b>Exploration<br/>du cycle</b>"
        subtitle="<em>This is a<br/>sub title</em>"
        stages="stages"
        change-page="changePage(page)"
    >
        Inner<br/>directive
    </plant-stages>

Plunker: http://plnkr.co/edit/s4CFI3wxs0SOmZVhUkC4?p=preview

【讨论】:

  • 哇,非常有用和广泛的答案,你让我很开心。不过有一点,(我对所有 3 个选项进行了测试,并保留了 #2)页面切换在文档加载时触发。如何防止这种情况? (我的小提琴jsfiddle.net/stephanedeluca/QRZFs/27
  • @StéphanedeLuca 在您的小提琴中,您还将changePageService.changePage('page'); 粘贴到myCtrl 控制器中,我添加了该控制器作为changePageService 服务如何在控制器内部重用和使用的示例。您应该删除或注释此行,并且在文档加载时不会有页面切换。 jsfiddle.net/L8VbK/2
  • 哎呀,你是对的(它解释了为什么我的应用程序中没有问题!)非常感谢 Vadim。
【解决方案2】:

指令的想法是将它们视为可重用的组件,并尽可能避免外部依赖。如果您有可能在自己的控制器中定义指令的行为,请执行此操作。

module.directive('myDirective', function () {
  return {
    restrict: 'E',
    controller: function() { /* behaviour here */ },
    template: '<div>Directive Template</div>',
        scope: {
            /* directive scope */
        }
    };
});

如果这不可能,您可以按照链接问题中的说明传递函数(请参阅上面的评论)。检查updated fiddle

【讨论】:

  • 如果我必须传入函数(甚至是名称),这正是让我感到不舒服的地方。这对我来说是一个架构弱点:-/
  • 你是对的。这使得指令更难重用。更好地将行为(在您的情况下为 changePage())封装在指令 jsfiddle.net/N6P6T/2 的控制器中
猜你喜欢
  • 1970-01-01
  • 2015-08-16
  • 2014-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-23
  • 1970-01-01
  • 2017-11-01
相关资源
最近更新 更多