Vuetify use v-html inside data table cell - javascript

I've an object with values what come from server with HTML tags like span,p, and I'm using vuetify data table to display this values, the problem is what I don't know how to render this HTML in table cell I know there is v-html directive, but how to use this directive with vuetify.
I found an example that modify values from specific columns:
<v-data-table
:headers="headers"
:items="rows"
:search="search"
dense>
<template v-slot:item.name="{ item }">
<span v-html="item.name"></span>
</template>
</v-data-table>
Here name is just one column, how can I use this example for each columns?

You could do that dynamically by looping through headers values :
<v-data-table
:headers="headers"
:items="rows"
:search="search"
dense>
<template v-for="header in headers" v-slot:item[header.value]="{ item }">
<div v-html="item[header.value]"></div>
</template>
</v-data-table>

When I tried a solution with slots (see above), ESLint trows an error: "An element cannot have multiple <template> elements which are distributed to the same slot". To solve this problem, I used the v-simple-table instead of v-data-table and used v-html to render table data (see codepen). But be aware that v-html may be used only in cases when the table data are not modified by users, otherwise it's a possibility for an XSS attack.
<v-simple-table>
<template v-slot:default>
<thead>
<tr>
<th v-for="header in headers" :key="header.value" class="text-left">
{{ header.value }}
</th>
</tr>
</thead>
<tbody>
<tr
v-for="item in desserts"
:key="item.name"
>
<td v-for="header in headers" :key="header.value" v-html="item[header.value]"></td>
</tr>
</tbody>
</template>
</v-simple-table>

Related

How customizable is vuetify data tables?

I am trying to achieve the above result. I have already have the table working in my vue project.
This piece of code is the template for the datatable from Vuetify.
<v-card>
<v-card-title>
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
hide-details
></v-text-field>
</v-card-title>
<v-data-table
:headers="headers"
:items="tableData"
:search="search"
></v-data-table>
</v-card>
Populating the table with basic data is just fine and I've already done that, but I need add that "Send again" link/button, and hook it up with a method. Or any other customization for that matter, like the yellow bullet point. Is this going to be super hard to do ?
To do this, we can use item or item.<name> Slots, you can read more about it in data table API https://vuetifyjs.com/en/api/v-data-table/#slots
your code can be something like this:
<v-data-table
:headers="headers"
:items="desserts"
class="elevation-1"
>
<template v-slot:item.status="{ item }">
<template v-if="item.status == `registered`">
{{ item.status }}
</template>
<template v-else>
{{ item.status }} - <v-btn #click="AddSendBtnMethod(item.id)"> send again<v-btn>
</template>
</template>
</v-data-table>
Here is a sample from Vuetify, changed to show how its works

Vue dynamic components not re rendering inside v-for loop when data changes

I have dynamic table component where I can modify columns (ordering, adding new, removing columns). Table body looks like this:
<tr v-for="row in data" :key="row.id">
<td v-for="column in columns" :key="column.slug">
<component
v-if="column.component"
:is="column.component"
v-bind="{ row: row, countries: row.reach }"
/>
<template v-else>
{{ row[column.slug] }}
</template>
</td>
</tr>
Everything works well except when I edit data (add new column or rearrange the order) then every component inside table body disappears and is not rendered. I tried to attach unique :key to <component> but was not able to make it work. When I inspect table body in Vue devtools I can see that components are inside just not rendered into DOM. When inspecting table body in chrome devtools I can see only <!--function(e,n,r,o){return dn(t,e,n,r,o,!0)}--> in place where component should be rendered. Any ideas what I might be doing wrong here?
You are using duplicated keys. You should provide unique keys for all v-for elements inside the whole component. Try this (see key for every column):
<tr v-for="row in data" :key="row.id">
<td v-for="column in columns" :key="`${row.id}/${column.slug}`">
<component
v-if="column.component"
:is="column.component"
v-bind="{ row: row, countries: row.reach }"
/>
<template v-else>
{{ row[column.slug] }}
</template>
</td>
</tr>
Also when you are in development it is highly recommended to use Vue development mode. It will highlight errors like this.
Turns out I was passing Component object into :is directive instead of string with Component name. That's why it was not rendered properly.

How to render a vue component with TR as the root element?

I don't have much experience with Vue and facing an issue with redering a component with root as TR element.
I read this in the docs https://v2.vuejs.org/v2/guide/components.html#DOM-Template-Parsing-Caveats and tried adding the component using is property, but that didn't help either.
Check the code below.
Vue.component('car',{
props: ['number']
})
new Vue({
el: "#app"
})
<div id="app">
<table>
<tr is='car' inline-template number='123'>
<tr>
<td>car no</td>
<td>{{number}}</td>
</tr>
</tr>
<tr is='car' inline-template number='456'>
<tr>
<td>car no</td>
<td>{{number}}</td>
</tr>
</tr>
</table>
</div>
This errors out:
Error compiling template:
Inline-template components must have exactly one child element.
How can I fix this? Appreciate the help.
Fiddle https://jsfiddle.net/8d65gvua/
Update ----
More findings
If i wrap my tr in a template it works
<tr is='car' inline-template number='456'>
<template>
<tr>
<td>car no</td>
<td>{{number}}</td>
</tr>
<template>
</tr>
I have no clue why. Shouldn't the issue with tr be fixed with using the is property? Why do we need to do this?
I've never used inline-template before, my guess is that you are using it to pass the number to your "inner template"? You can achieve the same thing by using slots. (Reference)
I did a test but using slots instead, which I think it's a better way to cater for passing "inner templates". Also given the below warning in the documentation with regards to inline-template I would opt for slots.
However, inline-template makes the scope of your templates harder to
reason about. As a best practice, prefer defining templates inside the
component using the template option or in a <template> element in a
.vue file.
Example
Vue.component('my-row', {
template: `
<tr>
<slot></slot>
</tr>
`
})
The above is a component to define a single table row. The <slot> element is basically a placeholder for any content you want to pass into this template.
Usage
<div id="app">
<table>
<tr
is="my-row"
v-for="todo in todos"
>
<td>{{ todo.text }}</td>
</tr>
</table>
</div>
What will happen here is that <td>{{ todo.text}}</td> will be placed instead of the <slot> element in the my-row component. You can have multiple <td> elements, whatever content you put in the my-row will appear instead of <slot>.
JS Fiddle: https://jsfiddle.net/x793nub6
PS: Slots!
I don't want to make the answer too long, but keep in mind that with slots you can have a super customisable components, of course this all depends on your needs. But I would suggest having a look at the documentation and trying out a few expirements.
Update - If you still want to use inline-template
I went through the JS Fiddle you shared once more and realised what's the issue you are having. Basically vue is saying that one child needs to be passed. I guess there's some mixup since there's a <tr> nested in another <tr>. To fix it wrap the inner <tr> with a <template> element.
From:
<tr is='car' inline-template number='123'>
<tr>
<td>car no</td>
<td>{{number}}</td>
</tr>
</tr>
To:
<tr is='car' inline-template number='456'
<template>
<tr>
<td>car no</td>
<td>{{number}}</td>
</tr>
</template>
</tr>

How to add tooltip to datatable header in vuetify?

In older version on vuetify you could access headerCell slot and easily add tooltips - see https://codepen.io/nueko/pen/dZoXeZ
In the latest version you have named slots, so you need to know header name before
<template v-slot:header.givenname="{ header }">
Is there a way to add a tooltip to all headers?
There are 2 ways to achieve this.
Option 1: Customizing whole table row
If you need to customize whole row element inside table heading this might be useful. Even though I would not recommend to follow this way if you don't want to loose sorting functionality which exist by default in v-data-table.
Example:
<template v-slot:header="{ props: { headers } }">
<thead>
<tr>
<th v-for="h in headers">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<span v-on="on">{{h.text}}</span>
</template>
<span>{{h.text}}</span>
</v-tooltip>
</th>
</tr>
</thead>
</template>
Working pen: https://codepen.io/onelly/pen/QWWmpZN
Option 2: Customizing each heading without losing sorting functionality
I guess this is more like what you are trying to do and the replacement for the old way. You can loop <template v-slot:header> and use Dynamic Slot Names to accomplish this. Syntax for Dynamic slot name looks like this <template v-slot:[dynamicSlotName]>.
Example:
<template v-for="h in headers" v-slot:[`header.${h.value}`]="{ header }">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<span v-on="on">{{h.text}}</span>
</template>
<span>{{h.text}}</span>
</v-tooltip>
</template>
Working pen: https://codepen.io/onelly/pen/ExxEmWd

Complex headers in angular2-data-table

I'm trying to create a table with a complex header (multi-line header) like this:
using the angular-2-data-table library found here:
https://github.com/swimlane/angular2-data-table
They have something called "Expressive cell templates" in which you can define columns like this:
<datatable
class="material"
[rows]="rows"
[options]="options">
<datatable-column name="Name">
<template let-column="column">
Hi + {{column.name}}
</template>
<template let-value="value">
Hi: <strong>{{value}}</strong>
</template>
</datatable-column>
<datatable-column name="Gender">
<template let-row="row" let-value="value">
My name is: <i [innerHTML]="row['name']"></i> and <i>{{value}}</i>
<div>{{joke}}</div>
</template>
</datatable-column>
</datatable>
plnkr here
But it seems like the column name is always defined as text, and it's unclear how you would have nested column names. Any ideas if complex headers are possible in angular2-data-table? It's possible in regular datatables (https://datatables.net/examples/advanced_init/complex_header.html).
Thanks in advance!

Categories

Resources