Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create VNodes from a string with html tags in Vue 2.5?

I'm trying to create a functional component that renders the Feather Icons package but I'm not able to figure out the last step. Here's what I got:

This is my FeatherIcon.vue component.

<script>
const feather = require("feather-icons");

export default {
  components: {},
  props: {
    type: {
      required: true,
      type: String
    }
  },
  mounted() {},
  render(createElement) {
    return createElement(
      "svg",
      {attrs: feather.icons[this.type].attrs },
      feather.icons[this.type].contents
    );
  }
};
</script>

The 3rd argument according to Vue docs says that it should be either:

// {String | Array}
// Children VNodes, built using `createElement()`,
// or using strings to get 'text VNodes'. Optional.

However, the result of my 3rd argument feather.icon[this.type].contents is a string containing the "innerHTML" inside the svg tag:

"<line x1="6" y1="3" x2="6" y2="15"></line><circle cx="18" cy="6" r="3"></circle><circle cx="6" cy="18" r="3"></circle><path d="M18 9a9 9 0 0 1-9 9"></path>"

So my question is, how to convert feather.icon[this.type].contents into a set of VNodes?

I've tried with DOMParser and using parseFromString but no luck. Any idea?

like image 553
Richard-MX Avatar asked Jan 28 '23 19:01

Richard-MX


1 Answers

You can use the domProps property of the data object.

This object also allows you to bind normal HTML attributes as well as DOM properties such as innerHTML (this would replace the v-html directive)

Here is an example.

console.clear()

const content = `
  <line x1="6" y1="3" x2="6" y2="15"></line>
  <circle cx="18" cy="6" r="3"></circle>
  <circle cx="6" cy="18" r="3"></circle>
  <path d="M18 9a9 9 0 0 1-9 9"></path>
`

new Vue({
  el: "#app",
  render(h){
    return h("svg", {domProps:{innerHTML: content}})
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app"></div>

In your case, you would just set innerHTML to the content from feathers.

Also, as a side note, if you don't have a template, generally you just use a straight javascript file. You don't need a single file component. So your component would be

FeatherIcon.js

const feather = require("feather-icons");

export default {
  props: {
    type: {
      required: true,
      type: String
    }
  },
  render(createElement) {
    return createElement(
      "svg",
      {
        attrs: feather.icons[this.type].attrs,
        domProps: {
          innerHTML: feather.icons[this.type].content
        }
      });
  }
};

Finally, you mentioned wanted to make this a functional component, in which case, you could just do something like this:

const FeatherIcon = {
  functional: true,
  props: {
    type: {
      required: true,
      type: String
    }
  },
  render(h, context){
    const {contents, attrs} = feather.icons[context.props.type]
    return h("svg", {attrs, domProps: {innerHTML: contents}})
  }
};

Here is an example of that working.

console.clear()

const FeatherIcon = {
  functional: true,
  props: {
    type: {
      required: true,
      type: String
    }
  },
  render(h, context){
    const {contents, attrs} = feather.icons[context.props.type]
    return h("svg", {attrs, domProps: {innerHTML: contents}})
  }
};

new Vue({
  el: "#app",
  components: {
    FeatherIcon
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<script src="https://unpkg.com/feather-icons"></script>
<div id="app">
  <feather-icon type="eye"></feather-icon>
  <feather-icon type="activity"></feather-icon>
  <feather-icon type="award"></feather-icon>
</div>
like image 176
Bert Avatar answered Jan 30 '23 07:01

Bert