3

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.

mlst
  • 2,688
  • 7
  • 27
  • 57

1 Answers1

2

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>
Tsepo Nkalai
  • 1,492
  • 11
  • 18
  • Sorry I couldn't make it work based on your answer. Could please create a codepen example that demonstrates your approach? (you can fork mine codepen example and change it) – mlst Nov 19 '19 at 17:08
  • @mlst I have updated my answer. What version of vue do you use, the linked version in your pen was very old, that's why you could not bind – Tsepo Nkalai Nov 19 '19 at 18:31
  • When I see your example I realize that I need to move gradient definitions to another svg element to prevent multiple gradiend id. Thank you. – hayatbiralem Oct 25 '20 at 11:37