Vue JS Simple loop - Uncaught (in promise) TypeError: Cannot read properties of null (reading 'insertBefore') - vue.js

I'm trying to update a table using bootstrap-table.
https://bootstrap-table.com/
The library is correctly loaded, but then when I update my data, I have :
This is my simple vue component :
<template>
<button #click="this.updateArtists">Click</button>
<table
v-if="this.artists"
id="table"
data-pagination="true"
data-search="true"
>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr v-for="artist in this.artists" :key="artist.id">
<td>{{ artist.id }}</td>
<td>{{ artist.name }}</td>
</tr>
</tbody>
</table>
</template>
<script>
import $ from "jquery";
import "bootstrap-table/dist/bootstrap-table.min";
import "bootstrap-table/dist/locale/bootstrap-table-fr-FR";
export default {
name: "Test",
data: function () {
return {
labels: null,
artists: null,
};
},
methods: {
updateArtists: function () {
this.artists = [
{
id: 4,
name: "name_4",
},
{
id: 5,
name: "name_5",
},
{
id: 6,
name: "name_6",
},
];
},
},
beforeCreate: async function () {
const labels = this.axios.get("/labels");
await Promise.all([labels]).then(([labels]) => {
this.labels = labels.data;
this.artists = [
{
id: 1,
name: "name_1",
},
{
id: 2,
name: "name_2",
},
{
id: 3,
name: "name_3",
},
];
});
$("#table").bootstrapTable();
},
};
</script>
<style scoped></style>
I don't understand how vue can find a properties of null ?
Of course when i comment the line $("#table").bootstrapTable();
There is no more problem.
I have no problem without bootstrap table loaded. However, when I load the package, initialize my table. Then when I click on the update button, the warning and the error are flag.
With this code, the table is render well with bootstrap table, but the update fail ("done" is never fire):
Updated Code:
<template>
<button #click="this.updateArtists">Update</button>
<table
v-if="this.artists"
id="table"
data-pagination="true"
data-search="true"
>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr v-for="artist in this.artists" :key="artist.id">
<td>{{ artist.id }}</td>
<td>{{ artist.name }}</td>
</tr>
</tbody>
</table>
</template>
<script>
import $ from "jquery";
import "bootstrap-table/dist/bootstrap-table.min";
import "bootstrap-table/dist/locale/bootstrap-table-fr-FR";
export default {
name: "Test",
data: function () {
return {
labels: null,
artists: null,
};
},
methods: {
updateArtists: function () {
this.artists = [
{
id: 4,
name: "name_4",
},
{
id: 5,
name: "name_5",
},
{
id: 6,
name: "name_6",
},
{
id: 7,
name: "name_7",
},
];
this.$nextTick(() => {
console.log("done");
});
},
},
mounted: async function () {
const labels = this.axios.get("/labels");
await Promise.all([labels]).then(([labels]) => {
this.labels = labels.data;
this.artists = [
{
id: 1,
name: "name_1",
},
{
id: 2,
name: "name_2",
},
{
id: 3,
name: "name_3",
},
];
this.$nextTick(() => {
$("#table").bootstrapTable();
});
});
},
};
</script>
<style scoped></style>
Sandbox: https://codesandbox.io/s/zen-feynman-w8fobn?file=/src/App.vue

Related

How to set the content of first row as headers?

How to set the content of first row as headers ?
<template>
<v-container>
<div #drop="_drop" #dragenter="_suppress" #dragover="_suppress">
<input
type="file"
class="form-control"
id="file"
:accept="SheetJSFT"
#change="_change"
/>
<v-btn :disabled="data.length ? false : true" #click="_export">
Export
</v-btn>
<table class="table table-striped">
<thead>
<tr>
<th v-for="c in cols" :key="c.key">{{ c.name }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(r, key) in data" :key="key">
<td v-for="c in cols" :key="c.key">{{ r[c.key] }}</td>
</tr>
</tbody>
</table>
</div>
</v-container>
</template>
<script>
import XLSX from 'xlsx'
const make_cols = (refstr) =>
Array(XLSX.utils.decode_range(refstr).e.c + 1)
.fill(0)
.map((x, i) => ({ name: XLSX.utils.encode_col(i), key: i }))
const _SheetJSFT = [
'xlsx',
'xlsb',
'xlsm',
'xls',
'xml',
'csv',
'txt',
'ods',
'fods',
'uos',
'sylk',
'dif',
'dbf',
'prn',
'qpw',
'123',
'wb*',
'wq*',
'html',
'htm',
]
.map(function (x) {
return '.' + x
})
.join(',')
export default {
data() {
return {
data: ['SheetJS'.split(''), '1234567'.split('')],
cols: [
{ name: 'A', key: 0 },
{ name: 'B', key: 1 },
{ name: 'C', key: 2 },
{ name: 'D', key: 3 },
{ name: 'E', key: 4 },
{ name: 'F', key: 5 },
{ name: 'G', key: 6 },
],
SheetJSFT: '.xlsx',
}
},
methods: {
_suppress(evt) {
evt.stopPropagation()
evt.preventDefault()
},
_drop(evt) {
evt.stopPropagation()
evt.preventDefault()
const files = evt.dataTransfer.files
if (files && files[0]) this._file(files[0])
},
_change(evt) {
const files = evt.target.files
if (files && files[0]) this._file(files[0])
},
_export(evt) {
/* convert state to workbook */
const ws = XLSX.utils.aoa_to_sheet(this.data)
const wb = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(wb, ws, 'SheetJS')
/* generate file and send to client */
XLSX.writeFile(wb, 'sheetjs.xlsx')
},
_file(file) {
/* Boilerplate to set up FileReader */
const reader = new FileReader()
reader.onload = (e) => {
/* Parse data */
const ab = e.target.result
const wb = XLSX.read(new Uint8Array(ab), { type: 'array' })
/* Get first worksheet */
const wsname = wb.SheetNames[0]
const ws = wb.Sheets[wsname]
/* Convert array of arrays */
const data = XLSX.utils.sheet_to_json(ws, { header: 1, range: 1 })
/* Update state */
const data = XLSX.utils.sheet_to_json(ws, { header: 1 })
/* Update state */
this.data = data
this.cols = make_cols(ws['!ref'])
}
reader.readAsArrayBuffer(file)
},
},
}
</script>

How to update a table's data in VueJS?

I have a table with date filters (it's a panel with 4 options: day/week/month/year). My task is create pagination. After selection of the filter with specific date range, I fetch data from API and save its response to vuex store. I logged everything - fetching and vuex work fine. But when I start to change filters, my table data isn't updating properly - seems like it uses old stored data when new is added. I tried lots of ways to solve it - using a computed property, special counter, etc. How my problem can be solved? Thanks.
<template>
<div class='page'>
<h1 class='title'>Bookings</h1>
<div class='schedule-page classes bookings' :class='{ isActive: isOpenPopup }'>
<div class='filter-bar-wrapper'>
<div class='row'>
<FilterBar #change='handleFilters' :categories='allCategories' :statuses='bookingClassesStatus' />
<button class='button button--primary button--create' #click='handleCreateBooking'
v-if='caller !== callerOptions.ADMIN'>
Add a new bookings
</button>
</div>
</div>
<div class='table-wrapper'>
<p class='title'>{{ today }}</p>
<table class='bookings-table'>
<thead>
<tr>
<th>ID booking</th>
<th>Date</th>
<th>Time</th>
<th>Studio name</th>
<th>Client name</th>
<th>Class name</th>
<th>Categories</th>
<th>Points</th>
<th>Event creator</th>
<th>Status</th>
<th>Validate code</th>
</tr>
</thead>
<tbody>
<template>
<tr :key='`booking-body-title`' class='empty-row'
v-if='this.newBookingsAmount > 0'>
<td :style='"padding: 12px 16px 12px 28px; color:" + Styles.BLUE'>New bookings</td>
</tr>
<template v-for='(booking, index) in this.bookings'>
// lots of tr/td
</template>
</template>
</tbody>
</table>
<Pagination :pagesNum='this.pages' #change='handlePaging' />
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
import moment from 'moment'
import FilterBar from '#/components/schedule/FilterBar'
import STYLES from '#/store/styles'
import bookingStatus from '#/store/modules/bookingStatus'
import Pagination from '#/components/Pagination'
import classStatus from '#/store/modules/classStatus'
import { TAKE_ITEMS_PER_PAGE } from '#/store/modules/admin/config'
import callerOptions from '#/store/modules/admin/bookings/callerOptions'
export default {
props: {
currentStudio: {
type: Object,
},
caller: {
type: String,
required: true,
validator: (value) => {
return Object.values(callerOptions).includes(value)
},
},
},
data() {
return {
bookings: [],
newBookingsAmount: 0,
pages: 0,
callerOptions: callerOptions,
mode: '',
filters: {
startDate: moment()
.startOf('day')
.utcOffset(0, true)
.toISOString(),
endDate: moment()
.endOf('day')
.utcOffset(0, true)
.toISOString(),
},
bookingClassesStatus: bookingStatus,
bookingItem: {
date: null,
class: '',
client: '',
status: '',
numberOfPoints: null,
comment: '',
},
}
},
computed: {
...mapGetters([
'bookingsList',
]),
today() {
switch (this.mode) {
case 'day':
return moment(this.filters.startDate).format('MMMM D')
case 'week':
return `${moment(this.filters.startDate).utc().format('D.MM')} - ${moment(this.filters.endDate).utc().format('D.MM')}`
case 'month':
return moment(this.filters.startDate).format('MMMM, YYYY')
case 'year':
return moment(this.filters.startDate).format('YYYY')
default:
return 'Invalid date'
}
},
},
methods: {
moment,
...mapActions([
'getBookingsList',
'addBooking',
]),
handleFilters(filters, mode) {
this.filters = filters
this.mode = mode
this.refresh()
},
handlePaging(page) {
const rootElement = document.querySelector('.main-content')
if (rootElement) {
rootElement.scrollTo(0, 0)
}
switch (this.caller) {
case callerOptions.ADMIN:
this.getBookingsList({
...this.filters,
page: page,
take: TAKE_ITEMS_PER_PAGE,
})
break
case callerOptions.CLIENT:
this.getBookingsList({
clientId: this.clientInfo.id,
...this.filters,
page: page,
take: TAKE_ITEMS_PER_PAGE,
})
break
case callerOptions.CLASS:
this.getBookingsList({
classId: this.studioClassesInfo.id,
...this.filters,
page: page,
take: TAKE_ITEMS_PER_PAGE,
})
break
case callerOptions.STUDIO:
this.getBookingsList({
studioId: this.studiosItemInfo.id,
...this.filters,
page: page,
take: TAKE_ITEMS_PER_PAGE,
})
break
default:
break
}
this.$router.push({
name: this.$route.name,
query: { page: page, mode: this.mode },
}).catch(() => {
})
this.bookings = [...this.bookingsList.filter(item => this.isNew.includes(item.status)), ...this.bookingsList.filter(item => !this.isNew.includes(item.status))]
this.newBookingsAmount = this.bookingsList.filter(item => this.isNew.includes(item.status)).length > 0 && this.bookingsList !== undefined ? this.bookingsList.filter(item => this.isNew.includes(item.status)).length : 0
this.pages = this.bookingsPages === undefined ? 1 : this.bookingsPages
},
async refresh() {
switch (this.caller) {
case callerOptions.ADMIN:
await this.getBookingsList({
...this.filters,
page: this.$router.currentRoute.query.page,
take: TAKE_ITEMS_PER_PAGE,
})
break
case callerOptions.CLIENT:
await this.getBookingsList({
clientId: this.clientInfo.id,
...this.filters,
page: this.$router.currentRoute.query.page,
take: TAKE_ITEMS_PER_PAGE,
})
break
case callerOptions.CLASS:
await this.getBookingsList({
classId: this.studioClassesInfo.id,
...this.filters,
page: this.$router.currentRoute.query.page,
take: TAKE_ITEMS_PER_PAGE,
})
break
case callerOptions.STUDIO:
await this.getBookingsList({
studioId: this.studiosItemInfo.id,
...this.filters,
page: this.$router.currentRoute.query.page,
take: TAKE_ITEMS_PER_PAGE,
})
break
default:
break
}
await this.$router.push({
name: this.$route.name,
query: { page: 1, mode: this.mode },
}).catch(() => {
})
this.newBookingsAmount = this.bookingsList.filter(item => this.isNew.includes(item.status)).length > 0 && this.bookingsList !== undefined ? this.bookingsList.filter(item => this.isNew.includes(item.status)).length : 0
this.pages = this.bookingsPages === undefined ? 1 : this.bookingsPages
this.bookings = this.$store.state.bookings.bookingsList
},
},
async mounted() {
await this.getClientsList()
await this.getClassesList()
await this.getStudiosList()
this.mode = this.$route.query.mode === undefined ? 'day' : this.$route.query.mode
},
watch: {
filters: {
handler() {
},
deep: true,
},
bookings: {
handler() {
},
deep: true,
}
},
components: {
FilterBar,
Pagination,
},
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
I fixed it. My solution was to add undefined validation inside rows

VUE.JS API data by using axios show in V-data-table didn't work: it didn't show data

I am a beginner and I try to reach API and show the table by using Vue.js and Vuetify.
I got data from API but I can't show it by v-data-table.
This is my code:
HTML part
<v-data-table :items="data" :headers="headers" :items-per-page="5">
<template slot="data" slot-scope="props">
<td>{{ props.data.userId }}</td>
<td>{{ props.data.id }}</td>
<td>{{ props.data.title }}</td>
</template>
</v-data-table>
script part
<script>
import axios from "axios";
export default {
name: "Current",
data: () => ({
items: ["albums", "todos", "posts"],
selected: "",
headers: [
{ text: "USER_ID", align: "start", sortable: false, value: "userId" },
{ text: "ID", value: "id" },
{ text: "TITLE", value: "title" },
],
data:[],
}),
methods: {
getData() {
axios
.get("https://jsonplaceholder.typicode.com/users/1/" + this.selected ,{dataType: 'json'})
.then((response) => { this.data = response.data;})
.catch((err) => alert(err));
},
},
mounted() {
this.getData();
}
};
</script>
Here is the output that I got
Here is what I want
I refactored your code, first of all when you fetch data from json placeholder there is a single object in the response, you have to push that object to data: [], instead of assigning. Also, it looks like your v-data-table template looks weird, and values from headers should match a key name from response data.
<template>
<v-app>
<v-data-table :items="data" :headers="headers" :items-per-page="5">
<template v-slot:items="props">
<td>{{ props.item.id }}</td>
<td>{{ props.item.name }}</td>
<td>{{ props.item.email }}</td>
</template>
</v-data-table>
</v-app>
</template>
<script>
import axios from "axios";
export default {
name: "App",
data: () => ({
items: ["albums", "todos", "posts"],
selected: "",
headers: [
{ text: "USER_ID", align: "start", sortable: false, value: "id" },
{ text: "EMAIL", value: "email" },
{ text: "NAME", value: "name" },
],
data: [],
}),
methods: {
getData() {
return axios
.get("https://jsonplaceholder.typicode.com/users/1/" + this.selected, {
dataType: "json",
})
.then((response) => {
this.data.push(response.data)
})
.catch((err) => alert(err));
},
},
mounted() {
this.getData();
},
};
</script>

Load data from API into mdbootstrap datatable (vue.js, axios)

I want to load data from my API into a mdbootstrap datatable.
How can I write a method, that calls the getPosts() API and writes the results into the rows[] array?
<template>
<mdb-datatable
:data="data"
striped
bordered
>
</template>
<script>
import { mdbDatatable } from 'mdbvue'
import api from '#/api'
export default {
name: 'TPanels',
components: {
mdbDatatable },
data: function () {
return {
loading: false,
posts: [],
model: {},
claims: '',
data: {
columns: [{
label: 'ID',
field: 'immo_id',
sort: 'asc'
},
{
label: 'Titel',
field: 'title',
sort: 'asc'
},
{
label: 'Preis',
field: 'price',
sort: 'asc'
}],
rows: [{
immo_id: XXX,
title: YYY,
price: ZZZ,
}],
}
}
},
async created () {
this.refreshPosts()
this.setup()
},
methods: {
async setup () {
this.claims = await this.$auth.getUser()
},
async isAuthenticated () {
this.authenticated = await this.$auth.isAuthenticated()
},
async refreshPosts () {
this.loading = true
this.posts = await api.getPosts()
}
}
</script>
In my template (vue.js) I can access the API for example with:
<tr v-for="post in posts" :key="post.id">
<td>{{ post.immo_id }}</td>
<td>{{ post.title }}</td>
<td>{{ post.url }}</td>
<td>{{ post.user_id }}</td>
</tr>
so I know that the API is working.
How can I do that please?
I managed to solve the problem by writing the method getTableData(). Important is to use push() on the array in order to have vue rendering the html newly.
},
async created () {
this.refreshPosts()
this.setup()
await this.getTableData()
},
},
getTableData () {
return api.getPosts().then(response => {
var index
let temp = response
console.log(temp)
for (index = 0; index < temp.length; ++index) {
this.data.rows.push({
title: temp[index].title,
immo_id: temp[index].immo_id,
price: temp[index].price
})
}
}
)
}

vue js 2 sorting a table

I have 2 questions with vue.js 2 and a fiddle here: https://jsfiddle.net/tmun9cxa/1/
When you click a column header, why does my sorting not work? What is the solution?
How do I go about making the search input field search only the pn column?
A lot of the examples ive found are using vue1 and out of date.
<input type="text" value="" v-model="search" placeholder="Search">
<table style="text-align: center;">
<thead>
<tr>
<th v-for="column in columns">
<a
href="#"
v-on:click="sort(column.shortcode)">{{column.label}}
</a>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(product) in products">
<td>{{product.pn}}</td>
<td>{{product.od}}</td>
<td>{{product.id}}</td>
<td>{{product.thickness}}</td>
<td>{{product.lo}}</td>
<td>{{product.weight}}</td>
</tr>
</tbody>
</table>
javascript here
var vm = new Vue({
el: '#app',
data: {
currentSort: 'pn',
currentSortDir: 'asc',
search: '',
columns: [
{ label: 'P/N', shortcode: 'pn' },
{ label: 'OD (De,mm)', shortcode: 'od' },
{ label: 'ID (De,mm)', shortcode: 'id' },
{ label: 'Thickness (De,mm)', shortcode: 'thickness' },
{ label: 'LO', shortcode: 'lo' },
{ label: 'Weight (kg/1000)', shortcode: 'weight' },
], // columns
products: [
{
pn: 170158,
od: 13,
id: .44,
thickness: 1,
lo: .45,
weight: .7
},{
pn: 1803561,
od: 12,
id: .8,
thickness: .7,
lo: .11,
weight: .5
},{
pn: 170149,
od: 9,
id: .64,
thickness: .6,
lo: .75,
weight: .3
},{
pn: 150149,
od: 15,
id: .22,
thickness: .3,
lo: .55,
weight: .9
},
], // products
},
methods: {
sort:function(col) {
//console.log( 'current: '+this.currentSort );
//console.log( 'col: '+col );
//var colthing = col;
// if you click the same label twice
if(this.currentSort == col){
console.log( 'same col: '+col );
// sort by asc
this.products = this.products.sort((a, b) => {
return a.col >= b.col;
});
}else{
this.currentSort = col;
console.log( 'diff col: '+col );
// sort by desc
this.products = this.products.sort((a, b) => {
return a.col <= b.col;
});
} // end if
}, // sort
}, // methods
}); // vue
the column sorting , as pointed out, was not working because you need to use a[col] instead of a.col
Also, you should consider using a computed value instead of modifying the original data. This makes filtering easier too.
here is the updated script (note that <tr v-for="(product) in products"> needs to be <tr v-for="(product) in showProducts"> for this to work)
var vm = new Vue({
el: '#app',
data: {
currentSort: 'pn',
currentSortDir: 'asc',
search: '',
columns: [
{ label: 'P/N', shortcode: 'pn' },
/// more columns ...
], // columns
products: [
//.... objects
], // products
},
computed: {
showProducts() {
return this.products.filter(a => {
console.log(a.pn)
return (a.pn + '').includes(this.search)
})
.sort((a, b) => {
if (this.currentSortDir === 'asc') {
return a[this.currentSort] >= b[this.currentSort];
}
return a[this.currentSort] <= b[this.currentSort];
})
},
},
methods: {
sort:function(col) {
// if you click the same label twice
if(this.currentSort == col){
this.currentSortDir = this.currentSortDir === 'asc' ? 'desc' : 'asc';
}else{
this.currentSort = col;
console.log( 'diff col: '+col );
} // end if
}, // sort
}, // methods
}); // vue
finally, the fiddle: https://jsfiddle.net/tmun9cxa/2/