BootstrapVue access b-table row data in slot template - javascript

I have a delete button on each row and I need to get log_id from items to pass to function deleteLog. That function always alerts log_id is undefined.
How can I pass log_id to the function deleteLog without undefined?
<template>
<b-table striped hover :items="items" :fields="fields">
<template v-slot:cell(Delete)>
<b-button variant="danger" v-on:click="deleteLog(log_id)">Delete</b-button>
</template>
</b-table>
</template>
<script>
export default {
data() {
return {
fields: ['Year', 'Month', 'Round', 'Name', 'Delete', 'log_id'],
items: []
}
}
}
</script>

You can access the row data and its log_id through the slot data:
<b-table striped hover :items="items" :fields="fields">
<template v-slot:cell(Delete)="data"> <!-- `data` -->
<b-button variant="danger" v-on:click="deleteLog(data.item.log_id)">Delete</b-button>
</template>
</b-table>
Here's another syntax, destructuring the slot data:
<b-table striped hover :items="items" :fields="fields">
<template v-slot:cell(Delete)="{ item }"> <!-- `item` -->
<b-button variant="danger" v-on:click="deleteLog(item.log_id)">Delete</b-button>
</template>
</b-table>

Related

How to validate a v-edit-dialog in a v-datatable

I'm new to vue.js and vuetify and currently trying to validate the input in a v-edit-dialog inside a v-datatable. The validation seems to work, however the save button does not get disabled and the user-input gets saved even when it's invalid. Is there a way to let the button be disabled or to prevent the saving of invalid data?
My datatable:
<v-data-table dense :headers="headers" :items="sitesTable">
<template v-slot:top>
<v-spacer></v-spacer>
<v-dialog v-model="dialog" max-width="500px">
<v-card>
<v-toolbar flat>
<v-toolbar-title v-model="title" class="primary--text">{{ title }} Site</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn icon #click="close">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-toolbar>
<v-card-text>
<v-container>
<FormTemplateSites v-bind:title="title" #dialog="!dialog"></FormTemplateSites>
</v-container>
</v-card-text>
</v-card>
</v-dialog>
</template>
<template v-slot:item.name="props">
<v-edit-dialog :return-value.sync="props.item.name" large persistent #save="saveName(props.item.name, props.item)">
<div>{{ props.item.name }}</div>
<template v-slot:input>
<div class="mt-4 title">Update Name</div>
</template>
<template v-slot:input>
<v-text-field v-model="props.item.name" :rules="required" label="Edit" single-line counter autofocus></v-text-field>
</template>
</v-edit-dialog>
</template>
<template v-slot:item.field="props">
<v-edit-dialog :return-value.sync="props.item.field" large persistent #save="saveField(props.item.field, props.item)">
<div>{{ props.item.field }}</div>
<template v-slot:input>
<div class="mt-4 title">Update Field</div>
</template>
<template v-slot:input>
<v-text-field v-model="props.item.field" :rules="rules_vectors" label="Edit" single-line counter autofocus></v-text-field>
</template>
</v-edit-dialog>
</template>
<template v-slot:item.position="props">
<v-edit-dialog :return-value.sync="props.item.position" large persistent #save="savePosition(props.item.position.x, props.item.position.y, props.item)">
<div>{{ props.item.position }}</div>
<template v-slot:input>
<div class="mt-4 title">Update Position</div>
</template>
<template v-slot:input>
<v-text-field v-model="props.item.position" label="Edit" single-line autofocus :rules="rules_vectors"></v-text-field>
</template>
</v-edit-dialog>
</template>
<template v-slot:item.actions="{ item }">
<v-icon small class="mr-2" #click="editSite(item)">mdi-pencil</v-icon>
<v-icon small #click="deleteItem(item)">mdi-delete</v-icon>
</template>
</v-data-table>
My component:
data: () => ({
title: '',
dialog: false,
required: [(v) => !!v || 'Required', ],
rules_vectors: [
(v) => !!v || 'Required',
(v) => {
try {
v = JSON.parse(v)
for (let item of v) {
if (!isNaN(item)) {
console.log(item)
} else {
return 'Invalid vector.'
}
}
return v.length === 2 || 'Invalid vector.'
} catch (error) {
console.log(error)
return 'Invalid vector.'
}
},
],
pagination: {},
headers: [{
text: 'Name',
align: 'start',
sortable: false,
value: 'name',
},
{
text: 'ID',
align: 'start',
sortable: false,
value: 'id',
},
{
text: 'Position',
sortable: false,
value: 'position',
},
{
text: 'Field',
sortable: false,
value: 'field',
},
{
text: 'Actions',
value: 'actions',
sortable: false,
},
],
}),
I had the same question
Is there a way to let the button be disabled or to prevent the saving of invalid data?
I found a way to de-couple the item value passed from the editted value and "prevent the saving of invalid data".
By using a seperate data field editName and linking the v-text-field to this, you can set the value on open and check and set it on save, without the item value becoming polluted by invalid values.
So
the v-model for the v-text-field is v-model="editName"
the v-edit-dialog has #open which sets the edit value - #open="editName = props.item.name"
you check the this.editName on save in your saveName(props.item)
as in
<v-data-table dense :headers="headers" :items="sitesTable">
<template v-slot:item.name="props">
<v-edit-dialog
large
persistent
#save="saveName(props.item)"
#open="editName = props.item.name"
>
<div>{{ props.item.name }}</div>
<template v-slot:input>
<div class="mt-4 title">Update Name</div>
</template>
<template v-slot:input>
<v-text-field
v-model="editName"
:rules="required"
label="Edit"
single-line
counter
autofocus
></v-text-field>
</template>
</v-edit-dialog>
</template>
</v-data-table>
on Save
saveName(item) {
if (this.validate(this.editName) {
item.name = this.editName
...
}
}
Edit. I removed the :return-value.sync="props.item.name" attribute on v-data-table as it seemed to be over-riding the setting of item.name in the saveName() function
I ran into the same thing and with some searching it turns out v-edit-dialog is going to be removed in v3 ... which doesn't help us now, but I did come up with a nimble solution that might work for you too.
<v-form v-model="isFormValid">
<v-data-table
:headers="headers"
:items="items"
item-key="ID"
>
<template v-slot:item.column="{ item }">
<v-text-field
v-model="itemData"
:rules="[validationRulesHere]"
required
></v-text-field>
</template>
</v-data-table>
<v-btn :disabled="!isFormValid">Save</v-btn>
</v-form>
I wrapped the whole table in a form and used it to overall tell me if everything in the table was correct.

Vue.js: How can I put a Login Modal inside a dropdown Menu?

I have the following dropdown menu:
<template>
<v-menu close-on-click transition="slide-y-transition">
<template v-slot:activator="{ on, attrs }">
<v-btn color="primary" v-bind="attrs" v-on="on">
Menu
</v-btn>
</template>
<v-list>
<v-list-item v-for="(item, index) in menuItemsMisc" :key="index" v-model="item.model">
<v-list-item-title>
<v-btn block color="white" #click="item.fn">{{ item.title }}</v-btn>
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
<!-- Modal code here -->
</template>
<script>
export default {
name: 'MenuBar',
data: () => ({
loginModal: false,
purchaseModal: false,
menuItemsMisc: [
{ title: 'Login',
model: 'loginModal',
fn: () => { this.loginModal = true}
},
{ title: 'Purchase',
model: 'purchaseModal',
fn: () => { this.purchaseModal = true }
},
]
}),
}
</script>
And I am trying to display this Login Modal When the Login Button is clicked in the dropdown.
<v-dialog v-model="loginModal" persistent max-width="500px">
<v-card class="elevation-12">
<v-toolbar color="primary" dark flat>
<v-toolbar-title>Login form</v-toolbar-title>
<v-spacer></v-spacer>
</v-toolbar>
<v-card-text>
<v-form>
<v-text-field name="login" prepend-icon="mdi-account" type="text"></v-text-field>
<v-text-field id="password" name="password" prepend-icon="mdi-lock" type="password">
</v-text-field>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="primary">Login</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
But whenever I click the Login or Purchase Button, I have an error that says:
TypeError: Cannot set property 'loginModal' of undefined
What is the Problem here?
From the Vue docs on v-model:
You can use the v-model directive to create two-way data bindings on form input, textarea, and select elements. It automatically picks the correct way to update the element based on the input type.
The v-model property on your <v-dialog> component is expecting it to be an input of some type.
You should be able to simply change this to a v-if:
<v-dialog v-if="loginModal" persistent max-width="500px">
This will cause the <v-dialog> component to display when your button is clicked.
EDIT: Please also make sure your data property on the Vue instance is declared as a class-style function. If you use a lambda function you will lose the this scope when referring to this.loginModal:
export default {
...
data() {
return {
...
}
}
}

VueJS: Dynamic props based on v-select item

I have got a chart which uses properties.
<template>
<v-row>
<v-col col="12" sm="12">
<Chart :data="series2"></Chart> ### This chart receives the props
</v-col>
<v-col cols="12" sm="6">
<v-select
item-color="red"
item-text="muh"
v-model="e7"
:items="items"
label="Select"
single
chips
hint="Which series do you want?"
persistent-hint
></v-select>
</v-col>
</v-row>
</template>
This is my script with the series.
<script>
import Chart from "./Chart"
export default {
data: function () {
return {
e7: [],
series1: [1,2,3,4,5],
series2: [5,6,5,6,5],
series3: [5,4,3,2,1],
items: ["series1", "series2", "series3"]
}
},
components: {
Chart
},
}
</script>
Passing the props works fine, but I want the v-select to change the selected series (props) based on the selected item. How can I do this?
You can do something like this.
<Chart :data="$data[e7]"></Chart>
Where e7 is the v-model attribute for v-select.

How can I expand slots in v-data-table component in Vuetify?

Hello I am trying expand default v-data-table component from Vuetify to avoid code redundancy. I have few components with v-data-table. It works on the same principles but table headers can be different. I am using v-slots but how I can expand dynamic slots from Vuetify?
When I have to add custom html to one of column (eg. action) in table I am using:
<v-data-table
:headers="questionTableHeaders"
:items="questions.fetchedData"
class="elevation-1"
hide-default-footer
:page.sync="questions.pagination.page"
:items-per-page="questions.pagination.itemsPerPage"
:loading="loadingData"
loading-text="Loading..."
no-data-text="No data.">
<template v-slot:item.action="{ item }">
<v-icon
small
class="mr-2"
title="title"
#click="clickOnTheItem(item)">
fas fa-plus
</v-icon>
</template>
</v-data-table>
But sometime tables don't have column action. How I can prepare dynamic slots in my component dataTable expanded default v-data-table component to make sure that each of the headers passing to my component have a slot that I can use in parent component?
If I have headers:
[
{text: "text1", value: "name"},
{text: "text2", value: "hash"}
]
I can have access to slots item.name and item.hash?
<dataTable :url="examApiUrl" :headers="examTableHeaders">
<template v-slot:item.name="{ item }"></template>
<template v-slot:item.hash="{ item }"></template>
</dataTable>
But as I wrote before, headers can be different. How I can prepare slots in my dataTable component?
Try it
<v-data-table
:headers="questionTableHeaders"
:items="questions.fetchedData"
class="elevation-1"
hide-default-footer
:page.sync="questions.pagination.page"
:items-per-page="questions.pagination.itemsPerPage"
:loading="loadingData"
loading-text="Loading..."
no-data-text="No data.">
<template v-slot:item.action="{ item }">
<v-icon
small
class="mr-2"
title="title"
#click="clickOnTheItem(item)">
fas fa-plus
</v-icon>
</template>
<template v-for="(slot, name) in $scopedSlots" v-slot:[name]="item">
<slot :name="name" v-bind="item"></slot>
</template>
</v-data-table>

Vue.js pass slot to wrapped Bootstrap-Vue Table component

I'm trying to create a wrapper for the bootstrap-vue Table component. This component uses slots to define cell templates, like that:
<b-table :items="itemsProvider" v-bind="options">
<template v-slot:cell(id)="data">
///...here goes the template for the cell's of itens key "id"
</template>
</b-table>
So, the wrapper i'm creating is like this:
<div>
<b-table :items="itemsProvider" v-bind="options" >
<slot></slot>
</b-table>
<b-pagination
v-model="currentPage"
:total-rows="rows"
:per-page="perPage"
/>
</div>
And i want to call this component like this:
<TableAjax :options="options">
<template v-slot:cell(id)="data">
///...here goes the template for the cell's of itens key "id"
</template>
</TableAjax>
But, since the slots needed on the b-table component are named, i'm having a hard time passing it from the wrapper.
How can i do that?
Passing slots to a child component can be done like this:
<template>
<div>
<b-table :items="itemsProvider" v-bind="options" >
<template v-slot:cell(id)="data">
<slot name="cell(id)" v-bind="data"></slot>
</template>
</b-table>
<b-pagination
v-model="currentPage"
:total-rows="rows"
:per-page="perPage"
/>
</div>
</template>
But since you may not know the slot names ahead of time, you would need to do something similar to the following:
<template>
<div>
<b-table :items="itemsProvider" v-bind="options" >
<template v-for="slotName in Object.keys($scopedSlots)" v-slot:[slotName]="slotScope">
<slot :name="slotName" v-bind="slotScope"></slot>
</template>
</b-table>
<b-pagination
v-model="currentPage"
:total-rows="rows"
:per-page="perPage"
/>
</div>
</template>

Categories

Resources