Vuetify Datatable item.<name> template not being applied - but other templates are fine - vue.js

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.

Related

Vue bootstrap table: button in column not rendering

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

How to change the behavior of clicking an option item of v-autocomplete

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.

Vue/Nuxt Component updates and rerenders without any data changing

I have two components, Carousel.vue and Showcase.vue. I'm testing them both in a page like this:
<template>
<main>
<app-showcase
before-focus-class="app-showcase__element--before-focus"
after-focus-class="app-showcase__element--after-focus"
>
<div class="test-showcase" v-for="n in 10" :key="n">
<img
class="u-image-cover-center"
:src="`https://picsum.photos/1000/1000?random=${n}`"
alt=""
/>
<div>Showcase numero {{ n }}</div>
</div>
</app-showcase>
<div class="u-layout--main u-margin-vertical--4">
<div>
<app-button #click="changeRequestTest(4)">Test Request 4</app-button>
<app-button #click="changeRequestTest(10)">Test Request 10</app-button>
<app-carousel
content-class="u-spread-horizontal--main"
center
:request-element="requestTest"
scroll-by="index"
#index-change="onIndexChange"
>
<template #header>
<h4>Placeholder images</h4>
<div>
Carousel heading h4, showing item number {{ index + 1 }}.
</div>
</template>
<template #default>
<img
:src="`https://picsum.photos/100/80?random=${n}`"
:data-carousel-item-name="n === 10 ? 'giovanni-rana' : ''"
alt=""
v-for="n in 20"
:key="n"
/>
</template>
</app-carousel>
</div>
</div>
</main>
</template>
<script>
export default {
data() {
return {
n1: 20,
n2: 20,
isAnimationOver: false,
index: 0,
requestTest: null,
};
},
methods: {
changeRequestTest(n) {
this.requestTest = n;
},
onIndexChange(e) {
this.requestTest = e;
},
logTest(msg = "hello bro") {
console.log(msg);
},
logElement(e) {
console.log(e);
},
},
created() {
this.requestTest = this.$route.query.hero;
},
};
</script>
Both components use a parameter called index, which basically registers the position (in the children array) of the element that is being focused/shown/highlighted.
...
data() {
return {
index: 0,
showBackButton: true,
showForwardButton: true,
lockScroll: false,
};
},
...
Carousel actually has a prop, requestElement, that allows the carousel to be scrolled to a specific element (via number or element name), and is used in combination with the event "index-change" to update the value in the parent component.
Now that's the problem: every time requestElement updates (there is a watcher in Carousel.vue to follow that), the Showcase component rerenders.
That's a huge problem because the Showcase component applies specific classes on the mounted hook, and on rerender, all that classes are lost. I solved the problem by calling my class-applying-methods in the update hook, but I don't particularly like this performance-wise.
Basically, if I remove any reference to requestElement, everything works as intended, but as soon as that value changes, the showcase rerenders completely. I tried to change props/params names, to use dedicated functions instead of inline scripts, nothing works. No common value is shared, neither via props or vuex, so this makes it even weirder.
Any idea why this happens?
EDIT: I tried the solution from this question, as expected no change was detected in Showcase component, but it still gets updated when requestElement is changed in Carousel.

v-autocomplete and setting user input as its value

I use vuetify in my project and need typeahead component. Sadly v-autocomplete implemented as combobox with filter, so it doesn't allow setting user input as v-model (or at least I can't find I way how to do so).
Could someone please explain me how to implement such functionality (maybe by another vuetify component)? I load items from server, but they are serve just as suggestions. Users need to have an ability to type and set any value they want to.
Here is a base example https://codepen.io/miklever/pen/oMZxzZ. The problem is that if I type any word that doesn't start with 'John' v-autocomplete clears it on blur. I've tried to set v-model manually and to add user input to array, but any of this methods has issues and doesn't work as expected.
<div id="app">
<v-app>
<v-content>
<v-container>
<p>Name: {{ select || 'unknown'}}</>
<v-autocomplete
:items="items"
:search-input.sync="search"
v-model="select"
cache-items
flat
hide-no-data
label="Name"
></v-autocomplete>
</v-container>
</v-content>
</v-app>
</div>
new Vue({
el: "#app",
data: () => ({
items: [],
search: null,
select: null,
commonNames: ["John", "John2", "John3"]
}),
watch: {
search(val) {
val && val !== this.select && this.querySelections(val);
}
},
methods: {
querySelections(v) {
setTimeout(() => {
this.items = this.commonNames.filter(e => {
return (e || "").toLowerCase().indexOf((v || "").toLowerCase()) > -1;
});
}, 500);
}
}
});
In Vuetify 1.1.7 Combobox has new feature which you can refer to.
Its on the Advance custom options.
had this same issue and solved it with the v-combobox it's kinda like the same as v-autocomplete so, you can just replace your tags with <v-combobox></v-combobox> or check the documentation here: https://vuetifyjs.com/en/components/combobox/#usage

Router link in vuetable?

I use vuetable 2 in the project. In this table I take data from json. It is necessary to do, for example:
There is a column to Order, in it information ORD1231 ** and so on. When you click on one of the cells, the order goes to another page with a dynamic id. Actually a question, how in vuetable to push router-link?
You can use __slot special field to user router-links.
Inside your <vuetable>, use <template slot=()>
<vuetable ref="vuetable" :fields="fields">
<template slot="code" slot-scope="props">
<router-link :to="props.rowData.code">
{{ props.rowData.code }}
</router-link>
</template>
</vuetable>
and in your script
data() {
return {
fields: {
{
name: '__slot:code'
title: 'Your Title'
}
}
}
}
You can access your data with props.rowData and props.rowIndex attributes.