操纵SVG元素时丢失事件绑定[使用JSFiddle]

时间:2014-06-03 19:30:13

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

我已经创建了一个自定义指令,用于将<rect><circle>等标记呈现为<svg>元素。

我有一个形状模型,它只是一个具有各种属性的对象数组,例如widthheightstroke-width

这些形状由我的自定义指令

呈现为SVG
<svg height="400" width="600">
  <dr-shape ng-repeat="shape in shapes"></dr-shape>
</svg>

指令定义如下所示(评论很多):

app.directive('drShape', ['$compile', function($compile) {
  // A helper function for adding attributes to an element.
  var bindNgAttr = function(el, attribute, value) {
    el.attr('ng-attr-' + attribute, '{{shape.' + attribute + '}}');
  };

  return {
    restrict: 'E',
    link: function(scope, element, attrs) {

      // #makeNode simply creates a DOM node using `document.createElementNS`
      // so that it can be appended to an SVG element. It also assigns
      // any attributes on the directive.
      var shape = makeNode(scope.shape.tagName, element, attrs);
      var elementShape = angular.element(shape);

      // This part iterates through a shape's attributes and attaches them
      // to the created node. I'm doing this (rather than declaring them
      // where the directive is used in the template because <circle> nodes
      // take different attributes to <rect> nodes. Thus I can't hardcode them.
      for (var attribute in scope.shape) {
        // #isPublic simply ensures that we don't assign any '$' attributes.
        if (isPublic(attribute, scope.shape[attribute])) {
          // Here we use the helper defined above.
          bindNgAttr(elementShape, attribute);
        }
      }

      // Here I'm adding a click listener to the element. This works and
      // when I click a <rect> I can see the console log. The fill color
      // never seems to change in the browser however. I can only assume that
      // the model data is being changed but the binding with the DOM has been
      // broken.
      elementShape.on('click', function() {
        console.log('Clicked in directive');
        scope.shape.fill = '#bada55';
      });

      element.replaceWith(shape);

      // Not sure what this does. It comes from: http://goo.gl/ZoYpQv
      attrs.$observe('value', function(value) {
        scope['value'] = parseInt(value, 10);
        $compile(shape)(scope);
      });
    }
  };
}]);

当我使用一些形状数据运行时,我在浏览器中获得以下SVG:

<svg height="400" width="600">
  <!-- ngRepeat: shape in shapes -->
  <rect ngRepeat="shape in shapes" strokeWidth="3" ng-attr-stroke="{{shape.stroke}}" ng-attr-fill="{{shape.fill}}" ng-attr-x="{{shape.x}}" ng-attr-y="{{shape.y}}" ng-attr-width="{{shape.width}}" ng-attr-height="{{shape.height}}" ng-attr-tagname="{{shape.tagName}}" stroke="#aea086" fill="#fff" x="239" y="89" width="25" height="22" tagname="rect"></rect>
  <!-- end ngRepeat: shape in shapes -->
  <rect ngRepeat="shape in shapes" strokeWidth="3" ng-attr-stroke="{{shape.stroke}}" ng-attr-fill="{{shape.fill}}" ng-attr-x="{{shape.x}}" ng-attr-y="{{shape.y}}" ng-attr-width="{{shape.width}}" ng-attr-height="{{shape.height}}" ng-attr-tagname="{{shape.tagName}}" stroke="#a265e7" fill="#fff" x="233" y="6" width="12" height="43" tagname="rect"></rect>
  <!-- end ngRepeat: shape in shapes -->
</svg>

Here's a JSFiddle with the above code

正如我在上面代码块的注释中解释的那样,操纵任何形状模型中的数据都不会更新相应的SVG DOM节点。

我尝试在点击处理程序中调用scope.$apply()scope.$digest(),但它不会更改任何内容。 (编辑:这是不正确的 - 请参阅下面的答案。)

如何让我的数据更改在视图中生效?

1 个答案:

答案 0 :(得分:0)

事实证明,调用scope.$apply()实际上在点击处理程序中起作用。我不确定我在做什么让我觉得它没有。

  elementShape.on('click', function() {
    console.log('Clicked in directive');
    scope.shape.fill = '#bada55';
    scope.$apply();
  });

我在JSFiddle中犯了一个错误,这个问题加剧了这个问题。 Here's a working fiddle

对不起,如果我浪费了任何人的时间!