skip to Main Content

Greetings fellow developers!

Using Vue 3, composition API with setup.

I am working on a custom component "Table" to render… a table. It expects a prop called "data", which is an array of objects (as rows) with such params: title, data and a render() function. Now with render() I want to have an ability to render a custom HTML inside any of the columns if needed, like showing multiple data, custom text, custom components etc. And custom components is where I got stuck.

What I have now.

Inside Table component I have a <tr v-for> which goes over the props.data and renders the columns. If it sees a render() function, it passes row data to that render() function, which should render the data. Render() function example:

render(row) => {
   return `This is some random text and: <my-custom-component>${row.someData}</my-custom-component`;
}

And here is part of Table component:

<tr v-for="group in tableData">
  <template v-for="column in props.columns">
     <td v-if="column.render">
         *** here I want to render whats inside render() function, including custom component ***
     </td>
     <td v-else :style="{ width: column.width ?? 'auto' }" v-html="group[column.data]"></td>
  </template>
</tr>

This part does not work. I receive a Vue warning "Failed to resolve component". I also tried to use Vue dynamic component:

<component is="myCustomComponent"></component>

But then it renders pure HTML like that:

<mycustomcomponent></mycustomcomponent>

I tried searching for answers, but I dont think I found one. I saw many examples with using h() function, but I cant find a way to add it to my code.

It should also be noted that I am loading the data into the table after I receive it from the API.

So what am I missing? Is this even possible? I am pretty new to Vue, so very likely that I am not familiar with some concept.

2

Answers


  1. Chosen as BEST ANSWER

    Ok. So following the recommendations in comments and answers, I found an article/tutorial regarding dynamic slots, this is something totally new I did not know was possible.

    The article is here on dev.to: Building a Table Component with Dynamic Slot Names in Vue 3

    And so this is how the Table component tr v-for element looks like:

    <tr v-for="group in tableData">
        <td v-for="column in props.columns">
             <slot :name="`cell(${column.data})`" :value="group[column.data]" :item="group">
                  {{ group[column.data] }}
             </slot>
        </td>
    </tr>
    

    And here is an example usage:

    <Table :columns="tableColumns" :data="reportList">
        <template #cell(fileName)="{ value, item }">
              <a href="{{ value }}"> {{ value }}</a>
              <my-custom-element>Random content</my-custom-element>
        </template>
    </Table>
    

    This allows for completely dynamic content to be used in any column I want. The article itself also shows other possible deeper customizations that this approach allows to implement.


  2. UPDATE

    You cannot simply render html in a text string to html using h().

    The result will be the same as when you just put the text string in Mustaches {{}} inside the template.

    Check the playground.

    const { createApp, h } = Vue;
    
    const MyCustomComp = {
      props: ['content'],
      setup(props) {
        return () => 
          h('div', null, props.content)
       }
    }
    
    const App = { components: { MyCustomComp } }
    const app = createApp(App)
    app.mount('#app')
    #app { line-height: 2; }
    [v-cloak] { display: none; }
    <div id="app" v-cloak>
      <my-custom-comp :content="'<h1>Some Content</h1>'"></my-custom-comp>
      {{ '<h2>Some Other Content</h2>' }}
      <div v-html="'<h3>v-html does not work wuth custom components:</h3><MyCustomComp /><my-custom-comp></my-custom-comp>'"></div>
    </div>
    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>

    You can use v-html to interpolate the standard HTML, but it will not resolve the custom components. Check the div with v-html.

    I would suggest you to consider using slost for your task. It will be much simpler.

    Or think about changing your design. It could possibly be done simpler.

    Otherwise you will need to parse yourself dynamic content from the text string and render it with the h() function, which is really very challenging task.

    Check my answers on the thema Vue Render Functions:


    Sure it’s possible and I bet, you don’t really need to use a render() function for your scenario.

    Usually, if you have an array with data objects, you can just iterate over this array.

    Like this

    <tr v-for="row in data">
       <td>
          This is some random text and: 
          <my-custom-component>{{row.someData}}</my-custom-component>
       </td>
    </tr>
    

    If you component is not rendered and you see the component tag in the html, like in your case <mycustomcomponent></mycustomcomponent> then it wasn’t properly registered or was not found.

    Also, Vue normally does not render components, when you pass them in a text string over props or some other way. Check on v-html directive and security risks caused by using Raw HTML in templates.

    So, if you want to render things dynamically, then you will need to use Render Function APIs.

    But again, in your case I don’t think it’s necessary.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search