I have a Vue Bootstrap table with a "cosimo" column that includes a button to launch a modal. However, the button is not rendering properly and does not work when clicked. Here is the code for the column:
columns: [
{
title: "Cosimo",
field: "component.name",
sortable: true,
formatter: (value, row, index) => {
return `
<b-button v-b-modal.modal-1 #click="showModal"
>Launch demo modal</b-button
>
`;
}
}, ...]
I've checked the Vue Bootstrap documentation and I believe I've followed the correct syntax for creating a button that launches a modal. However, the button is not working and the HTML is not rendering properly. Can anyone help me identify the issue? Thanks in advance! It works outside the table in the template part.
EDIT
I am rendering the column with this code:
<bootstrap-table
ref="table"
:columns="columns"
:data="data"
:options="options"
#on-load-success="tableLoaded"
>
<template v-slot:cell(component.name)>
<span><b-btn #click="showModal(item)">Launch Demo Modal</b-btn></span>
</template>
</bootstrap-table>
And then the columns entry:
{
title: "Cosimo Recommendation",
field: "component.name",
sortable: true
},
I'm not sure which version of bootstrap-vue you're using, but if you're on version 2.23.0, then you can use slots for adding buttons and other HTML elements, see below example of info model button in documentation:
https://bootstrap-vue.org/docs/components/table#complete-example
You can achieve this by using slot in the template itself for that particular column.
<b-table :items="items" :fields="fields">
<template v-slot:cell(<component.name>)="{ item }">
<span><b-btn #click="showModal(item)">Launch Demo Modal</b-btn></span>
</template>
</b-table>
You can see cell({key}) slot here
Related
Edit 1: I have added a codepen here.
I have a simple datatable:
<v-data-table
:items="filteredSpecialOrders"
:headers="headers"
:loading="isLoading"
:group-by="groupby"
:footer-props="footerprops"
item-key="Id"
show-expand
>
<template #expanded-item="{ headers, item }">
<td :colspan="headers.length">
The expanded-item template is working. Customer is {{ item.Customer }}
</td>
</template>
<template #top>
<div>The top template is working</div>
</template>
<template #item.SubmittedDateSort="{ item }">
broken
</template>
<template #item.EtaWarehouseSort="{ item }">
broken
</template>
</v-data-table>
with these headers:
headers() {
return [
{ text: 'Submitted', value: 'SubmittedDateSort', },
{ text: 'ETA to Whse', value: 'EtaWarehouseSort', },
// and so on
]
},
This is the items from the computed() section:
modifiedSpecialOrders() {
return this.specialOrders
.map((spo) => {
return {
...spo,
SubmittedDate: spo.SubmittedDate && new Date(spo.SubmittedDate),
SubmittedDateSort: spo.SubmittedDate && new Date(spo.SubmittedDate).getTime(),
EtaWarehouse: spo.EtaWarehouse && new Date(spo.EtaWarehouse),
EtaWarehouseSort: spo.EtaWarehouse && new Date(spo.EtaWarehouse).getTime(),
};
});
The EtaWarehouseSort is the result of new Date(datestring).getTime() - basically milliseconds for sorting.
The template is for providing a "human-readable" version of the date - but as you can see from the screenshot, all I'm ever seeing is the milliseconds. With the current template, all I should see is the word broken but I'm still seeing the milliseconds. I am at a loss for why this isn't applying the template.
I've looked at your codepen. I found out that the problem is linked with the use of PascalCase (camelCase as well) for header values. Although it works for displaying the data, you can't access the named slot in DOM template, since the browser will interpret any upper case symbol as lowercase. See this GitHub bug report and the Vue documentation page, with hints on this problem.
There are two solutions proposed: the first is obvious - convert all headers' value key to lowercase. Second, use string templates with which I'm not very familiar.
Scenario
When clicking an option item, natively, the v-autocomplete (with multiple prop) active its respective checkbox and keeps with the menu open, allowing you to continue selecting other items until you click out of the menu
Objective
What I would like is to change this behavior, when I clicked only in the checkbox, the native behavior is maintained, but when clicked specifically on the line, the menu is closed and trigger a function using the value of the selected item.
I imagine that it is necessary to use the slots but I really don't know how to use it or if this is the best way to obtain this new behavior.
Note
The vertical line is for concept demonstration purposes only, it is not necessary to include this line in the component.
As you said, you can do it using slots. The idea is to override items display to remove the clickable v-list-item underneath. The drawback is that you have to re-implement values selection.
You'll need a checkbox and two methods: isSelected and toggleItem.
Template section:
<v-autocomplete
v-model="values"
:items="items"
outlined
dense
chips
small-chips
label="Outlined"
multiple
>
<template #item="{ item }">
<v-list-item class="d-flex">
<div>
<v-simple-checkbox color="primary" :value="isSelected(item)" #click="toggleItem(item)" />
</div>
<div class="ml-2">{{ item }}</div>
</v-list-item>
</template>
</v-autocomplete>
Script section:
data: () => ({
items: ['foo', 'bar', 'fizz', 'buzz'],
values: ['foo', 'bar'],
}),
methods: {
isSelected(item) {
return this.values.includes(item);
},
toggleItem(item) {
if (this.values.includes(item)) {
this.values = this.values.filter(v => v !== item);
} else {
this.values.push(item);
}
}
}
Please take a look at this working example.
How can I trigger the search filter once a button is clicked?
I don't want to filter each type that someone typing in the search bar.
I want to trigger searching once they click a button "Search Now".
<b-table
id="table-transition-example"
:busy.sync="loading"
:items="myProvider"
:fields="fields"
:current-page="currentPage"
:per-page="perPage"
striped
small
primary-key="user_id"
:filter="filter"
:sort-by="sortBy"
:sort-desc="sortDesc"
>
With this code anytime that someone typing in search bar, automatically search the table.
Can somehow to append :filter attribute if I create a custom event? OR any other ideas?
Bind a different data property to your input, and create a method that assigns the value from your input to your filter property.
Rough example:
<input v-model="filterInput">
<button #click="applyFilter">Search now</button>
<b-table :filter="filterTable"></b-table>
<script>
data() {
return {
filterInput: '',
filterTable: ''
}
},
methods: {
applyFilter() {
this.filterTable = this.filterInput
}
}
</script>
So this is the code i type on my editor
<nuxt-link :to="www.test.com" tag="v-btn" /> Link Button </nuxt-link>
apparently v-btn is not an HTML original tag but a vuetify specific one and when i write the code this way it doesn't actually work and i'm not getting the styles that are bind with a v-btn tag.
so i'm wondering if there's actually a way to do this...
ps: wrapping the v-btn with a nuxt-link tag in this way below
<nuxt-link to="www.text.com">
<v-btn>Link Button</v-btn>
</nuxt-link>
makes the button a link and actually works but this can't be used everywhere since some of the vuetify tags are related together and adding non vuetify tags inside it ruins the styles, like this example below which is a case of using a v-btn inside a v-toolbar-item, normally v-btn has a special style inside a v-toolbar-item but when we wrap it with a nuxt-link it loses all the styles.
<v-toolbar-items>
<nuxt-link :to="www.test.com>
<v-btn>Link Button</v-btn>
</nuxt-link>
</v-toolbar-items>
The v-btn component from Vuetify has a nuxt property to handle Nuxt.js:
<v-btn nuxt to="www.text.com">Link Button</v-btn>
See vuetify docs about the "nuxt" property: https://vuetifyjs.com/en/components/buttons/#api
You can access the href="string"
<template>
<v-col class="pb-0">
<v-btn
v-for="(link, i) in links"
:key="i"
depressed
color=""
text
rounded
class="text-none"
:to="link.l"
:href="link.href"
>
{{ link.n }}
</v-btn>
</v-col>
</template>
<script>
export default {
data () {
return {
links: [
{
n: 'About us',
l: '/about-us'
},
{
n: 'Help',
l: '/help'
},
{
n: 'Blog',
l: '/blog'
},
{
n: 'Contact',
l: '/contact'
},
{
n: 'google.com',
href: 'https://www.google.com'
}
]
}
}
}
</script>
I get this problem whenever I modify an array that is used to render a v-for list.
Let's say I've got a v-for list of three items:
<ul>
<li v-for="item in items"></li>
<ul></ul>
<ul>
<li>One</li> <!-- Has focus or a specific child component -->
<li>Two</li>
<li>Three</li>
</ul>
Add a new item to the items array:
<ul>
<li>New Item</li> <!-- Focuses on this item, the child component seems to be moved here -->
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
The focus seems to move...
Please have a look at a fiddle that illustrates the problem https://jsfiddle.net/gu9wyctr/
I understand that there must be a good reason for this behaviour, but I need to manage it or avoid completely. Ideas?
EDIT:
I've just realized that my explanation is rather ambiguous. Here's an updated fiddle to illustrate the problem https://jsfiddle.net/keligijus/d1s4mjj7/
The problem is that the input text is moved to another element...
My real life example. I've got a forum-like list of posts. Each post has an input for a reply. If someone publishes a new post while other user is typing in a reply, the input that this user is typing in is moved to another post. Just like the example in the fiddle.
Providing key is the answer!
https://v2.vuejs.org/v2/guide/list.html#key
When Vue is updating a list of elements rendered with v-for, it by default uses an “in-place patch” strategy. If the order of the data items has changed, instead of moving the DOM elements to match the order of the items, Vue will simply patch each element in-place and make sure it reflects what should be rendered at that particular index. This is similar to the behavior of track-by="$index" in Vue 1.x.
This default mode is efficient, but only suitable when your list render output does not rely on child component state or temporary DOM state (e.g. form input values).
To give Vue a hint so that it can track each node’s identity, and thus reuse and reorder existing elements, you need to provide a unique key attribute for each item. An ideal value for key would be the unique id of each item. This special attribute is a rough equivalent to track-by in 1.x, but it works like an attribute, so you need to use v-bind to bind it to dynamic values (using shorthand here):
<li v-for="(item, index) in items" :key="'item-'+item">
<input :id="'item-'+index" type="text" style="width:80%;">
</li>
Updated fiddle to show that this works https://jsfiddle.net/keligijus/d1s4mjj7/3/
Try this:
var app = new Vue({
el: '#app',
data: {
messages: [
{ message: 'Hello Vue!', id: 0 },
{ message: 'Hello Vuex!', id: 1 },
{ message: 'Hello VueRouter!', id: 2 }
],
msg: null,
focus: 'item-1'
},
mounted () {
document.getElementById(this.focus).focus()
setTimeout(() => {
this.messages.unshift({ message: 'Focus moves!', id: 3 })
}, 2000)
setTimeout(() => {
this.messages.unshift({ message: 'Moves again...', id: 4 })
this.msg = `I suppose this happens because of the way DOM is updated and I understand there must a good reason for this. However I need to avoid this behaviour. How can I do this?`
}, 4000)
},
updated: function () {
document.getElementById(this.focus).focus()
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<ul>
<li v-for="(message, index) in messages">
<input :id="'item-'+message.id" type="text" v-model="message.message" style="width:80%;">
</li>
<li v-if="msg">{{msg}}</li>
</ul>
</div>
Basically I make id the same even when new items are added, and then I can track the focused item, and focus them again even after updated.