mlst
mlst

Reputation: 3411

Is it possible to bind SVG fill attribute that uses url() in VueJS?

I'd need to dynamically bind fill attribute of a SVG rect element but it doesn't work. Here is my simple VueJS component to demonstrate what I am trying to do (also available in codepen)

<template>
<div>
  <!-- this works but id and fill attributes are hardcoded -->
  <svg class="green" width="100" height="50" version="1.1" xmlns="http://www.w3.org/2000/svg">
    <linearGradient id="gradient1">
      <stop stop-color="red" offset="0%" />
      <stop stop-color="blue" offset="100%" />
    </linearGradient>
    <rect width="100" height="50" fill="url(#gradient1)" />
  </svg>

  <!-- this doesn't work... -->
  <svg class="green" width="100" height="50" version="1.1" xmlns="http://www.w3.org/2000/svg">
    <linearGradient :id="myid">
      <stop stop-color="red" offset="0%" />
      <stop stop-color="blue" offset="100%" />
    </linearGradient>

    <rect width="100" height="50" :fill="myfill" />
  </svg>
</div>
</template>

<script>
new Vue({
  el: 'body',
  data: {
    title: 'Vuejs SVG binding example',
    myid: 'gradient2',
    myfill: 'url(#gradient2)'
  },
});
</script>

Notice that fill attribute uses url() which takes element id as argument which complicates the thing. As far as I know the only way for fill attribute to use linearGradient defined in the same component is to reference it by element id attribute.

The reason I am trying to do this is because I want to avoid hardcoding ids inside components. Since I will have many instance of this component on the webpage, there will be multiple elements with same id value which should not happen.

Upvotes: 3

Views: 2070

Answers (1)

Tsepo Nkalai
Tsepo Nkalai

Reputation: 1512

Yes it is possible to do what want to do. I have done something a bit similar but with just a div not and not svg.

The theory

Bind a dynamic css class name to the svg and and put the fill in that class. this link shows how to us css for a css CSS - Style svg fill with class name

My proposed solution assumes are passing in some value to the component via props

The solution

<template>
...

    <rect width="100" height="50" :class="myfill" />
...
</template>

<script>
new Vue({
  el: 'body',
  data: {
    title: 'Vuejs SVG binding example',
    myid: 'gradient2',
    myfill: somePropYouPassedIn
  },
});
</script>

Modification To First Answer

On trying out your fiddle, I think your were doing the coding correctly, you were just using an old version of Vuejs (this I notice when I was trying to get your fiddle to work). Anyway, I could not get your pen to work with vue so I created a totally new fiddle here https://jsfiddle.net/nkalait/ohmzxb7L/

Code

<div>
  {{ title }}
  <!-- this works but id and fill attributes are hardcoded -->
  <svg class="green" width="100" height="50" version="1.1" xmlns="http://www.w3.org/2000/svg">
    <rect width="100" height="50" :fill="gradient1" />
  </svg>

  <!-- this doesn't work... -->
  <svg class="green" width="100" height="50" version="1.1" xmlns="http://www.w3.org/2000/svg">
    <rect width="100" height="50" :fill="gradient2" />
  </svg>


  // I HAVE PUT THE GRADIENTS IN THERE OWN SVG
  <svg aria-hidden="true" focusable="false" style="width:0;height:0;position:absolute;">
    <linearGradient id="gradient1">
      <stop stop-color="yellow" offset="0%" />
      <stop stop-color="blue" offset="100%" />
    </linearGradient>
    <linearGradient id="gradient2">
        <stop stop-color="red" offset="0%" />
        <stop stop-color="green" offset="100%" />
    </linearGradient>
  </svg>
</div>

Upvotes: 2

Related Questions