How to set the content of first row as headers? - sheetjs

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>

Related

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

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

Watch triggers only once - vue3

Vue3 newbie here. I am trying to toggle a string value, but watch triggers only once.
<template>
<div>
...
<table>
<thead>
...
<th>
<div className="create-date-label">
<p>Create Date</p>
<i
#click="toggleSortDate"
:className="
sortDirection === SORT_DIRECTIONS.DESCENDING
? 'fa fa-arrow-down'
: 'fa fa-arrow-up'
"
/>
</div>
</th>
...
</div>
</template>
<script>
import Navbar from "../components/Navbar.vue";
import ConfigurationRow from "../components/ConfigurationRow.vue";
const SORT_DIRECTIONS = Object.freeze({
ASCENDING: "ASCENDING",
DESCENDING: "DESCENDING",
});
export default {
name: "Home",
components: {
Navbar,
ConfigurationRow,
},
data() {
return {
configurations: [],
SORT_DIRECTIONS,
sortDirection: '',
};
},
methods: {
toggleSortDate() {
if (this.sortDirection === this.SORT_DIRECTIONS.ASCENDING)
this.sortDirection = this.SORT_DIRECTIONS.DESCENDING;
if (this.sortDirection === this.SORT_DIRECTIONS.DESCENDING)
this.sortDirection = this.SORT_DIRECTIONS.ASCENDING;
},
},
watch: {
sortDirection: function (newDirection) {
console.log("watch sort direction", newDirection); //Prints ASCENDING once
if (newDirection === this.SORT_DIRECTIONS.ASCENDING)
this.configurations = this.configurations.sort(
(a, b) => a.date.getTime() - b.date.getTime()
);
else if (newDirection === this.SORT_DIRECTIONS.DESCENDING)
this.configurations = this.configurations.sort(
(a, b) => b.date.getTime() - a.date.getTime()
);
},
deep: true, //Tried with removing this too, same result
},
created() {
this.configurations = [
{
key: "some_key",
value: "1.4.5.21",
description: "This is a kind of a long description",
date: new Date(),
},
{
key: "another_key",
value: "1.2",
description: "Desc",
date: new Date(),
},
{
key: "min_value",
value: "13",
description:
"This is a kind of a long description This is a kind of a long description This is a kind of a long description ",
date: new Date(),
},
].sort((a, b) => a.date.getTime() - b.date.getTime());
this.sortDirection = this.SORT_DIRECTIONS.DESCENDING;
},
};
</script>
I am using vue3 but do I have to use ref or reactive to achieve this? Anybody have an idea on how this triggers once but not again?
Try this:
toggleSortDate() {
if (this.sortDirection === this.SORT_DIRECTIONS.ASCENDING)
this.sortDirection = this.SORT_DIRECTIONS.DESCENDING;
else if (this.sortDirection === this.SORT_DIRECTIONS.DESCENDING)
this.sortDirection = this.SORT_DIRECTIONS.ASCENDING;
},

Can't read property 'replace' of null in angular data table

I wrote an angular datatable which fetches and populates the table with data from server. Code is given below:
HTML:
<table
class="table display table-striped table-hover table-bordered row-border hover responsive nowrap"
datatable
[dtOptions]="dtOptions"
datatable=""
#dataTable
id="issueTable"
>
<thead class="headColor">
<!-- <tr>
<th>Action</th>
</tr> -->
</thead>
<tbody>
<!-- <tr>
<td><button class="btn btn-primary btnColor" (click)="buttonInRowClick($event)">View</button></td>
</tr> -->
</tbody>
</table>
angular code:
import { Component, OnInit, Renderer, AfterViewInit, ViewChild } from '#angular/core';
import { Router, RouterLink } from '#angular/router';
import { routerTransition } from '../../router.animations';
import { DataTableDirective } from 'angular-datatables';
import 'datatables.net';
import 'datatables.net-bs4';
window['jQuery'] = window['$'] = $;
#Component({
selector: 'app-charts',
templateUrl: './issues.component.html',
styleUrls: ['./issues.component.scss'],
animations: [routerTransition()]
})
export class IssuesComponent implements OnInit {
#ViewChild(DataTableDirective)
datatableElement: DataTableDirective;
message = '';
title = 'angulardatatables';
#ViewChild('dataTable') table;
dataTable: any;
/**
* gets settings of data tables
*
* #type {DataTables.Settings}
* #memberof IssuesComponent
*/
constructor(private router: Router) { }
dtOptions: DataTables.Settings = {};
// someClickhandler(info: any): void {
// this.message = info.number + '-' + info.assignedto + '-' + info.company;
// console.log(this.message);
// }
ngOnInit(): void {
this.dtOptions = {
pagingType: 'full_numbers',
pageLength: 15,
processing: true,
responsive: true,
autoWidth: false,
dom: 'Bfrtip',
'ajax': {
url: 'http://localhost:8080/incident-updated',
type: 'GET',
dataSrc: 'result',
},
columns: [
{
title: 'Incident',
data: 'number'
},
{
title: 'Product',
data: 'u_product'
},
{
title: 'Status',
data: 'state'
},
{
title: 'Created By',
data: 'sys_created_by'
},
{
title: 'Group',
data: 'assignment_group'
},
{
title: 'Category',
data: 'u_category'
},
{
title: 'Updated on',
data: 'sys_updated_on'
},
{
title: null,
data: null,
render: function(data, type, full) {
return '<a class="btn btn-info btn-sm" href=#/>' + 'View' + '</a>';
}
}
]
// ,
// columnDefs: [
// {
// targets: -1,
// data: null,
// defaultContent: '<button>Click!</button>'
// }
// ]
};
this.dataTable = $(this.table.nativeElement);
console.log('table is ==>', this.dataTable);
$('#issueTable tbody').on('click', 'button', function () {
const data = this.dataTable.row($(this).parents('tr').data);
console.log('Data is ==>', data);
});
}
buttonInRowClick(event: any): void {
event.stopPropagation();
console.log('Button in the row clicked.');
this.router.navigate(['/event-viewer']);
}
wholeRowClick(): void {
console.log('Whole row clicked.');
}
// ngAfterViewInit() {
// this.renderer.listenGlobal('document', 'click', (event) => {
// if (event.target.hasAttribute("view-person-id")) {
// this.router.navigate(["/person/" + event.target.getAttribute("view-person-id")]);
// }
// });
// }
}
When I run that code, I get this error:
ERROR TypeError: Cannot read property 'replace' of null
at _fnSortAria (VM1053 scripts.js:16659)
at jQuery.fn.init.<anonymous> (VM1053 scripts.js:11820)
at VM1053 scripts.js:17237
at Function.map (VM1053 scripts.js:456)
at _fnCallbackFire (VM1053 scripts.js:17236)
at _fnDraw (VM1053 scripts.js:14107)
at _fnReDraw (VM1053 scripts.js:14150)
at _fnInitialise (VM1053 scripts.js:15333)
at loadedInit (VM1053 scripts.js:11893)
at HTMLTableElement.<anonymous> (VM1053 scripts.js:11905)
How do I fix this error?
I'm not sure about this but I think here is the problem:
{
title: null,
data: null,
render: function(data, type, full) {
return '<a class="btn btn-info btn-sm" href=#/>' + 'View' + '</a>';
}
}
Even if you don't use any field inside return, you should put some column name, for example data: 'u_product':
{
title: null,
data: 'u_product',
render: function(data, type, full) {
return '<a class="btn btn-info btn-sm" href=#/>' + 'View' + '</a>';
}
}
I recommend you to use the primary key column of the table, maybe in the future it will be useful in the button.

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/

Remove category_id property from ColumnValue object in Vue template

I want to remove category_id from {{ columnValue }}, but what is the best way to to that, because i need category_id in the first part ?
<table>
<tr>
<td v-for="columnValue, column in record">
<template v-if="editing.id === record.id && isUpdatable(column)">
<template v-if="columnValue === record.category_id">
<select class="form-control" v-model="editing.form[column]">
<option v-for="column in response.joins">
{{ column.category }} {{ column.id }}
</option>
</select>
</template>
<template v-else="">
<div class="form-group">
<input class="form-control" type="text" v-model= "editing.form[column]">
<span class="helper-block" v-if="editing.errors[column]">
<strong>{{ editing.errors[column][0]}}</strong>
</span>
</div>
</template>
</template>
<template v-else="">
{{ columnValue }} // REMOVE category_id here!
</template>
</td>
</tr>
</table>
And the view (its the number under group i want to remove):
The DataTable view
The script:
<script>
import queryString from 'query-string'
export default {
props: ['endpoint'],
data () {
return {
response: {
table: null,
columntype: [],
records: [],
joins: [],
displayable: [],
updatable: [],
allow: {},
},
sort: {
key: 'id',
order: 'asc'
},
limit: 50,
quickSearchQuery : '',
editing: {
id: null,
form: {},
errors: []
},
search: {
value: '',
operator: 'equals',
column: 'id'
},
creating: {
active: false,
form: {},
errors: []
},
selected: []
}
},
filters: {
removeCategoryId: function (value) {
if (!value) return ''
delete value.category_id
return value
}
},
computed: {
filteredRecords () {
let data = this.response.records
data = data.filter((row) => {
return Object.keys(row).some((key) => {
return String(row[key]).toLowerCase().indexOf(this.quickSearchQuery.toLowerCase()) > -1
})
})
if (this.sort.key) {
data = _.orderBy(data, (i) => {
let value = i[this.sort.key]
if (!isNaN(parseFloat(value)) && isFinite(value)) {
return parseFloat(value)
}
return String(i[this.sort.key]).toLowerCase()
}, this.sort.order)
}
return data
},
canSelectItems () {
return this.filteredRecords.length <=500
}
},
methods: {
getRecords () {
return axios.get(`${this.endpoint}?${this.getQueryParameters()}`).then((response) => {
this.response = response.data.data
})
},
getQueryParameters () {
return queryString.stringify({
limit: this.limit,
...this.search
})
},
sortBy (column){
this.sort.key = column
this.sort.order = this.sort.order == 'asc' ? 'desc' : 'asc'
},
edit (record) {
this.editing.errors = []
this.editing.id = record.id
this.editing.form = _.pick(record, this.response.updatable)
},
isUpdatable (column) {
return this.response.updatable.includes(column)
},
toggleSelectAll () {
if (this.selected.length > 0) {
this.selected = []
return
}
this.selected = _.map(this.filteredRecords, 'id')
},
update () {
axios.patch(`${this.endpoint}/${this.editing.id}`, this.editing.form).then(() => {
this.getRecords().then(() => {
this.editing.id = null
this.editing.form = {}
})
}).catch((error) => {
if (error.response.status === 422) {
this.editing.errors = error.response.data.errors
}
})
},
store () {
axios.post(`${this.endpoint}`, this.creating.form).then(() => {
this.getRecords().then(() => {
this.creating.active = false
this.creating.form = {}
this.creating.errors = []
})
}).catch((error) => {
if (error.response.status === 422) {
this.creating.errors = error.response.data.errors
}
})
},
destroy (record) {
if (!window.confirm(`Are you sure you want to delete this?`)) {
return
}
axios.delete(`${this.endpoint}/${record}`).then(() => {
this.selected = []
this.getRecords()
})
}
},
mounted () {
this.getRecords()
},
}
</script>
And here is the json:
records: [
{
id: 5,
name: "Svineskank",
price: "67.86",
category_id: 1,
category: "Flæskekød",
visible: 1,
created_at: "2017-09-25 23:17:23"
},
{
id: 56,
name: "Brisler vv",
price: "180.91",
category_id: 3,
category: "Kalvekød",
visible: 0,
created_at: "2017-09-25 23:17:23"
},
{
id: 185,
name: "Mexico griller 500 gram",
price: "35.64",
category_id: 8,
category: "Pølser",
visible: 0,
created_at: "2017-09-25 23:17:23"
},
{
id: 188,
name: "Leverpostej 250 gr.",
price: "14.25",
category_id: 9,
category: "Pålæg",
visible: 1,
created_at: "2017-09-25 23:17:23"
},
}]
.. and so on......
I would recommend using a filter in Vue to remove the property, such as:
new Vue({
// ...
filters: {
removeCategoryId: function (value) {
if (!value) return ''
delete value.category_id
return value
}
}
})
An then use this in your template:
{{ columnValue | removeCategoryId }}
Update: I misunderstood the scope of the loop. This works, and I verified on jsfiddle: https://jsfiddle.net/spLxew15/1/
<td v-for="columnValue, column in record" v-if="column != 'category_id'">