6

How to return html formatted cell value?

I want to get custom formatted value (with html or other components) with <el-table-column> formatter.

<el-table :data="data">
  <el-table-column v-for="(column, index) in columns" 
                   :key="index" :label="column.label" 
                   :formatter="column.formatter">
  </el-table-column>
</el-table>
data() {
  return {
    columns: [
      {
        label: 'Created at',
        formatter: (row, col, value, index) => {
          return `<span class="date">${value}</span>`
        }
      },
      // edit:
      {
        label: 'Address',
        formatter: (row, col, value, index) => {
          return `<mini-map :data="${row}"></mini-map>`
        }
      }
      // other dynamic columns...
    ]
  }
}

But cell content is displayed as escaped html string. Is there any possibility to force html?

EPIC EDIT: I added an answer with a solution.

6 Answers 6

8

Ok, after a few hours of brainstorming I found pretty nice solution. I'm putting it here for others - I hope this helps somebody.

Value displayed in custom column can be:

  • simple string (prop)

  • formatted string (safe, without any html or components, via original formatter)

  • customized value (html, component, also safe!)

In order to achieve this, I had to create custom formatter components, which I put in folder i.e. /TableFormatters/

For this purpose, there is simple functional component DatetimeFormatter that display date-time with icon.

TableFormatters/DatetimeFormatter.vue

<template functional>
  <span>
    <i class="icon-date"></i> {{ props.row[props.column] }}
  </span>
</template>

<script>
  export default {
    name: 'DatetimeFormatter',
  }
</script>

Here come's columns configuration:

import DatetimeFormatter from './TableFormatters/DatetimeFormatter'
// ...
data() {
  return {
    data: [/*...*/],
    columns: [
      name: {
        label: 'Name',
      },
      state: {
        label: 'State',
        formatter: row => {
            return 'State: '+row.state__label
        }
      },
      created_at: {
        label: 'Created at',
        formatter: DatetimeFormatter
      }
    ]
  }
}

Now it's time to define <el-table>:

<el-table :data="data">
  <el-table-column 
    v-for="(column, index) in columns" 
    :key="index" 
    :label="columns[column] ? columns[column].label : column"
    :prop="column"
    :formatter="typeof columns[column].formatter === 'function' ? columns[column].formatter : null">
    <template #default="{row}" v-if="typeof columns[column].formatter !== 'function'">
      <div v-if="columns[column].formatter"
        :is="columns[column].formatter" 
        :row="row" 
        :column="column">
      </div>
      <template v-else>
        {{ row[column] }}
      </template>
    </template>
  </el-table-column>
</el-table>

This works like a charm. What's going on here with formatter? First we check if the formatter is set as a function. If so, the native <el-table-column> formatter takes the control, because <template #default={row}> will not be created. Otherwise formatter component will be created (via :is attribute). However, it there is no formatter, the plain value for a row will be shown.

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

3 Comments

This is working very well thank you. Is it possible to not use a functional component or does it have to? I've tried but it doesn't seem to work.
@dguay you can also use normal components as well, but you need directly define row and column props.
well I thought I tried that but you're right that works. Thanks !
1

If you want to render custom HTML for a <el-table-column>, you will need to make use of the custom column template functionality, rather than the :formatter prop. It's going to look something like this:

<el-table :data="data">
  <el-table-column
    v-for="(column, index) in columns" 
    :key="index"
    :label="column.label"
  >
    <template slot-scope="scope">
      <span class="date">{{ scope.row.value }}</span>
    </template>
  </el-table-column>
</el-table>

In the general case, you can make use of the v-html directive if you need to render unescaped HTML. There are some security implications involved, so make sure you understand those before reaching for v-html.

Essentially, it boils down to this: never use v-html to render content that was provided by the user.

2 Comments

Thanks, I used <template slot-scope="scope"> before with static table columns template. But what with more columns with custom formatters? *I have updated my question by adding more sample columns.
It's is better to use v-slot instead of slot-scope as it is deprecated, and it will even be nicer with destructuring: <template v-slot:default="{ row }")> or shorter <template #default="{ row }">
0

Use template slot scope to add html formatted columns

<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui/lib/index.js"></script>
<div id="app">
<template>
<el-table :data="tblData">
  <el-table-column prop="title"></el-table-column>
  <el-table-column prop="text">
    <template scope="scope">
      <span style="color:red;">{{scope.row.text}}</span>
    </template>
  </el-table-column>
</el-table>
</template>
</div>

var Main = {
  data() {
    return {
                tblData           : [
                    {title: 'title1', text:'text1'},
                    {title: 'title2', text:'text2'},
                    {title: 'title3', text:'text3'},
                ],
            }
  },
  methods : {

  }
}
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')

2 Comments

thanks, but I need full custom template (with html etc.) for each column. I added answer with solution that I found.
Thanks for the update, if my solutions helps you, you can upvote this answer as well
0

This also works for me:

<el-table
   :data="tenancy.renewals"
   stripe
   height="300"
   style="width: 100%">

   <el-table-column
     prop="term"
     label="Term"
     :formatter="formatTerm"
     width="180">
   </el-table-column>

   <el-table-column
     prop="started"
     label="Started"
     :formatter="formatColDate"
     width="180">
   </el-table-column>

   <el-table-column
     prop="expiry"
     :formatter="formatColDate"
     label="Expiry">
   </el-table-column>

   <el-table-column
     prop="amount"
     :formatter="formatAmount"
     label="Amount">
   </el-table-column>

 </el-table>

Then in your methods have methods corresponding to the formatter ones.

In my case, I already have mixins for numbers and dates:

...
      formatTerm (row, col, value, index) {
        return this.addSuffix(value, false)
      },

      formatColDate (row, col, value, index) {
        return this.formatDate(value)
      },

      formatAmount (row, col, value, index) {
        return this.formatMoney(value)
      },
...

1 Comment

That's nice, but it doesn't tell us if/how this displays html, which was the question ?
0

I feel headache, but it worked for me

<el-table :data="tableData" style="width: 100%">
              <el-table-column label="shortName" width="180">
                <template v-slot="scope">
                  <span v-html="scope ? scope.row.shortName : ''"></span>
                </template>
              </el-table-column>
...

Comments

0
import { h } from 'vue'    
 formatter(row, column, cellValue, index) {
    return h('span', {class : 'bg-cyan-400 p-2 rounded' } ,cellValue)
}, 

My solution here

1 Comment

Thank you for contributing to the Stack Overflow community. This may be a correct answer, but it’d be really useful to provide additional explanation of your code so developers can understand your reasoning. This is especially useful for new developers who aren’t as familiar with the syntax or struggling to understand the concepts. Would you kindly edit your answer to include additional details for the benefit of the community?

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.