Datatable vuetify select multiple rows (Shift+Click) - vue.js

I am using Datatable from Vuetify 1.5.x
Have enabled the checkboxes so that multiple rows can be selected, I would like to be able to select with Shift + Click so that I don't have to click on each checkbox exact same as Gmail works.
It wouldn't be difficult to do if I had a row id that changes by the sort or if the rows array was reordered when the data table is sorted. But none of these seem to work.
Has anyone achieve this with vuetify datatable?
<template v-slot:items="props">
<tr :active="props.selected" #click="selectRow(props);">
<td>
<v-layout>
<v-flex>
<v-checkbox
:input-value="props.selected"
primary
hide-details
:class="{ 'red--text': props.item.was_modified_recently == 1 }"
></v-checkbox>
</v-flex>
</td>
</tr>
</template>
Vuetify documentation example

I actually had to solve this today.
My solution relied on using the item-selected hook and method that performs the bulk selection.
methods: {
bulkSelect({ item: b, value }) {
const { selectedRows, current, shiftKeyOn } = this;
if (selectedRows.length == 1 && value == true && shiftKeyOn) {
const [a] = selectedRows;
let start = current.findIndex((item) => item == a);
let end = current.findIndex((item) => item == b);
if (start - end > 0) {
let temp = start;
start = end;
end = temp;
}
for (let i = start; i <= end; i++) {
selectedRows.push(current[i]);
}
}
},
}
So that's the meat of it. There's other housekeeping details like keeping track of when shift is being held down, and preventing the browser from highlighting the text of the table when holding down shift and clicking the second row.
I made a codepen showing this functioning here.
https://codepen.io/ryancwynar/pen/jOWoXZw

In the new version of vuetify i.e. 2.0
You question is solved easily the following way
I have put the answer in the following Stack Overflow question link

Vuetify added prepend-item slot that lets you add a custom item before listing items by which we can add "select all"
Please check the example in codepen on pretend-item for select all checkboxes

I've got the same case as you faced.
First of all, you got to add another events (keyboard events) on the <tr>
tag and pass the props as argument.
And then use slice to the items array for determined range of the selected items you want.
In my case, i've stored the selected array inside vuex store.
hoped i can helped ;)
<template v-slot:items="props">
<tr :active="props.selected" #click.shift="useShift(props)" #click="selectRow(props)">
<td>
<v-layout>
<v-flex>
<v-checkbox
:input-value="props.selected"
primary
hide-details
:class="{ 'red--text': props.item.was_modified_recently == 1 }"
></v-checkbox>
</v-flex>
</td>
</tr>
</template>
export default {
methods:{
...mapMutations(["setSelected"]),
useShift({ index }) {
this.setSelected(
this.items.slice(
this.items.findIndex(el => {
return this.selected.find(v => v.track.id == el.track.id);
}),
index
)
);
},
}
}

Related

Svelte {#if variable} block does not react to variable updates within the block

I would like to populate a table with visible rows in Svelte.
My current attempt relies on a {#if variable} test, where the rendered row updates the variable. Unfortunately, the test does not appear to react to changes to the variable. Perhaps this is as designed but the documentation does not appear to address this. Essentially:
<table>
<tbody>
{#each rows as row}
{#if renderIt==true}
<tr use:updateRenderIt>
<td>cell</td>
</tr>
{/if}
{/each}
</tbody>
</table>
I think my understanding of the timing is lacking :(. Perhaps the {#if} block cannot react to each renderIt change. There are quite a few examples of {#if} blocks, but none appear to rely on a variable which is changed within the block.
There is a running example in the Svelte playground. The console divider can be moved vertically to change the viewport dimensions.
If someone knows of a way to achieve this it would be appreciated! I can do it in traditional Javascript, but my Svelte expertise is limited :).
What I'm assuming you want is to have a state on each row when it is visible.
To do so you will need to store some data with your row, so instead of your row being a list of numbers and a single boolean to say if all rows are visible or not, it will be a list of objets that have a property visible:
let rows = [];
for (let i = 0; i < 100; i++) {
rows.push({
index: i,
visible: false,
});
};
Next, to capture when visibility changes on those rows, use Intersection Observer API:
let observer = new IntersectionObserver(
(entries, observer) => {
console.log(entries);
}
);
And use a svelte action to add that observer to elements:
<script>
...
let intersect = (element) => {
observer.observe(element);
};
</script>
<table>
<tbody>
{#each rows as row (row.index)}
<tr
use:intersect>
<td>{row.visible}</td>
</tr>
{/each}
</tbody>
</table>
To pass the intersecting state back to the element throw a custom event on it:
let observer = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
entry.target.dispatchEvent(
new CustomEvent("intersect", { detail: entry.isIntersecting })
);
});
}
);
And finally capture that event and modify the state:
<tr use:intersect
on:intersect={(event) => (row.visible = event.detail)} >
<td>{row.visible}</td>
</tr>
To render rows up to how many can fit on screen you could make the defaut state visible: true, and then wrap the element with an {#if row.visible}<tr .... </tr>{/if}. After the first event you would then remove the observer from the element using observer.unobserve by either updating the svelte action or in the observer hook.

How to show message if category exist in vue js?

I had a function to check categories is exist or not. But here I am facing one issue when check for duplicate category it is working fine.
but when call this one in template it now working fine.
<template v-slot:no-data>
<v-list>
<v-list-item v-if="checkForDuplicateCategory()">
Duplicate entries not permitted
</v-list-item>
<v-list-item v-else>
No results matching "<strong>{{ bid.search }}</strong
>" . Press <kbd>enter</kbd> to create a new one
</v-list-item>
</v-list>
</template>
function is
checkForDuplicateCategory(bool){
console.log("Entered to check duplicate categories");
let newEstimates = this.newEstimates.map((estimate) => {
return estimate.category?.toLowerCase();
});
console.log("New Estimates" , newEstimates);
if (newEstimates.includes(this.bid.category?.toLowerCase())) {
return true;
}
return false;
}
Here my scenario is when category exist it has to show like this Duplicate entries not permitted else it has show like this Press enter to create a new one
Use Strict equality instead of includes().
checkForDuplicateCategory(){
this.newEstimates.map((estimate) => {
return estimate.category?.toLowerCase() === this.bid.category?.toLowerCase()
});
}

Clearing the "cache" of computed properties upon each reload

I have this code:
TEMPLATE:
<b-dropdown class="dropdown-small dropdown-channel" text="Axes">
<b-dropdown-item v-for="(item, index) in chartAxes" :key="index" #click="displayAxis(item)">
{{ item }}
</b-dropdown-item>
</b-dropdown>
COMPUTED:
chartAxes(): string[] {
const axes = this.chart?.axes?.items
?.filter((axis: Tee.IAxis) => axis.title.text != "")
?.map((axis: Tee.IAxis) => axis.title.text)
if (axes === undefined) {
return [];
}
return axes;
},
What this does is show the different lines in a graph in a dropdown (and one can choose a specific line to populate the left-hand axis with).
The issue I am facing is every time I load a new graph, the available lines are just accumulated in the dropdown. What I want to do is to clear it and re-populate it with the new lines. Tried to push this into an array and then clear that, but doesn’t work.

Vuetify combobox - How to disable typing in vuetify's combobox

The vuetify combobox is allowing the user to type inside the combobox. Any clue on how to disable this.
This is how I have implemented my combobox.
<v-combobox
:loading="isSurveyBeingPopulated"
class="static--inputs"
color="red"
box
:items="folders"
:rules="[rules.required]"
item-text="value"
dense
placeholder="Select Survey Folder"
item-value="key"
slot="input"
v-model="selectedSurveyFolder">
</v-combobox>
Combobox:
The v-combobox component is a v-autocomplete that allows the user to
enter values that do not exist within the provided items. Created
items will be returned as strings.
So if you want to disable the typing you should use a select: https://vuetifyjs.com/en/components/selects
Select fields components are used for collecting user provided
information from a list of options.
You can just delete new item after changed.
<v-combobox
v-model="selected"
:items="[...selected, ...items]"
multiple
#change="onChangeCombobox"
/>
onChangeCombobox(items) {
this.selected = items.filter((item) => this.items.includes(item))
}
I had the same problem, I used v-select instead of v-combobox, it works fine:
<v-select
return-object
></v-select>
I've gone thru the same, i need combobox to list item and custom values but i must disable typing, my solutions was to use key events and "change"its behavior like:
#keydown="$event.target.blur()"
#keypress="$event.target.blur()"
#keyup="$event.target.blur()"
Yes, you can achieve filtering of a combobox by using the rules code, e.g.
<v-combobox
v-model="selection"
:items="items"
:search-input.sync="input"
:rules="intRule"
chips
clearable
dense
multiple
small-chips
item-text="title"
autocomplete="off"
return-object
>
</v-combobox>
and then in your script section in data, sth like
data() {
return {
selection: [],
input: null,
items: [],
valid: false,
intRule: [
function(v) {
if (!v || v.length < 0) {
return false
} else if (v.length > 0) {
for (let i = 0; i < v.length; i++) {
if (/[^0-9]/g.test(v[i].id)) {
v.splice(-1, 1)
return false
}
}
return false
} else {
return true
}
}
]
}
}
the input could be used to overwrite a no results slot and show for what input are no results found.
hope this is helping

VeeValidate not working with inline editing. I am using v-for and v-if for inline editing

I have user list inline editing table structure. Each row is represents a user. I am editing user and using vee validate for validation
validateState(ref) {
if (
this.veeFields[ref] &&
(this.veeFields[ref].dirty || this.veeFields[ref].validated)
) {
return !this.veeErrors.has(ref)
}
return null
},
saveUserUpdate: function(user) {
this.$validator.validateAll().then((result) => {
debugger
if (!result) {
return
} else {
//code to save user api
}
})
}
<tr v-for="(user, userIndex) in userList" :key="userIndex">
<td>
<span v-show="user.isEditing">
<b-form-input size="sm" v-model="user.fullName" :name="'tbnewFullName' + user.unique_id" :state="validateState('tbnewFullName' + userIndex)" :aria-describedby="'tbnewFullName-feedback' + userIndex"></b-form-input>
<b-form-invalid-feedback :id="'tbnewFullName-feedback' + userIndex">
Required.
</b-form-invalid-feedback>
</span>
<div v-show="!user.isEditing">
{{user.fullName}}
</div>
</td>
</tr>
I don't know what wrong I am doing here but validations are not fired in saveUserUpdate. veeFields list in validateState are not showing my user's name field. Also veeFields is showing list of fields from previous form.
You have to use v-model="userList[userIndex].fullName. See here for details. The summary is, due to how vee-validate works internally, they have trouble tracking the limited-in-scope user variable.