Используйте svg с angularjs ng-repeat

Я изучаю angularjs, и я пытаюсь использовать ng-repeat для создания графика svg.

У меня есть этот html:

<svg>
    <g id="g_{{$index}}" ng-repeat="i in range" ng-cloak>
        <rect x="{{i / 5}}" y="{{i / 5}}" width="{{i / 5}}" height="{{i / 5}}"></rect>
    </g>
</svg>

"диапазон" - это просто простой массив, который определен в контроллере следующим образом:

$scope.range = [100, 200, 300];

работает html; прямоугольники отображаются на моей странице.

Однако Chrome продолжает бросать следующую ошибку:

Error: Invalid value for <rect> attribute height="{{i / 5}}"    js/angular.js:1584
  JQLiteClone   js/angular.js:1584
  JQLite.(anonymous function)   js/angular.js:2163
  publicLinkFn  js/angular.js:3862
  ngRepeatWatch js/angular.js:13641
  Scope.$digest js/angular.js:7889
  Scope.$apply  js/angular.js:8097
    js/angular.js:961
  invoke    js/angular.js:2857
  resumeBootstrapInternal   js/angular.js:959
  bootstrap js/angular.js:973
  angularInit   js/angular.js:934
    js/angular.js:14756
  fire  js/jquery-2.0.0.js:2863
  self.fireWith js/jquery-2.0.0.js:2975
  jQuery.extend.ready   js/jquery-2.0.0.js:398
  completed js/jquery-2.0.0.js:93

Кажется, что это не совсем похоже на то, что я делаю...

У кого-нибудь есть идея, почему я получаю эту ошибку?

Ответ 1

Проблема заключается в том, что Chrome видит интерполяцию Angular как недопустимое значение для этих атрибутов (поскольку по крайней мере однажды элемент фактически существует в DOM - хотя и невидимо - с "недопустимыми" значениями). Я написал решение, которое соответствует другим решениям Angular в отношении обработки браузером специальных атрибутов, где вместо использования x, y, width и height вы указываете ng-x, ng-y, ng-width и ng-height и реальные атрибуты устанавливаются после интерполяции значений.

Вот решение на JSFiddle. Я собираюсь отправить патч на Angular, чтобы узнать, можем ли мы получить это в ядре.

HTML

<div ng-app="SampleApp" ng-controller="MainCtrl">
    <svg>
        <g id="g_{{$index}}" ng-repeat="i in range" ng-cloak>
            <rect ng-x="{{i / 5}}" ng-y="{{i / 5}}" ng-width="{{i / 5}}" ng-height="{{i / 5}}"></rect>
        </g>
    </svg>
</div>

JS

angular.module('SampleApp', [], function() {})
    .directive('ngX', function() {
        return function(scope, elem, attrs) {
            attrs.$observe('ngX', function(x) {
                elem.attr('x', x);
            });
        };
    })
    .directive('ngY', function() {
        return function(scope, elem, attrs) {
            attrs.$observe('ngY', function(y) {
                elem.attr('y', y);
            });
        };
    })
    .directive('ngWidth', function() {
        return function(scope, elem, attrs) {
            attrs.$observe('ngWidth', function(width) {
                elem.attr('width', width);
            });
        };
    })
    .directive('ngHeight', function() {
        return function(scope, elem, attrs) {
            attrs.$observe('ngHeight', function(height) {
                elem.attr('height', height);
            });
        };
    });

function MainCtrl($scope) {
    $scope.range = [100, 200, 300];
}

Ответ 2

Комментарий Markus для использования позднего связывания лучше.

Т.е. префикс вашего атрибута с помощью "ng-attr-", "ng: attr:" или "ng_attr_", например:

<rect ng:attr:x="{{i / 5}}" ng:attr:y="{{i / 5}}" ng:attr:width="{{i / 5}}" nng:attr:height="{{i / 5}}"></rect>