【问题标题】:How to trigger a scope function defined inside a Directive如何触发指令内定义的范围函数
【发布时间】:2015-03-07 00:54:52
【问题描述】:

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

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

   <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>

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

例如,在处理放置事件时,我想通过调用 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();
    }

}

})();

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

      TypeError: undefined is not a function

这是来自主仪表板指令的 sn-p - 特别是 link 函数:

 link: function (scope) {

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

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

droppable 指令如下:

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

这里是dashboard.html 视图,我放置了droppable 和dashboard 指令:

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

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

   <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>

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

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

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

问候,

鲍勃

【问题讨论】:

  • 问题是您试图在较低范围内触发函数。通常当您需要在不同的控制器中触发方法时,您会使用服务..
  • @rahpuser - 这是真的,我很欣赏这个建议。我确实有很多服务/工厂,但主要的小部件功能存在于该指令中,因此将其移植到服务中可能并非易事。也许我可以在幕后模仿 ng-click - 即在我的页面上的可拖动指令期间。
  • 试图遵循这篇文章中的一些建议,但这有点棘手 - stackoverflow.com/questions/16881478/…

标签: javascript html angularjs angularjs-directive


【解决方案1】:

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

我为您的代码制作了一个轻量级版本的小提琴,以提供一个工作演示,您可以从那里随意构建。基本上,我通过 id 获取您的元素并“挖掘”到它的范围,然后调用范围函数。在我的例子中,我打电话给你的chart_area 喜欢

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


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


JSFiddle Link

【讨论】:

  • 谢谢。我现在正在试验。我还发布了一个关于使用 REQUIRE 选项的问题,允许我直接从 'droppable' 指令调用 'dashboard' 指令 - stackoverflow.com/questions/27863573/…
  • 这对你有用吗?我也试图回答你的另一个问题。查看答案stackoverflow.com/a/27864420/1464112。祝你好运
  • sal - 我永远不会猜到你提供的解决方法。它确实直接调用了我的仪表板指令的 addWidgetInternal()。我将 elementById 更新为抓取“破折号”,这就是我要放弃的地方。
  • 我现在只需要弄清楚如何从那个 ng-click="addWidgetInternal($event, widget);" 传递原始的两个参数。我需要在 droppable 指令的“drop”侦听器中获取事件和小部件类型。
  • 没问题,只需从 UI 传递 $event 并更新所有函数签名,直到指令。这是一个使用相同示例的小提琴。 jsfiddle.net/aregnfy1如果对您有用,请考虑接受答案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-30
  • 2016-03-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多