Render custom header in element-ui to show bootstrap tooltip? - vue.js

I try to render a bootstrap tooltip (https://bootstrap-vue.js.org/docs/components/tooltip/) within a header of an element-ui table.
I use the custom header render method:
<el-table-column
v-for="(column, index) in headers"
:render-header="renderHeader"
/>
The following code renders as expected except of the id which is missing. For debugging purposes I added a class which renders as expected:
renderHeader(h, { column }) {
return h(
'div',
{
id: `tooltip-target__${column.property}`,
class: 'some-class'
},
[
column.label,
h(
'b-tooltip',
{
attrs: {
triggers: 'hover',
target: `tooltip-target__${column.property}`
}
},
column.label
)
]
);
},

The id needs to be defined inside the attrs object:
renderHeader(h, { column }) {
return h(
'div',
{
attrs: {
id: `tooltip-target__${column.property}`
}
class: 'some-class'
},
[
column.label,
h(
'b-tooltip',
{
attrs: {
triggers: 'hover',
target: `tooltip-target__${column.property}`
}
},
column.label
)
]
);
},
I found a good example in the documentation:
https://v2.vuejs.org/v2/guide/render-function.html#The-Data-Object-In-Depth

Related

How to make nested properties reactive in Vue

I hava a component with complex nested props:
<template>
<div>
<a-tree :tree-data="data" :selected-keys="[selectedKey]" #select="onSelect" />
<div>
<a-input v-model="sheet[selectedKey].tableName" />
<ux-grid ref="previewTable">
<ux-table-column v-for="field in sheet[selectedKey].fields"
:key="field.name" :field="field.name">
<a-input slot="header" v-model="field.label" />
</ux-table-column>
</ux-grid>
</div>
</div>
</template>
<script>
export default {
props: {
previewData: { type: Array, default: () => [] }
},
data () {
return {
data: this.previewData,
selectedKey: '0-0-0',
sheet: { 'none': { tableName: null, fields: [] } }
}
},
created () {
this.data.forEach((file, fid) => {
file.sheets.forEach((sheet, sid) => {
this.$set(this.sheet, `0-${fid}-${sid}`, {
tableName: sheet.label,
fields: sheet.fields.map(field => ({ ...field }))
})
})
})
},
mounted () {
this.$refs.previewTable.reloadData(this.data[0].sheets[0].data)
},
methods: {
onSelect ([ key ], { node }) {
if (key !== undefined && 'fields' in node.dataRef) {
this.selectedKey = key
this.$refs.previewTable.reloadData(node.dataRef.data)
} else {
this.selectedKey = 'none'
this.$refs.previewTable.reloadData()
}
}
}
}
</script>
And previewData props is something like:
{
name: "example.xlsx",
filename: "80b8519f-f7f1-4524-9d63-a8b6c92152b8.xlsx",
sheets: [{
name: "example",
label: "example",
fields:[
{ label: "col1", name: "col1", type: "NUMBER" },
{ label: "col2", name: "col2", type: "STRING" }
]
}]
}
</script>
This component allows user to edit the label properties. I have to make Object sheet reactive to user input, and I tried $set and Object.assign, it works for sheets.label but fields[].label is still not reactive.
I wish to know what would be the declarative (and optimal) solution for it
You might need a watcher or computed property in React for previewData to be changed.

Mdb datatable does not rendering data in Vue.js

I'm trying to implement a datatable with mdbootstrap in vue.js.
I would like to update table data on events and when initialized but it does not work.
Template;
<div class="col-md-12">
<mdb-datatable
:data="data"
striped
bordered
/>
</div>
Script;
import { mdbDatatable } from 'mdbvue';
export default {
name: 'userManagement',
components: {
mdbDatatable
},
data() {
return {
className:"",
classList: [],
data: {
columns: [
{
label: 'Name',
field: 'className',
sort: 'asc'
}, {
label: 'ID',
field: 'id',
sort: 'asc'
}
],
rows: [
{
className: 'Tiger Nixon',
id:1
},
{
className: 'Garrett Winters',
id:2
}
]
}
}
},
methods: {
getClassList(){
var _this = this;
this.$axios.get('my_url/admin/classes').then(function (response) {
if (response.status === 200) {
_this.data.rows = [];
response.data.forEach(function (obj) {
let item = {
className: obj.className,
id: obj.id
};
_this.data.rows.push(item);
});
}
}).catch(function (err) {
alert("" + err);
});
}
},
mounted(){
this.getClassList();
},
It always shows default values, I check the data rows from console the value seems to be updated but no change on the datatable.
Any help would be appreciated.
We've found the solution for Your issue.
The new code is available here: https://mdbootstrap.com/docs/vue/tables/datatables/#external-api
Also to make sure the data is reactive it's necessary to add the following code to the Datatable component in our package:
watch: {
data(newVal) {
this.columns = newVal.columns;
},
(...)
}
It will be fixed in the next MDB Vue release.
I installed mdbvue 5.5.0 which includes the change that mikolaj described. This caused the table columns to update when changed but in order to get the rows to update too I had to add to the watch method in Datatable.vue as follows:
watch: {
data(newVal) {
this.columns = newVal.columns;
this.rows = newVal.rows;
},

How to use render-header function in table-column in Element library?

I need to have a custom table header (title + svg + tooltip) for Element table.
I am trying to use the 'render-header' function without luck.
To be more specific - how to print the label + SVG (with tooltip on hover) for each column?
Html:
<el-table-column property="name" label="Indicator" :render-header="appendTip">
</el-table-column>
Script:
appendTip(h, { column }) {
return h(
"el-tooltip",
{
attrs: {
effect: "dark",
content: "Test",
placement: "top"
}
},
[h("el-button", ["Tooltip"])]
);
Thanks.
This is my solution:
appendTip(h, { column, $index }) {
// Function(h, { column, $index })
return h("span", [
column.label,
h(
"el-popover",
{
props: {
placement: "top",
// title: "hello",
// width: "200",
trigger: "hover",
content: this.test(column.label)
}
},
[
h(
"i",
{
slot: "reference",
class: "el-icon-info"
//style: "color:gray;font-size:16px;margin-left:10px;"
},
""
)
]
)
]);
I used this as a reference:
https://v2.vuejs.org/v2/guide/render-function.html
I have modified little bit for my requirements,
I do not need [i] button for popover, I needed popover on header text, so the syntax that worked for me is given below
appendTip: function (h, { column, $index }) {
var content = "Popover content"
if (column.property == "isrequired") {
content = "Value is Required";
}
else {
return column.label;
}
return h(
"el-popover",
{
props: {
placement: "top",
// title: "hello",
// width: "200",
trigger: "hover",
}
},
[
content,
h(
"span",
{
slot: "reference",
},
column.label
)
]
);
},
Thanks badigard for your answer, that helped me a lot.

How to trigger a Vue method by name for each element in a v-for loop?

I'm having troubles triggering a separate Vue instance method by name for each element in a v-for loop on click.
Each action corresponds to a method, but it's not triggered. What am I doing wrong?
Code:
<v-btn v-for="btn in windowControlButtons" :key="btn.id"
#click="btn.action"
>
<v-icon size="20px">{{btn.icon}}</v-icon>
</v-btn>
...
window: remote.getCurrentWindow(),
windowControlButtons: [
{
icon: 'remove',
action: minimizeWindow()
},
{
icon: 'crop_square',
action: maximizeWindow()
},
{
icon: 'close',
action: closeWindow()
}
]
...
methods: {
minimizeWindow() {
this.window.minimize()
},
maximizeWindow() {
this.window.maximize()
},
closeWindow() {
this.window.close()
}
}
UPDATE
I can trigger some code directly in the data(), e.g.:
...
{
icon: 'remove',
action: () => {remote.getCurrentWindow().minimize()}
},
But what if a method wasn't as short?
How do I trigger a method already specified in methods: { }?
btn.action is a string, thus you can't execute it.
Every Vue instance/component method is accessible as a property in the vm.$options.methods object.
I suggest creating another method, say handleClick, to simplify your method calling depending on the button, and invoke the best suitable method from this.$options.methods as shown below.
new Vue({
el: '#app',
data: {
windowControlButtons: [
{id: 1, icon: 'remove', action: 'minimizeWindow'},
{id: 2, icon: 'crop_square', action: 'maximizeWindow'},
{id: 3, icon: 'close', action: 'closeWindow'}
]
},
methods: {
handleClick(button) {
if (this.$options.methods[button.action]) { // guard to prevent runtime errors
this.$options.methods[button.action]();
}
},
minimizeWindow() {
console.log('minimizeWindow');
},
maximizeWindow() {
console.log('maximizeWindow');
},
closeWindow() {
console.log('closeWindow');
}
}
})
<script src="https://unpkg.com/vue#2.5.15/dist/vue.min.js"></script>
<div id="app">
<button v-for="btn in windowControlButtons" :key="btn.id" #click="handleClick(btn)">
<span>{{btn.icon}}</span>
</button>
</div>

The `render` function is not replacing slot content

I am using vue 2.0.
I want to generate this template:
<Poptip placement="right" width="400">
<Button type="ghost">click 激活</Button>
<div class="api" slot="content">my content</div>
</Poptip>
Using the render function:
render: (h, params) => {
return h('Poptip', {
props: {
placement: 'right'
}
}, [
h('Button', {
props: {
type: 'ghost'
}
}, 'Edit'),
h('div', {
props: {
slot: 'content'
}
}, 'My content')
])
}
Using the template works perfectly. But, when I use the render function, the slot's content is not replaced with my content.
You are specifing a prop named slot with value content:
h('div', {
props: {
slot: 'content'
}
}, 'My content')
To simply specify a slot named content:
h('div', {
slot: 'content'
}, 'My content')
See the documentation.