4

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?

1 Answer 1

9

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>

Sign up to request clarification or add additional context in comments.

1 Comment

This worked like a charm! Thanks! I will edit my original question that had a typo. Should be "contents" instead "content" as I typed. I've tried the domProps approach as well but wasn't working because of this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.