Element UI, passing id to table column with checkbox - vue.js

Is there a way to pass id to el-table-column with selection type? I've tried passing a slot, but then checkbox is not rendering. Here is the code:
<el-table-column>
<template slot-scope="scope" v-if="scope.row">
<div :id="`column-${scope.row.name}`">{{ `reports-${scope.row.name}` }}</div>
</template>
</el-table-column>
<el-table-column prop="selected" align="center" type="selection" class-name="checkbox-column">
</el-table-column>
First column getting id via scoped slot.

Instead of ID if you are okay with class then use cell-class-name
In your template
<el-table your-attrs ... :cell-class-name="cellClassName">
In your script
methods: {
cellClassName({row, column, rowIndex, columnIndex}) {
if (columnIndex === 1) return `checkbox-${rowIndex}`;
}
}

Related

b-table cell cliked event missing

I have to pass cell value to the function clicking on the cell. It can be done easily on good table, but I am trying to find the way do it on bootstrap-vue b-table.
table:
<b-table
striped
responsive
class="mb-0"
:items="permissionsData"
>
<template #cell(module)="data">
{{ data.value }}
</template>
<template
#cell()="data"
>
<b-form-checkbox
:checked="data.value"
/>
</template>
</b-table>
and the method:
methods: {
onPermissionClick(value) {
console.log("column name, row name and the value")
}
},
Is anyone know good way how to do that please?
<b-table
striped
responsive
class="mb-0"
:items="permissionsData"
>
<template #cell(module)="data">
<div #click="onPermissionClick">
{{ data.value }}
</div>
</template>
</b-table>
You will get all property in the data. but here I am mentioning cell, row, and column as per your question. other property you can see by console.log(data)
methods: {
onPermissionClick(data) {
console.log(data.value) // cell value
console.log(data.item) // row
console.log(data.field) // column
}
},

Vue: can slot data go back to parent to pass down a prop?

I have my parent component using a MyGrid component and slotting in data. Inside MyGrid, I am checking the item type and if it is a certain type, I am giving it a class typeSquare.
In the interest of keeping MyGrid "dumb", is there a way I can check the type and then have MyGrid pass in a prop for the class?
Parent
<MyGrid
:items="items"
columnGap="12"
rowGap="14"
>
<template v-slot="slotProps">
<Cover
v-if="slotProps.typename === 'NewOne'"
:project="slotProps.item.data"
/>
<Cover2 v-else-if="slotProps.typename != 'NewOne'" :project="slotProps.item.data"/>
</template>
</MyGrid>
MyGrid.vue
<template>
<div :class="$style.root">
<div :class="$style.gridContainer">
<template v-for="(item, index) in items">
<div
:key="index"
:class="[{ [$style.gridItem]: true }, { [$style.typeSquare]: item.typename === 'NewOne' }]"
:style="{
padding: `${columnGap}px ${rowGap}px`,
}"
>
<slot :item="item"></slot>
</div>
</template>
</div>
</div>
</template>
If your goal is to only pass items that are of a certain criteria you can filter them out using computed properties. In your script add computed object like so
computed:{
filteredItems(){
return this.items.filter(item=>item.typename === 'NewOne')
}
},
And after in your template you can pass computed property instead of the whole list
<MyGrid
:items="filteredItems"
columnGap="12"
rowGap="14"
>

Vuetify data table - manually display expandable rows

I have a bunch of dynamic columns in a v-data table which I need to loop through and interrogate in order to display the correct info. It looks a bit like this (taken from the answer here: Vuetify format cell based on item type)
<v-data-table :item="items" ... >
<template v-for="header in headers" v-slot:[`item.${header.value}`]="{ item } ">
<template v-if="header.type === 'foo'">
<span style="color: red;">{{ item[header.value] }}</span>
</template>
<template v-else-if="header.value === 'data-table-expand'">
???
</template>
<template v-else>
{{ item[header.value] }}
</template>
</template>
</v-data-table>
Since I need the v-if statement, all other types default to the v-else. However, the v-else is not suitable for when a type is an expandable row. It will display a blank value for that column. So I created a v-else-if template to be able to capture the expandable row column and correctly render it to the screen.
The problem is that I don't know what to put in the template to indicate it's a column with expandable rows (https://vuetifyjs.com/en/components/data-tables/#expandable-rows). In other words it does not render the carat icon that shrinks/expands the subtable, nor does it render the clickable actions. How would I modify the v-else-if template to correctly render its contents?
I came up with a workaround using computed properties.
Instead of using
v-for="header in headers"
I changed it to a computed headers which is filtered.
<template v-for="header in headersIWant" v-slot:[`item.${header.value}`]="{ item } ">
<span style="color: red;">{{ item[header.value] }}</span>
</template>
...
computed: {
headersIWant() {
return this.headers.filter(x => x.type === 'foo');
}
}

Vue element-ui <el-table-column> formatter – how to display html?

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.
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.
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.
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')
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)
},
...
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>
...

How to combine props arithmetically in Vue.js?

I have a table that looks like this:
<el-table
:data="info"
class="myform"
stripe
style="width: 100%">
<el-table-column
prop="location_name"
label="Name"
width="180">
</el-table-column>
<el-table-column
v-if="promoActive"
prop="original_price"
label="Original Price"
width="180">
</el-table-column>
<el-table-column
prop="spaces_available"
label="Spaces"
width="180">
</el-table-column>
<el-table-column
v-if="promoActive"
prop="discount_value"
label="Discount Value"
width="180">
</el-table-column>
</el-table-column>
<el-table-column
v-if="promoActive"
label="Final Price"
width="180">
</el-table-column>
</el-table>
I'm trying to make the prop value of the last table column equal to the the final price which is the original price minus the discount price. However, when I put the prop like this, it doesn't work:
<el-table-column
v-if="promoActive"
prop="(original_price - discount_value)"
label="Final Price"
width="180">
</el-table-column>
What's the best way to go about this?
you can use computed property like
computed:{
calculate(){
return this.prop1 - this.prop2;
}
}
And then called the prop
Have you considered computed properties in Vue JS ?
refer : [https://v2.vuejs.org/v2/guide/computed.html#Computed-Properties]
these properties are derived from the data and change whenever the source data is modified. This could be the recommended approach.
example :
computed:{
finalPrize : function () {
return this.original_price - this.discount_value;
}
}
First and foremost, the reason why your prop="(original_price - discount_value)" did not work is because prop attribute is evaluated as text unless you use the v-bind syntax. So this should work:
v-bind:prop="(original_price - discount_value)"
However, this is what computed properties in VueJS can be used for, to abstract all these calculation logic away from your template: you can perform the calculations in there, and simply use the v-bind directive to bind the computed value to the element. Let's name your computed property finalPrice. In your VueJS app/component code, you can do this:
computed: {
finalPrice() {
return this.original_price - this.discount_value;
}
}
Then, update your markup to use v-bind:prop="finalPrice":
<el-table-column
v-if="promoActive"
v-bind:prop="finalPrice"
label="Final Price"
width="180">
</el-table-column>
p/s: If you prefer to use shorthands, fret not: :prop="finalPrice" (note the : prefix) is syntactically identical to v-bind:prop="finalPrice".
Not recommended, but still workable: if you want to pass the arguments to perform calculations instead, you can use a method instead:
method: {
calculateFinalPrice(original, discount) {
return original- discount;
}
}
And your markup will be:
<el-table-column
v-if="promoActive"
v-bind:prop="calculateFinalPrice(original_price, discount_value)"
label="Final Price"
width="180">
</el-table-column>
You can use computed property for this type of arithmetic calculations. where you can define a function and have full control over all properties of component.
follow this link. you will get idea how you can do it.