从AngularJS中的指令访问$ scope

时间:2017-03-23 00:06:26

标签: javascript angularjs svg angularjs-directive angularjs-scope

由于我们在开发AngularJS方面缺乏专业知识,我们在开发过程中遇到了另一个障碍。

我们正在开发一个Angular / Web API应用程序,其中我们的页面只包含一个交互式SVG图表,当用户将鼠标悬停在Angular指令中的特定SVG标记上时,该图表显示数据。

目前应用程序中有两个自定义指令。

  1. 指令一 - 将SVG文件加载到网页
  2. 指令二 - 添加SVG元素悬停事件/数据过滤器
  3. enter image description here

    指令一:

    //directive loads SVG into DOM
    angular.module('FFPA').directive('svgFloorplan', ['$compile', function  ($compile) {
    return {
        restrict: 'A',
    
        templateUrl: 'test.svg',
        link: function (scope, element, attrs) {
    
            var groups = element[0].querySelectorAll("g[id^='f3']")
            angular.forEach(groups, function (g,key) {
                var cubeElement = angular.element(g);
                //Wrap the cube DOM element as an Angular jqLite element.
                cubeElement.attr("cubehvr", "");
                $compile(cubeElement)(scope);
            })
        }
    }
    }]);
    

    SVG图包含具有唯一标识符的标记,即:

    <g id="f3s362c12"></g>
    

    指令二从注入的服务加载JSON数据,该服务对应于每个SVG标记ID。

     //filters json based on hover item
     dataService.getData().then(function(data) {
        thisData = data.filter(function (d) {
        return d.seatId.trim() === groupId
     });
    

    如上所示,Directive Two还添加了一个悬停事件函数,该函数根据悬停的标记过滤JSON数据。

    IE:如果用户将鼠标悬停,则指令中的过滤器将返回此JSON记录:

    {"Id":1,
     "empNum":null,
     "fName":" Bun E.",
     "lName":"Carlos",
      ...
     "seatId":"f3s362c12 ",
     "floor":3,
     "section":"313 ",
     "seat":"12 "}
    

    指令二:

    //SVG hover directive/filter match json to svg
    angular.module("FFPA").directive('cubehvr', ['$compile', 'dataService',     function ($compile, dataService) {
    return {
        restrict: 'A',
        scope: true,
        link: function (scope, element, attrs) {
    
            //id of group 
            scope.elementId = element.attr("id");
            //alert(scope.elementId);
            var  thisData;
            //function call
            scope.cubeHover = function () {
    
                //groupId is the id of the element hovered over.
                var groupId = scope.elementId;
    
                //filters json based on hover item
                dataService.getData().then(function(data) {
                thisData = data.filter(function (d) {
                    return d.seatId.trim() === groupId
                });
                  //return data.seatId === groupId
                  scope.gData = thisData[0];
                  alert(thisData[0].fName + " " + thisData[0].lName + " " +   thisData[0].deptId);  
                });
                //after we get a match, we need to display a tooltip with   save/cancel buttons.
                $scope.empData = $scope.gData;
            };
            element.attr("ng-mouseover", "cubeHover()");
            element.removeAttr("cubehvr");
            $compile(element)(scope);
        }
        //,
        //controller: function($scope, $element){
        // $scope.empData = $scope.gData;
        //}
      }
    }]);
    

    我们现在遇到的问题是(除了具有最小的Angular经验并面临一个独特而困难的实现问题)我们正在尝试使用div标签和角度范围变量来实现创建tooltop的方法可以在用户将鼠标悬停在SVG标记元素上时显示(而不是在下面的Plunker POC链接中演示的Javascript警报)。

    由于数据是由指令驱动的,并且指令已经将“cubehvr”作为参数:

    angular.module("FFPA").directive('*cubehvr*', ['$compile', 'dataService', function ($compile, dataService)
    

    因为我们不知道如何设置HTML页面范围指令或变量,所以我们陷入困境,比如我们的第二个指令:

    <div uib-popover="Last Name: {{empData.lName}}" 
        popover-trigger="'mouseenter'" 
        type="div" 
        class="btn btn-default">Tooltip
    </div>
    

    或者简单地说,这个:

      <div emp-info></div>
    

    div工具提示将具有调用Web API更新功能的html按钮。

    我们在这里缩小了POC Plunk:

    POC Plunk

    还考虑使用Angular Bootstrap UI作为工具:

    Bootstrap UI Plunk

    希望这是有道理的。

1 个答案:

答案 0 :(得分:1)

//编辑。我再次阅读你的问题并通过我的回答。我没有完全回答你的问题,因为它是多层次的。现在我将通过你所有的担忧并尝试回答它们:

  1. 将$ scope传递给其他指令。
  2. $ scope是MVVM设计模式中的Model-View,它将您的模板(View)和模型粘合在一起。从理论上讲,你可能会将$ scope传递给另一个指令,但我认为这是一种反模式。

    1. 指令之间的通信。您可以使用至少4种方法来传达指令:

      • 分享相同的范围,你在你的plunker中几乎做了什么,只是不要在你的指令规范中定义任何“范围”。我不确定这是否是最佳方式,因为您的任何指令都可能会影响您的示波器数据。
      • 创建隔离的范围并使用ng-model或$ watch,这是更安全的方法,但它需要更多的开销。在这种情况下,你将变量向下传递,你可以使用它。$ watch。它是双向绑定的。您可以推送和拉取值。 $watch
      • 创建一个服务,在其中保存诸如事件总线或变量存储之类的内容
      • 您可以将您的指令与事件通信: $on $emit 这适用于分层指令(因此,您必须创建独立的子范围)
    2. 将弹片添加到SVG的孩子。 Bootstrap能够将popover添加到body而不是parent元素。它对SVG很有用:https://angular-ui.github.io/bootstrap/#!#popover

    3. 我重构了你的代码以使用两个指令,并且数据被加载到控制器中。一个指令包装popover,第二个指令传递数据,popover也使用模板,所以它是用angular编译的:

        var app = angular.module('FFPA',  ['ngAnimate', 'ngSanitize', 'ui.bootstrap']);
      
        //controller
        app.controller('myCtrl', function ($scope, dataService) {
          $scope.test = 'test';
          dataService.getData().then(function(data) {
            $scope.dataset = data.reduce(function (obj, item) {
              obj[item.seatId.trim()] = item;
              item.fullName = item.fName + ' ' + item.lName;
              return obj;
            }, {});
          });
        });
      
        angular.module('FFPA').service('dataService', function($http){
          this.getData = function(){
            return $http.get("data.json").then(
              function(response){
                return response.data;
              }, function() {
                return {err:"could not get data"};
              }
            );
          }
        });
      
        //directive loads SVG into DOM
        angular.module('FFPA').directive('svgFloorplan', ['$compile', function ($compile) {
            return {
              restrict: 'A',
              templateUrl: 'test.svg',
              scope: {
                'dataset': '=svgFloorplan'
              },
              link: {
                pre: function (scope, element, attrs) {
                  var groups = element[0].querySelectorAll("g[id^='f3']");
                  scope.changeName = function (groupId) {
                    if (scope.dataset[groupId] && scope.dataset[groupId].lastName.indexOf('changed') === -1) {
                      scope.dataset[groupId].lastName += ' changed';
                    }
                  }
      
                  groups.forEach(function(group) {
                    var groupId = group.getAttribute('id');
                    if (groupId) {
                      var datasetBinding = "dataset['" + groupId + "']";
                      group.setAttribute('svg-floorplan-popover', datasetBinding);
      
                      $compile(group)(scope);
                    }
                  });
                }
              }
            }
        }]);
      
        angular.module('FFPA').directive('svgFloorplanPopover', ['$compile', function ($compile) {
            return {
              restrict: 'A',
              scope: {
                'person': '=svgFloorplanPopover'
              },
              link: function (scope, element, attrs) {
                scope.changeName = function () {
                  if (scope.person && scope.person.fullName.indexOf('changed') === -1) {
                    scope.person.fullName += ' changed';
                  }
                }
                scope.htmlPopover = 'popoverTemplate.html';
                element[0].setAttribute('uib-popover-template', "htmlPopover");
                element[0].setAttribute('popover-append-to-body', 'true');
                element[0].setAttribute('popover-trigger', "'outsideClick'");
                element[0].querySelector('text').textContent += '{{ person.fullName }}';
      
                element[0].removeAttribute('svg-floorplan-popover');
      
                $compile(element)(scope);
      
              }
            }
        }]);
      

      您的HTML正文现在看起来像:

        <body style="background-color:#5A8BC8;">
          <div ng-app="FFPA" ng-controller="myCtrl">
            <div svg-floorplan="dataset"></div>
          </div>
        </body>
      

      popover的HTML:

        <div><button type="button" class="btn btn-default" ng-click="changeName()">{{ person.fullName }}</button></div>
      

      这是工作的plunker: http://plnkr.co/edit/uHgnZ1ZprZRDvL0uIkcH?p=preview