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.

Daniel
  • 2,621
  • 2
  • 18
  • 34

5 Answers5

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.

Daniel
  • 2,621
  • 2
  • 18
  • 34
  • 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 Oct 22 '21 at 14:57
  • @dguay you can also use normal components as well, but you need directly define `row` and `column` props. – Daniel Oct 24 '21 at 17:38
  • well I thought I tried that but you're right that works. Thanks ! – dguay Oct 25 '21 at 14:33
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.

tony19
  • 125,647
  • 18
  • 229
  • 307
Vince
  • 3,207
  • 1
  • 17
  • 28
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')
chans
  • 5,104
  • 16
  • 43
  • thanks, but I need full custom template (with html etc.) for each column. I added answer with solution that I found. – Daniel Oct 11 '19 at 17:10
  • Thanks for the update, if my solutions helps you, you can upvote this answer as well – chans Oct 11 '19 at 17:12
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)
      },
...
Anthony
  • 487
  • 1
  • 6
  • 21
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>
...
Youth overturn
  • 341
  • 5
  • 7