bootstrap-vue datatable show row details issue - vuejs2

trying to achieve the same result as https://bootstrap-vue.js.org/docs/components/table#row-details-support
My code:
<b-table ref="propertydata" striped hover
:items="datatable.items"
:fields="datatable.fields"
:current-page="datatable.currentPage"
:per-page="datatable.perPage"
:filter="datatable.filter"
:sort-by.sync="datatable.sortBy"
:sort-desc.sync="datatable.sortDesc"
:sort-direction="datatable.sortDirection"
#filtered="onFiltered">
<template slot="action" slot-scope="row">
<!-- we use #click.stop here to prevent emitting of a 'row-clicked' event -->
<b-button size="sm" #click.stop="row.toggleDetails" class="mr-2">
{{ row.detailsShowing ? 'Hide' : 'Show'}} Details
</b-button>
</template>
<template slot="row-details" slot-scope="row">
<b-card>
<ul>
<li v-for="(value, key) in row.item" :key="key">{{ key }}: {{ value}}</li>
</ul>
</b-card>
</template>
</b-table>
<b-row>
<b-col md="6" class="my-1">
<b-pagination :total-rows="datatable.totalRows" :per-page="datatable.perPage" v-model="datatable.currentPage" class="my-0" />
</b-col>
</b-row>
/* Datatable related */
datatable: {
fields: [
{
key: 'account_id',
sortable: true
},
{
key: 'account_name',
sortable: true
},
{
key: 'property_count',
sortable: true,
},
{
key: 'commission',
sortable: true,
variant: 'success'
},
{
key: 'action',
}
],
items: [],
currentPage: 1,
perPage: 20,
totalRows: 0,
pageOptions: [ 20, 100, 300, 500, 1000, 2000, 3000 ],
sortBy: null,
sortDesc: false,
sortDirection: 'asc',
filter: null
}
computed: {
sortOptions () {
// Create an options list from our fields
return this.datatable.fields
.filter(f => f.sortable)
.map(f => { return { text: f.label, value: f.key } })
}
}, // END COMPUTED
methods: {
onFiltered (filteredItems) {
// Trigger pagination to update the number of buttons/pages due to filtering
this.datatable.totalRows = filteredItems.length
this.datatable.currentPage = 1
}
}, // END METHODS
The result is:
As you can see, the show details is isolated to a single column instead of the full row.
What I want to achieve is for this show details section to be the full row width and if I could click the button to go to a method to call additional data to be displayed here as oppose to the native bootstrap-vue functionality.

Related

Child-modal is empty on every second click

I am using row-selected on Bootstrap table, so that when a user select a row in a table, the data of that table is passed to a new object which is then sent as a prop to a child component, which is a modal.
The issue is that when I first click on a row, the modal opens and shows the data correctly, but if I close the modal and click the same row again, the data object is empty and nothing gets shown. I need to click it again for a third time to see the data. Then if I click a fourth time, the data is gone again.
I don't understand it, because in my showModal method, I check first to see if selectedRows are empty or not, so it shouldn't open if there was no data.
Any idea why this happens?
Parent:
<template>
<b-container>
<b-card class="mt-4 mb-4">
<h5>{{ $t('errorLogs.errorLog') }}</h5>
<b-table
:items="completedTasks"
:fields="fields"
:per-page="[10, 25, 50]"
selectable
:select-mode="'single'"
#row-selected="onRowSelected"
#row-clicked="showModal"
include-actions
sort-desc
/>
</b-card>
<error-log-entry-modal ref="errorLogEntryModal" :selected-error-log="selectedRows"/>
</b-container>
</template>
<script>
import ErrorLogEntryModal from '#/components/error-log/ErrorLogEntryModal';
export default {
components: {
ErrorLogEntryModal,
},
data() {
return {
errors: null,
tasksCompleted: null,
selectedRows: []
};
},
computed: {
fields() {
return [
{
key: 'done',
label: '',
thStyle: 'width: 1%',
template: {
type: 'checkbox',
includeCheckAllCheckbox: true,
},
},
{
key: 'priority',
label: this.$t('errorLogs.priority'),
formatter: type => this.$t(`model.errors.types.${type}`),
sortable: true,
},
{
key: 'creationDateTime',
label: this.$t('creationDateTime'),
formatter: date => moment(date).locale(this.$i18n.locale).format('L'),
sortable: true,
},
{
key: 'stackTraceShort',
label: this.$t('errorLogs.stackTrace'),
sortable: true,
},
{
key: 'message',
label: this.$t('message'),
sortable: true
},
]
},
completedTasks(){
if(this.errors){
return this.errors.filter(log => log.done)
}
},
},
methods: {
load(){
errorService.getErrorLogs().then(result => {
result.data.forEach(log => log.stackTraceShort = log.stackTrace.substring(0,30));
this.errors = result.data
})
},
submit(){
this.$bvModalExt
.msgBoxYesNo(
this.$t('archiveErrorLog'),
{
title: this.$t('pleaseConfirm'),
okVariant: 'success'
}
).then(value => {
if (value) {
errorService.updateStatusOnErrorEntryLog(this.errors)
}
})
},
onRowSelected(fields){
this.selectedRows = fields
},
showModal(){
if (this.selectedRows) {
this.$refs.errorLogEntryModal.show()
}
},
},
created() {
this.load()
},
};
</script>
child:
<template>
<b-modal
modal-class="error-log-modal"
v-model="showModal"
ok-only
size="xl">
<b-row class="lg-12" v-for="log in selectedErrorLog">
<b-col class="ml-2 mr-2 mb-4">
<h4>{{ $t('errorLogs.errorMessage') }}</h4>
{{ log.message }}
</b-col>
</b-row>
<b-row class="lg-12">
<b-col class="ml-2 mr-2"><h4>{{ $t('errorLogs.stackTrace') }}</h4></b-col>
</b-row>
<b-row class="lg-12" v-for="log in selectedErrorLog">
<b-col class="ml-2 mr-2" style="word-break: break-word; background-color: #F5C9C1;">
{{ log.stackTrace }}
</b-col>
</b-row>
</b-modal>
</template>
<script>
export default {
props: {
selectedErrorLog: Array
},
data() {
return {
showModal: false,
};
},
methods: {
show(){
this.showModal = true
},
}
};
</script>

Remove Chips from Vuetify Multi Select in v-for loop

I'm using vuetify v.2 and creating some multi select via v-for.
everything is ok except removing chips. When I try to remove form any select, it removes from the last one. It got something to do with index of removed item as defined in remove method. Looks like all items get index of the last select.
Here is my code:
<template>
<v-row class="ma-0">
<v-col cols="12" sm="12" md="6" class="px-1" v-for="attribute in allAttr" :key="attribute.id">
<v-select
v-model="attrArr"
:items="attribute.items"
item-text="title"
item-value="id"
chips
:label="attribute.title"
multiple
outlined
#change="changed()"
>
<template v-slot:selection="{ attrs, item, select, selected }">
<v-chip
v-bind="attrs"
:input-value="selected"
close
#click="select"
#click:close="remove(item)"
>
<strong>{{ item.title }}</strong>
</v-chip>
</template>
</v-select>
</v-col>
{{attrArr}}
</v-row>
</template>
<script>
export default {
data(){
return{
attrArr:[],
allAttr: null
}
},
async fetch(){
// fetch all attributes
//this.allAttr = await this.axiosGet(`attributes/0/1`)
this.allAttr = [
{
id: 1,
title: "color",
items:[
{
id: 11,
title: "blue"
},
{
id: 12,
title: "green"
},
{
id: 13,
title: "black"
}
]
},
{
id: 2,
title: "size",
items:[
{
id: 21,
title: "L"
},
{
id: 22,
title: "S"
}
]
},
{
id: 3,
title: "Material",
items:[
{
id: 31,
title: "X01"
},
{
id: 32,
title: "X02"
},
{
id: 33,
title: "X03"
},
{
id: 34,
title: "X04"
}
]
}
]
},
methods: {
remove (item) {
this.attrArr.splice(this.attrArr.indexOf(item), 1)
this.attrArr = [...this.attrArr]
},
changed(){
this.$emit('changed', this.attrArr)
}
},
}
</script>
You can just pass in the id instead of the entire item object...
<v-chip
v-bind="attrs"
:input-value="selected"
close
#click="select"
#click:close="remove(item.id)">
<strong>{{ item.title }}</strong>
</v-chip>
...
remove (id) {
let idx = this.attrArr.indexOf(id)
this.attrArr.splice(idx, 1)
this.attrArr = [...this.attrArr]
},
Demo: https://codeply.com/p/Cb5TYCS6Bt
in your remove method you're looking for the index of an object within attrArr which consists of ids only. Try this instead:
this.attrArr.splice(this.attrArr.indexOf(item.id), 1)

Change background of v-data-table row on event from child component

I have an expanding data table in my parent component and a child component inside the expanded row with a button. I would like to change the background color of the associated row when I click the button inside the child component. I'm not sure how to target the row to add the css class on event.
ScanGrid(parent):
<template>
<v-flex v-if="items.length === 0">
<ScanAdd #selectBatch="showScan" />
</v-flex>
<v-card v-else class="ma-5">
<v-card-text>
<v-layout align-center>
<v-data-table
:headers="headers"
:items="items"
item-key="StorageName"
show-expand
single-expand
:expanded="expanded"
hide-default-footer
#click:row="clickedRow"
>
<template
#isDeleted="deleteRow"
v-if="groupBy === 'barCode'"
v-slot:expanded-item="{ item }"
>
<td :colspan="12">
<ScanGridCode :item="item" />
</td>
</template>
<template v-else v-slot:expanded-item="{ item }">
<td :colspan="12">
<ScanGridDef :item="item" />
</td>
</template>
</v-data-table>
</v-layout>
</v-card-text>
</v-card>
</template>
<script>
import { API } from "#/api";
import ScanAdd from "./ScanAdd";
import ScanGridCode from "./ScanGridCode";
import ScanGridDef from "./ScanGridDef";
export default {
name: "ScanGrid",
props: {
items: {
type: Array,
required: true
}
},
components: {
ScanGridCode,
ScanGridDef,
ScanAdd
},
methods: {
deleteRow(value) {
this.isDeleted = value;
},
showScan(value) {
this.selectedId = value;
this.addScanBatch(value);
this.$emit("processingBatch", true);
this.processingBatch = true;
},
async addScanBatch(Id) {
const selectedItems = await API.getPhysicalInventoryBatch(Id);
if (selectedItems.data.Id === this.selectedId) {
this.items = selectedItems.data.Locations;
}
},
clickedRow(value) {
if (
this.expanded.length &&
this.expanded[0].StorageName == value.StorageName
) {
this.expanded = [];
} else {
this.expanded = [];
this.expanded.push(value);
}
}
},
data: () => ({
isDeleted: false,
groupBy: "barCode",
expanded: [],
items: [],
toDelete: "",
totalResults: 0,
loading: true,
headers: [
{
text: "Localisation",
sortable: true,
value: "StorageName",
class: "large-column font-weight"
},
{
text: "Paquets scannés",
sortable: true,
value: "ScannedProduct",
class: "large-column font-weight"
},
{
text: "Paquets entrants",
sortable: true,
value: "Incoming",
class: "large-column font-weight"
},
{
text: "Paquets sortants",
sortable: true,
value: "Outgoing",
class: "large-column font-weight"
},
{
text: "Paquets inconnus",
sortable: true,
value: "Unknown",
class: "large-column font-weight"
}
]
})
};
</script>
ScanGridCode(child):
<template>
<div class="codeContainer">
<div class="cancelLocation">
<v-flex class="justify-center">
<v-btn class="ma-5" large color="lowerCase" tile #click="deleteLocation"
>Annuler le dépôt de cette localisation</v-btn
>
</v-flex>
</div>
</div>
</template>
<script>
export default {
name: "ScanGridCode",
props: {
item: {
type: Object,
required: true
}
},
methods: {
deleteLocation() {
this.item.IsDeleted = true;
this.$emit("IsDeleted", true);
}
},
data: () => ({
IsDeleted: false,
groupBy: 0,
headersGroupCode: [
{
text: "Code barre",
sortable: true,
value: "SerialNumber",
class: "large-column font-weight-light"
},
{
text: "De",
sortable: true,
value: "FromLocation",
class: "large-column font-weight-light"
},
{
text: "Vers",
sortable: true,
value: "ToLocation",
class: "large-column font-weight-light"
}
]
})
};
</script>
I use Vuetify 2.1.7 and Vue 2.6.10. When I click on the button I call deleteLocation function. I assume I need to $emit a value to my parent but after that I don't know how to target the tr to change its style.
Since you're using Vuex, I would suggest using some variable such as store.state.selectedRow to keep track of whether or not a row has been selected (or in cases where there are more than one row, which row has been selected). Then you can have a computed property myProperty = this.$store.state.selectedRow in your Vue component which will automatically reflect the single source of truth, and your conditional class can be bound to this myProperty. This means you don't need to worry about emitting on events.
The approach to emitting the event is what should be done. So I am assuming you will emit from deleteLocation function.
Since you need a custom styling on rows you need to add the items slot and add your logic there
<template v-slot:item="{ item, select}">
<tr :class="key === coloredRow ? 'custom-highlight-row' : ''">
<td :colspan="12">
<ScanGridCode #changeColor="changeColor(key)" :item="item" />
</td>
//add this method to your script element
changeColor(idx) {
this.coloredRow = idx;
}

Job posting wont delete, keep getting id is "undefined"

I'm having an issue with my small application. I currently have a job page, I'm able to post and get the jobs.
On the frontend I have a button to press Delete but when I do it keeps giving me this error - DELETE FROM 'jobs' WHERE 'id' = 'undefined'.
Currently, technologies being used ar MySQL, sequelize, node.js, express, and vue.js.
Console
<div>
<h2 class="mb-4 font-weight-light">Job postings</h2>
<div class="d-flex align-items-center justify-content-between">
<b-input-group class="w-30">
<b-form-input v-model="filter" placeholder="Type to Search" />
<b-input-group-append>
<b-btn :disabled="!filter" #click="filter = ''">Clear</b-btn>
</b-input-group-append>
</b-input-group>
<b-button variant="primary" class="d-flex align-items-center" v-b-modal.addJob><i class="material-icons mr-1"></i> Add job</b-button>
</div>
<b-table responsive hover :items="jobs" :fields="fields" :filter="filter" no-sort-reset sort-by="postedOn" :sort-desc="true" class="mt-3 f6">
<template slot="job_postingURL" slot-scope="data">
<a :href="`${data.value}`" target="_blank">{{ data.value }}</a>
</template>
<template slot="Remove" scope="jobs">
<b-btn variant="danger" #click="deleteJob(jobs.ID)"> Delete </b-btn>
</template>
</b-table>
<add-job></add-job>
</div>
</template>
<script>
import AddJob from '#/components/jobs/AddJob'
import JobService from '../../services/JobService'
import axios from 'axios'
export default {
components: {
AddJob
},
data () {
return {
fields: [
{ Key: 'ID', label: 'Job ID', sortable: false},
{ key: 'job_title', label: 'Job title', sortable: true },
{ key: 'job_name', label: 'Company name', sortable: true },
{ key: 'job_location', label: 'Location', sortable: true },
{ key: 'job_postingURL', label: 'Job posting link', sortable: false },
{ key: 'job_postingOn', label: 'Posted on', sortable: true, tdClass: 'text-right' },
{ key: 'job_postingBy', label: 'Posted by', sortable: true },
{ key: 'Remove', sortable: true }
],
filter: null,
jobs: [
{
ID: '',
job_title: '',
job_name: '',
job_location: '',
job_postingURL: '',
job_postingOn: '',
job_postingBy: ''
},
],
}
},
// this method is to get the data from database
async created () {
try {
this.jobs = await JobService.getJobs();
} catch(err) {
this.error = err.message;
}
},
methods: {
deleteJob (ID) {
axios.delete(`http://localhost:5000/api/jobs/${this.ID}`)
.then((res) => {
this.ID = ''
this.job_title = ''
this.job_name = ''
this.job_location = ''
this.job_postingURL =''
this.job_postingOn = ''
this.job_postingBy = ''
console.log(res)
})
.catch((err) => {
console.log(err)
})
}
}
}
</script>
Since you have jobs like a data object property you couldn't use jobs as slot-scope value, try something like row, in this case row object contains some properties like item which contain data about the current item shown in this row, so you should do :
<template slot="Remove" slot-scope="row">
<b-btn variant="danger" #click="deleteJob(row.item.ID)"> Delete </b-btn>
</template>
and in your method :
deleteJob (ID) {
axios.delete('http://localhost:5000/api/jobs/'+ID)
.then((res) => {...

how to add edit button on vue-good-table in vue

I m new to Vue and stuck in a situation and don't know how to do that if anybody suggests me how I can do this let me show my code first
<div class="table-responsive-sm">
<vue-good-table
title="Shop List Table"
:columns="columns"
:rows="rows"
:paginate="true"
:lineNumbers="true"
:globalSearch="true" >
<template slot="table-row" slot-scope="props" ><a class="btn btn-sm primary" #on-row-click="onRowClick">save</a></template>
</vue-good-table>
and in script
data(){
return{
columns: [
{
label: 'Brand Name',
field: 'brand_name',
},
{
label: 'Brand Desc',
field: 'brand_desc',
},
{
label: 'Action',
field: 'before',
},
],
rows:[],
}
}
getTotals(){
var self = this;
var new1=[];
this.$http.get('/api/brands')
.then(function (response) {
self.rows=response.data
})
},
now my problem is that if I use
<span v-if="props.column.field == 'before'">
before
</span>
as suggested in this https://jsfiddle.net/aks9800/hsf0sqf8/ it throws an error like field not defined I just want to add an extra action button for edit this is vue-good table one more thing none of the action as suggested in this link for eg:- #on-row-click="onRowClick" not working
Try this
<div class="table-responsive-sm">
<vue-good-table
title="Shop List Table"
:columns="columns"
:rows="rows"
:paginate="true"
:lineNumbers="true"
:globalSearch="true" >
<template slot="table-row" slot-scope="props" >
<a class="btn btn-sm primary" #on-row-click="onRowClick">save</a>
</template>
<template slot="table-row" slot-scope="props">
<span v-if="props.column.field == 'actions'">
<a class="btn btn-sm primary" #on-row-click="onRowClick">save</a>
</span>
<span v-else>
{{props.formattedRow[props.column.field]}}
</span>
</template>
</vue-good-table>
</div>
data(){
return{
columns: [
{
label: 'Brand Name',
field: 'brand_name',
},
{
label: 'Brand Desc',
field: 'brand_desc',
},
{
label: 'Actions',
field: 'actions',
sortable: false,
}
],
rows:[],
}
}
getTotals(){
var self = this;
var new1=[];
this.$http.get('/api/brands')
.then(function (response) {
self.rows=response.data
})
},
Here is very good example how to add "edit" button in a row by marekfilip
https://jsfiddle.net/marekfilip/jm4ywzor/
html:
<div id="app">
<vue-good-table
:columns="columns"
:rows="rows" >
<template slot="table-row" slot-scope="props">
<span v-if="props.column.field == 'before'">
<button #click="editRow(props.row.id)">Edit</button>
<button #click="deleteRow(props.row.id)">Delete</button>
</span>
<span v-else>
{{props.formattedRow[props.column.field]}}
</span>
</template>
</vue-good-table>
<span>{{ text }}</span>
</div>
javascript
new Vue({
el: '#app',
data() {
return {
columns: [
{
label: 'Before',
field: 'before'
},
{
label: 'ID',
field: 'id',
sortable: true,
},
{
label: 'Text',
field: 'text',
type: 'number',
sortable: true,
},
],
rows: [
{ text: 'A', id: 1 },
{ text: 'B', id: 2 },
{ text: 'C', id: 3 },
{ text: 'D', id: 5 },
],
text: ''
};
},
methods: {
editRow(id) {
this.showAlert(id, 'EDIT')
},
deleteRow(id) {
this.showAlert(id, 'DELETE')
},
showAlert(id, type) {
this.text = `You clicked ${type} on row ID ${id}`
}
}
});