Svg binding and limitations


#1

Due to the valid markup of the binding syntax, aurelia is well suited to binding to svg. We’ve created some modular visualisation components using aurelia so have quite a bit of experience trying it out.

Firstly, we found out very quickly that binding to large path strings was very slow:

<path d.one-way="humongousPathString" ... ></path>

Aurelia bindings should obviously not be used to pass huge primitive types, so it was considerably faster to simply set the attribute manually:

this.pathElement.setAttribute('d', newPath);

svg in component template causes a few issues

Aurelia svg components have to be wrapped in an svg element:

<template>
   <svg>
    ... component here
   </svg>
</template>

To get anything complex to display correctly, you’ll probably need to include the following CSS. This is because the default for the non-root svg is to hide the overflow:

svg {
  overflow: visible !important;
}

We spent ages trying to work out why the above approach was not working when we exported to png. It turns out you have to inline styles in every component for that to work. That gives us:

<template>
  <svg style="overflow: visible">

  </svg>
</template>

The nestled svg element also causes issues if we want to use a clipPath from a parent component as they don’t appear to work in the child svg. This means we currently have to copy the clip dimensions into some components and render the <clipPath> within it:

<template>
  <svg style="overflow: visible">
    <clipPath id="clip">
      <rect id="clip-rect" x.bind="rect.x" y.bind="rect.y" width.bind="rect.width" height.bind="rect.height"></rect>
    </clipPath>

    <line clip-path="url(#clip)" stroke="#000" ...
  </svg>
</template>

Update

See below for a possible solution


Composed SVG elements missing attributes
#2

The overflow issue seems like a browser bug. Did you test in multiple browsers?


#3

The default in Chrome is to hide the overflow:
image

So we have no choice but to include it inline if we want it to display correctly when converting to png.


#4

I’m guessing that the wrapping <svg> in the templates is so that the elements are created with the correct namespace?

It looks like it’s possible to remove the wrapping svg using a custom attribute which simply copies the elements out when it’s attached. This fixes all the above issues.

<template>
  <svg svg-remove>
    <rect x.bind="rect.x" y.bind="rect.y" width.bind="rect.width" height.bind="rect.height" style="background-color:red"></rect>
  </svg>
</template>
import {inject, customAttribute} from 'aurelia-framework';

@customAttribute('svg-remove')
@inject(Element)
export class SvgRemove {
  constructor(element){
    this.element = element;
  }
  
  attached(){
    for(var i = 0; i< this.element.children.length; i++){
      this.element.parentNode.appendChild(this.element.children[i])
    }
    this.element.remove();
  }
}

#5

I’ve been trying out my svg-remove attribute with some of our more complicated visualisations.

Unfortunately, the DOM manipulation the attribute does on attached breaks aurelias binding to the elements and they’re not removed properly when the contents of a repeat.for change.