I've coded a personalized directive to display tags like `<rect>
` and `<circle>
` within `<svg>
` elements.
My shapes model is simply an array of objects with different attributes such as `width
`, `height
`, and `stroke-width
.
These shapes are then visualized in an SVG element by my custom directive.
<svg height="400" width="600">
<dr-shape ng-repeat="shape in shapes"></dr-shape>
</svg>
The definition of the directive includes detailed comments:
app.directive('drShape', ['$compile', function($compile) {
// A function to add 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) {
// Function that creates a DOM node using `document.createElementNS`
// for appending it to an SVG element along with its attributes.
var shape = makeNode(scope.shape.tagName, element, attrs);
var elementShape = angular.element(shape);
// This section assigns the shape's attributes to the created node.
for (var attribute in scope.shape) {
if (isPublic(attribute, scope.shape[attribute])) {
bindNgAttr(elementShape, attribute);
}
}
// Add click listener to change fill color when clicking on a shape.
elementShape.on('click', function() {
console.log('Clicked in directive');
scope.shape.fill = '#bada55';
});
element.replaceWith(shape);
// Not sure what this does. Source: http://goo.gl/ZoYpQv
attrs.$observe('value', function(value) {
scope['value'] = parseInt(value, 10);
$compile(shape)(scope);
});
}
};
}]);
Rendering some shape data results in the following SVG output:
<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>
Check out this JSFiddle with the provided code.
Despite trying scope.$apply()
and scope.$digest()
in the click handler, I couldn't update the corresponding SVG DOM node upon manipulating the data in the shapes model. (EDIT: This statement is incorrect — refer to my response below.)
How can I ensure that changes made to the data reflect in the view?