如何触发指令中定义的作用域函数 [英] How to trigger a scope function defined inside a Directive

查看:18
本文介绍了如何触发指令中定义的作用域函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在我的 Angular 应用程序中使用了一个仪表板框架,它有很多指令.

在主指令中,有一些作用域函数可以使用 ng-click 从 html 模板中调用,例如,当用户通过下拉菜单向仪表板添加小部件时:

 

<button ng-repeat="widget in widgetDefs"ng-click="addWidgetInternal($event, widget);"type="button" class="btn btn-primary">{{widget.name}}

但是,我希望能够从我的主控制器代码中调用 addWidgetInternal().

例如,在处理放置事件时,我想通过调用 addWidgetInternal 来添加新的小部件:

(函数(){'使用严格';angular.module('愤怒').controller('MainCtrl',['$scope', '$interval', '$window', 'widgetDefinitions','defaultWidgets', 'gadgetInitService', initOptions]);函数 initOptions($scope, $interval, $window, widgetDefinitions, defaultWidgets, gadgetInitService) {this.userName = '鲍勃';this.userRole = '风险分析师';this.descriptionText = '风险引擎';$scope.dashboardOptions = {小部件定义:小部件定义,defaultWidgets:defaultWidgets,存储:$window.localStorage,storageId: 'rage.ui',};//拖放事件$scope.handleDrop = 函数 () {//$scope.addWidgetInternal();//*** 不能直接调用其他指令!//**** 更新:在仪表板"指令中访问作用域函数的解决方法angular.element(document.getElementById('dash')).scope().addWidgetInternal();}}})();

但是在调用上面的 $scope.addWidgetInternal() 时出现错误:

 TypeError: undefined 不是函数

这里是主仪表板指令的片段 - 特别是 link 函数:

 链接:函数(作用域){scope.widgetDefs = new WidgetDefCollection(scope.options.widgetDefinitions);scope.addWidgetInternal = 函数(事件,widgetDef){event.preventDefault();scope.addWidget(widgetDef);};}

droppable 指令如下:

.directive('droppable', function () {返回 {限制:'A',范围: {drop: '&',},require: '?dashboard',//'dashboard' 指令是可选的;见下面的下降"链接:函数(范围、元素、属性、dashboardCtrl){var el = 元素[0];el.addEventListener('drop', function (e) {if (e.preventDefault) { e.preventDefault();}this.classList.remove('over');var item = document.getElementById(e.dataTransfer.getData('Text'));this.appendChild(item.cloneNode(true));//调用从dashboard.html 'drop' 属性传入的drop 函数范围.$应用(函数(范围){var fn = scope.drop();if ('undefined' !== typeof fn) {fn(e);//将事件传递给调用函数}});返回假;}, 错误的);}}});

这是我放置 droppable 和仪表板指令的dashboard.html 视图:

 

<div id="dash"dashboard="dashboardOptions" class="dashboard-container"></div>

我使用的可拖动指令放置在 中,如下所示(因此,上面控制器代码中提到的放置事件):

 <td><img data-draggable id="chart_gridhier" src="images/chart_gridhier.jpg" title="TreeGrid" alt="Hierarchy Grid" width="80" height="95"></td><td><img data-draggable "id="chart_area" src="images/chart_area.jpg" title="面积图" alt="面积图" width="80" height="95"><<;/td></tr>

底线:不是从 html 视图模板文件触发 ng-click="addWidgetInternal($event, widget);",我需要从控制器触发它.

**** 更新 ****我最终将一些拖放指令逻辑移植到仪表板指令代码中.这样我就可以在与仪表板小部件定义相同的范围内处理 DROP 事件.这是 dashboard 指令中更新后的链接代码:

link: 函数(作用域,元素){//处理来自小工具拖放操作的放置事件(参见 gadgets-include.html)var el = 元素[0];el.ondrop = 函数 (e) {e.dataTransfer.dropEffect = '移动';var item = document.getElementById(e.dataTransfer.getData('text'));this.appendChild(item.cloneNode(true));var newWidget = _.findWhere(scope.widgetDefs, { name: item.id });//这会将新的小部件 div 添加到仪表板scope.addWidgetInternal(e, newWidget);};}

问候,

鲍勃

解决方案

这通常不是一个强烈推荐的方法来实现这一点,但我发现这可以很方便的边缘情况 - 特别是在集成 Angular 之外的代码时.

我为您的代码制作了一个轻量版本的小提琴,以提供一个工作演示,您可以随意在那里进行构建.基本上,我通过 id 抓取您的元素并挖掘"它的范围,然后调用范围函数.在我的示例中,我将您的 chart_area 称为

角度.element(document.getElementById('dash')).scope().addWidgetInternal();.范围().addWidgetInternal(消息);


message 在这种情况下只是我从 UI 发送并在指令中的 addWidgetInternal() 中注销的字符串,从控制器调用


JSFiddle 链接

I'm using a Dashboard framework in my Angular application which has lots of directives.

Within the main directive there are some scope functions which are getting called from an html template using ng-click, for example, when a user adds a widget to the dashboard via a dropdown menu :

   <div class="btn-group" ng-if="options.widgetButtons">
        <button ng-repeat="widget in widgetDefs"
                ng-click="addWidgetInternal($event, widget);" type="button" class="btn btn-primary">
            {{widget.name}}
        </button>
    </div>

However, I'd like to be able to call addWidgetInternal() from my main controller code.

For example, upon handling a drop event I'd like to add the new widget by calling addWidgetInternal:

(function () {
'use strict';
angular.module('rage')
    .controller('MainCtrl',
        ['$scope', '$interval', '$window', 'widgetDefinitions',
        'defaultWidgets', 'gadgetInitService', initOptions]);

function initOptions($scope, $interval, $window, widgetDefinitions, defaultWidgets, gadgetInitService) {

    this.userName = 'Bob';
    this.userRole = 'Risk Analyst';

    this.descriptionText = 'Risk Engine';

    $scope.dashboardOptions = {
        widgetDefinitions: widgetDefinitions,   
        defaultWidgets: defaultWidgets,
        storage: $window.localStorage,
        storageId: 'rage.ui',
    };

    // DRAG AND DROP EVENTS         
    $scope.handleDrop = function () {

        // $scope.addWidgetInternal(); // *** CANNOT MAKE THIS CALL DIRECTLY TO OTHER DIRECTIVE !

        // **** UPDATE: a work-around to access scope function in 'dashboard' directive
        angular.element(document.getElementById('dash')).scope().addWidgetInternal();
    }

}

})();

But I get an error when calling $scope.addWidgetInternal() above:

      TypeError: undefined is not a function

and here's a snippet from the main dashboard directive - specifically, the link function:

 link: function (scope) {

          scope.widgetDefs = new WidgetDefCollection(scope.options.widgetDefinitions);

          scope.addWidgetInternal = function (event, widgetDef) {
              event.preventDefault();
              scope.addWidget(widgetDef);
          };
  }

the droppable directive is the following:

.directive('droppable', function () {
  return {
    restrict: 'A',
    scope: {
        drop: '&',
    },
    require: '?dashboard',  // 'dashboard' directive is optionally requested; see 'drop' below
    link: function (scope, element, attributes, dashboardCtrl) {
        var el = element[0];

        el.addEventListener('drop', function (e) {

            if (e.preventDefault) { e.preventDefault(); }

            this.classList.remove('over');
            var item = document.getElementById(e.dataTransfer.getData('Text'));                
            this.appendChild(item.cloneNode(true));

            // call the drop function passed in from the dashboard.html 'drop' attribute
            scope.$apply(function (scope) {
                var fn = scope.drop();
                if ('undefined' !== typeof fn) {                        
                    fn(e);   // PASS THE EVENT TO CALLING FUNCTION
                }                    
            });

            return false;
        }, false);
    }
}
});

and here's the dashboard.html view where I place both droppable and dashboard directives:

    <div class="col-lg-12" data-droppable drop="handleDrop">
        <div id="dash" dashboard="dashboardOptions" class="dashboard-container"></div>
    </div>

The draggable directive I'm using is placed inside an <img> as follows (hence, the drop event mentioned in the above controller code) :

   <tr>
        <td >
            <img data-draggable id="chart_gridhier" src="images/chart_gridhier.jpg" title="TreeGrid" alt="Hierarchy Grid" width="80" height="95">
        </td>   
        <td><img data-draggable "id="chart_area" src="images/chart_area.jpg"  title="Area Chart" alt="Area Chart" width="80" height="95"></td>            
    </tr>

Bottom line: instead of triggering ng-click="addWidgetInternal($event, widget);" from an html view template file, I need to trigger it from a controller.

**** UPDATE **** I ended up porting some of the drag-drop directive logic INTO the dashboard directive code. This way I can handle the DROP event within the same scope as the dashboard widget definitions. Here's the updated link code in the dashboard directive :

link: function (scope, element) {

          // handle drop event from the gadgets drag-drop operation (see gadgets-include.html)
          var el = element[0];
          el.ondrop = function (e) {
              e.dataTransfer.dropEffect = 'move';
              var item = document.getElementById(e.dataTransfer.getData('text'));                 
              this.appendChild(item.cloneNode(true));                  
              var newWidget = _.findWhere(scope.widgetDefs, { name: item.id });
              // This will add the new widget div to the dashboard
              scope.addWidgetInternal(e, newWidget);
          };
  }

regards,

Bob

解决方案

This generally is not a highly recommended way to accomplish this, but I have found edge cases where this can be handy- especially when integrating code that is outside of Angular.

I crafted a light version fiddle of your code to provide a working demo which you can feel free to build on from there. Basically, I am grabbing your element by id and 'digging' into it's scope, then calling the scope function. In my example, I call your chart_area like

angular
    .element(document.getElementById('dash')).scope().addWidgetInternal();
    .scope()
    .addWidgetInternal(message);


message in this case is just a string I am sending from the UI and logging out in addWidgetInternal() in the directive, called from the controller


JSFiddle Link

这篇关于如何触发指令中定义的作用域函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆