Angular.js 在指令中更新 SVG 模板 [英] Angular.js updating SVG templates in directives

查看:20
本文介绍了Angular.js 在指令中更新 SVG 模板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

不久前我问了Angular.js 在指令中渲染 SVG 模板",在这里我用 SVG 节点替换了 angular 在渲染模板时生成的 DOM 节点.我得到了一个为我回答的回复,但我意识到我丢失了 angular 的所有数据绑定.

查看 Plunkr(点击更新):http://plnkr.co/edit/HjOpqc?p=预览

如何用 SVG 节点替换这些 DOM 节点,并保持我的角度绑定不变?我尝试使用 $compile 使其工作(就像我使用常规 html 所做的那样),但它只是不起作用.

代码:

var svgNS = 'http://www.w3.org/2000/svg';app.directive('path', ngSvg('path'));app.directive('g', ngSvg('g'));功能ngSvg(类型){返回函数($超时,$编译){返回 {限制:'E',链接:函数(范围,EL,属性){//如果节点已经是svg,则跳过它们if (el[0].namespaceURI === svgNS) {返回;}//我希望下面的代码块能够工作,//但它不与 ng-repeat//var newAttr = {};//_.each(el[0].attributes, function(at) {//newAttr[at.nodeName] = at.value;//});//var path = makeNode(type, el, newAttr);//var parent = path.cloneNode(true);//$compile(parent)(scope);//var children = el.children();//$(parent).append(children);//$timeout(function() {//el.replaceWith(parent);//})//这适用于渲染,但不会更新 svg 元素//当点击更新时$超时(功能(){var newAttr = {};_.each(el[0].attributes, function(at) {newAttr[at.nodeName] = at.value;});var path = makeNode(type, el, newAttr);var parent = path.cloneNode(true);var children = el.children();$(parent).append(children);el.replaceWith(parent);});}}}}/* 使用给定的设置创建一个形状节点.*/函数makeNode(名称,元素,设置){//var ns = 'http://www.w3.org/2000/svg';var node = document.createElementNS(svgNS, name);for(设置中的var属性){var 值 = 设置[属性];if (value !== null && value !== null && !attribute.match(/\$/) &&(typeof value !== 'string' || value !== '')) {node.setAttribute(attribute, value);}}返回节点;}

解决方案

这个问题在 Angular 1.3 中得到解决,这里是一些自定义 svg 指令的实现,具有您期望从 Angular 指令中获得的行为.现在对指令声明有一个特殊要求,如下 templateNamespace: 'svg'

另请注意,我覆盖了一些保留属性,例如 xheight 相关.为了保持对这些的更多控制,您可以利用 ng-attr ,例如 '<rect ng-attr-width="{{ ngWidth }}"/>

JSFiddle 链接

这里是一个自定义的

app.directive('ngRect', [function() {返回 {模板命名空间:'svg',替换:真的,模板:'<rect ng-attr-width="{{ ngWidth }}" ng-attr-height="{{ ngHeight }}" ng-attr-x="{{ ngX }}" ng-attr-y="{{ ngY }}" ng-click="ngRectClick()"/>',范围: {'ngHeight': '=','ngWidth': '='},链接:函数(范围,元素,属性){scope.ngRectClick = function() {控制台日志(元素);}}}}]);app.directive('ngCircle', [function () {返回 {模板命名空间:'svg',替换:真的,模板:'<circle ng-attr-cx="{{ ngCx }}" ng-attr-cy="{{ ngCy }}" ng-attr-r="{{ ngR }}" ng-attr-fill="{{ ngFill }}" ng-click="ngCircleClick()"/>',范围: {'ngCx': '=','ngCy': '=','ngR': '=','ngFill': '='},链接:函数(范围,元素,属性){scope.ngCircleClick = function() {控制台日志(元素);}}}}]);

A while ago I asked about "Angular.js rendering SVG templates in directives", where I was replacing the DOM nodes that angular makes when rendering templates, with SVG nodes. I got a response that answered it for me, but I realized that I lost all the databindings from angular.

See Plunkr (click update): http://plnkr.co/edit/HjOpqc?p=preview

How do I replace these DOM nodes with SVG nodes, and leave my angular bindings intact? I tried using $compile to make it work (as I've done with regular html), but its just not working.

code:

var svgNS = 'http://www.w3.org/2000/svg';
app.directive('path', ngSvg('path'));
app.directive('g', ngSvg('g'));

function ngSvg(type) {
  return function($timeout, $compile) {
    return {
      restrict: 'E',
      link: function(scope, el, attr) {
        //skip nodes if they are already svg
        if (el[0].namespaceURI === svgNS) {
          return;
        }

        // I would expect the chunk of code below to work,
        // but it does not with ng-repeat

        // var newAttr = {};
        // _.each(el[0].attributes, function(at) {
        //   newAttr[at.nodeName] = at.value;
        // });

        // var path = makeNode(type, el, newAttr);
        // var parent = path.cloneNode(true);

        // $compile(parent)(scope);

        // var children = el.children();
        // $(parent).append(children);

        // $timeout(function() {
        //   el.replaceWith(parent);
        // })


        // this works for rendering, but does not update the svg elements
        // when update is clicked
        $timeout(function() {
          var newAttr = {};
          _.each(el[0].attributes, function(at) {
            newAttr[at.nodeName] = at.value;
          });

          var path = makeNode(type, el, newAttr);
          var parent = path.cloneNode(true);


          var children = el.children();
          $(parent).append(children);
          el.replaceWith(parent);
        });
      }
    }
  }
}

/* Create a shape node with the given settings. */
function makeNode(name, element, settings) {
  // var ns = 'http://www.w3.org/2000/svg';
  var node = document.createElementNS(svgNS, name);
  for (var attribute in settings) {
    var value = settings[attribute];
    if (value !== null && value !== null && !attribute.match(/\$/) &&
      (typeof value !== 'string' || value !== '')) {
      node.setAttribute(attribute, value);
    }
  }
  return node;
}

解决方案

This issue is solved in Angular 1.3 and here is an implementation of some custom svg directives with the behaviors you would expect from an Angular directive. There is now a special requirement is on the directive declaration as follows templateNamespace: 'svg'

Also notice I am overriding some reserved attributes, for example, x and height in regards to <rect/>. To retain more control over these you can leverage ng-attr as such '<rect ng-attr-width="{{ ngWidth }}" />

JSFiddle Link

Here is a custom <rect/> and <circle/>

app.directive('ngRect', [function () {
    return {
        templateNamespace: 'svg',
        replace: true,
        template: '<rect ng-attr-width="{{ ngWidth }}" ng-attr-height="{{ ngHeight }}" ng-attr-x="{{ ngX }}" ng-attr-y="{{ ngY }}" ng-click="ngRectClick()"/>',
        scope: {
            'ngHeight': '=',
            'ngWidth': '='
        },
        link: function (scope, elem, attrs) {
            scope.ngRectClick = function() {
                console.log(elem);
            }
        }
    }
}]);

app.directive('ngCircle', [function () {
    return {
        templateNamespace: 'svg',
        replace: true,
        template: '<circle ng-attr-cx="{{ ngCx }}" ng-attr-cy="{{ ngCy }}" ng-attr-r="{{ ngR }}" ng-attr-fill="{{ ngFill }}" ng-click="ngCircleClick()"/>',
        scope: {
            'ngCx': '=',
            'ngCy': '=',
            'ngR': '=',
            'ngFill': '='
        },
        link: function (scope, elem, attrs) {
            scope.ngCircleClick = function() {
                console.log(elem);
            }
        }
    }
}]);

这篇关于Angular.js 在指令中更新 SVG 模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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