Open ghost opened 2 years ago
When remove the return ...
line, I could only see the table header and not the data.
<script setup>
import {ref, reactive, onMounted} from 'vue';
import {TabulatorFull as Tabulator} from 'tabulator-tables';
const table = ref(null);
const tabulator = ref(null);
const tableData = reactive([
{ id: 1, name: "Oli Bob", age: "1" },
{ id: 2, name: "Mary May", age: "1", col: "blue", dob: "14/05/1982" },
]);
onMounted(() => {
tabulator.value = new Tabulator(table.value, {
data: tableData.value, //link data to table
reactiveData:true, //enable data reactivity
columns: [
{ title: "Name", field: "name", width: 150 },
{ title: "Age", field: "age", hozAlign: "left", formatter: "progress" },
{ title: "Favourite Color", field: "col" },
], //define table columns
});
console.log(table)
})
</script>
<template>
<div ref="table"></div>
</template>
That example was provided by a member of the community. If you would like to see any amends to it, please post a working example code here and I would be happy to update the website
Waiting for contributors.
@GuidanceZero the hint is on you to provide the contribution when you resolve your issue. That's how open source works, we all help eachother
Seem to have the same issue, the header is appear but tableData is not showing.
<script setup>
import {ref, reactive, onMounted} from 'vue';
import {TabulatorFull as Tabulator} from 'tabulator-tables';
const table = ref(null);
const tabulator = ref(null);
const tableData = reactive([
{ id: 1, name: "Oli Bob", age: "1" },
{ id: 2, name: "Mary May", age: "1", col: "blue", dob: "14/05/1982" },
]);
onMounted(() => {
tabulator.value = new Tabulator(table.value, {
data: tableData.value, //link data to table
reactiveData:true, //enable data reactivity
columns: [
{ title: "Name", field: "name", width: 150 },
{ title: "Age", field: "age", hozAlign: "left", formatter: "progress" },
{ title: "Favourite Color", field: "col" },
],
});
console.log(table.value)
return { tabulator, table };
})
</script>
<template>
<div ref="table"></div>
</template>
ReactiveData not work for me also in Vue 2.5.11 with Vuex 3.0.1. If I use the watch for listTaskMap with setData data is showing.
<template>
<div class="list-task-table">
<div ref="listTaskTable"></div>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import { Tabulator, SortModule, FormatModule, ReactiveDataModule } from 'tabulator-tables';
import 'tabulator-tables/dist/css/tabulator_bootstrap3.css';
export default {
name: 'ListTaskTable',
data() {
return {
tabulator: undefined,
};
},
mounted() {
Tabulator.registerModule([ SortModule, FormatModule, ReactiveDataModule ]);
this.tabulator = new Tabulator(this.$refs.listTaskTable, {
data: this.listTaskMap,
debugInvalidComponentFuncs: false,
reactiveData: true,
columns: this.columns,
});
},
computed: {
...mapGetters({
listTaskMap: 'listTask/getListTaskMap',
dateFormat: 'organization/getDateFormat',
}),
columns() {
const bind = this;
const column = [
{
title: 'Number',
field: 'number',
},
{
title: 'Created At',
field: 'createdAt',
formatter(cell, formatterParams) {
const value = cell.getValue();
return bind.$moment.unix(value)
.format(bind.dateFormat);
},
},
];
return column;
},
},
};
</script>
reactivity not working for me either on Vue 3.2.45, I guess it's a problem on tabulator on the way it handles the data with the proxy
Hey Guys,
Im happy help resolve this, but i am not a user of Vuex so will need a member of the community to investigate this one further if they want a resolution.
Start up a PR if you are interested in helping out.
Cheers
Oli :)
@GuidanceZero I believe there is a slight issue with the example in the documentation which is why it is not working for you. Similar to what @joaquinjsb alluded to it's related to how Vue provides you with the proxy reference to your reactive data.
<template>
<div ref="table"></div>
<button @click="addRow">Add Data</button>
</template>
<script setup>
import {ref, reactive, onMounted} from 'vue';
import {TabulatorFull as Tabulator} from 'tabulator-tables';
const table = ref(null);
const tabulator = ref(null);
const tableData = reactive([
{ id: 1, name: "Oli Bob", age: "1" },
{ id: 2, name: "Mary May", age: "1", col: "blue", dob: "14/05/1982" },
]);
const addRow = () => {
tableData.push({id:3,name:'Aaron',age:"28"})
}
onMounted(() => {
tabulator.value = new Tabulator(table.value, {
data: tableData, //link data to table
reactiveData:true, //enable data reactivity
columns: [
{ title: "Name", field: "name", width: 150 },
{ title: "Age", field: "age", hozAlign: "left", formatter: "progress" },
{ title: "Favourite Color", field: "col" },
],
});
})
</script>
<style></style>
As you can see from my example when you're using a reactive
reference you don't need to reference it with .value
inside the data instance of Tabulator. This is because it's value is automatically proxied by Vue so by adding .value
you are actually getting undefined
back. For further context If you were using a ref
then you would need to use .value
Also since you are using the script setup version of Vue you can remove the return statement altogether, this is only required if you are using script setup with a setup()
function e.g.
<template>
<div ref="table"></div>
<button @click="addRow">Add Data</button>
</template>
<script>
import {ref,reactive,onMounted} from 'vue';
import {TabulatorFull as Tabulator} from 'tabulator-tables'; //import Tabulator library
export default {
setup() {
const table = ref(null); //reference to your table element
const tabulator = ref(null); //variable to hold your table
const tableData = reactive([
{id:1, name:"Billy Bob", age:"12", gender:"male", height:1, col:"red", dob:"", cheese:1},
{id:2, name:"Mary May", age:"1", gender:"female", height:2, col:"blue", dob:"14/05/1982", cheese:true},
{id:3, name:"Christine Lobowski", age:"42", height:0, col:"green", dob:"22/05/1982", cheese:"true"},
{id:4, name:"Brendon Philips", age:"125", gender:"male", height:1, col:"orange", dob:"01/08/1980"},
{id:5, name:"Margret Marmajuke", age:"16", gender:"female", height:5, col:"yellow", dob:"31/01/1999"},
]); //data for table to display
const addRow = () => {
tableData.push({id:6, name:"Aaron Mcguire", age:"28", gender:"male", height:1, col:"yellow", dob:"", cheese:false})
}
onMounted(() => {
//instantiate Tabulator when element is mounted
tabulator.value = new Tabulator(table.value, {
data: tableData, //link data to table
reactiveData:true, //enable data reactivity
columns: [
{ title: "Name", field: "name", width: 150 },
{ title: "Age", field: "age", hozAlign: "left", formatter: "progress" },
{ title: "Favourite Color", field: "col" },
{ title: "Date Of Birth", field: "dob", hozAlign: "center" },
{ title: "Rating", field: "rating", hozAlign: "center", formatter: "star" },
{ title: "Passed?", field: "passed", hozAlign: "center", formatter: "tickCross" }
], //define table columns
});
})
// expose to template and other options API hooks
return {table,tabulator,addRow }
},
}
</script>
<style></style>
Hopefully this comment helps a few people that like me were confused by the subtle issue.
@AaronAMcGuire thank you very much for the detailed answer, that is very helpful. I will get the docs update this week to make it easier to understand.
Cheers
Oli :)
@olifolkerd Apologies after a little more real-world testing there is actually a valid issue
I'll try my best to give an accurate description.
<template>
{{tableData}}
</template>
<script setup>
import {ref} from 'vue';
const tableData = ref([]);
fetch("https://api.nationalize.io/?name=aaron",{method:"GET",headers:{"Content-Type":"application/json"}})
.then(response => response.json())
.then(data => {
tableData.value.push(...data.country)
console.log(tableData);
}).catch(error => { console.error(error)})
</script>
<style></style>
Take the basic example above this is not using Tabulator if you run this SFC Playground Demo
You can see an api call has been sent, the data has been returned and the tableData
ref has automatically updated it's value on the page, this is an example of everything working as expected.
If we now introduce Tabulator :
<template>
<div ref="table"></div>
{{tableData}}
</template>
<script setup>
import {ref, onMounted} from 'vue';
import {TabulatorFull as Tabulator} from 'tabulator-tables';
const table = ref(null);
const tabulator = ref(null);
const tableData = ref([]);
fetch("https://api.nationalize.io/?name=aaron",{method:"GET",headers:{"Content-Type":"application/json"}})
.then(response => response.json())
.then(data => {
tableData.value.push(...data.country)
}).catch(error => { console.error(error)})
onMounted(() => {
tabulator.value = new Tabulator(table.value, {
data: tableData.value, //link data to table
reactiveData:true, //enable data reactivity
columns: [
{ title: "Country ID", field: "country_id" },
{ title: "Probability", field: "probability"},
],
});
})
</script>
<style></style>
What happens now is the api call is made, the data in the table is updated as you would expect but the tableData
reference does not update as you would expect indicating that the definition has lost reactivity.
Next I tried to confirm it was Tabulator breaking the reactivity. So I looked in ReactiveData.js
to see how it worked and noticed you were re-defining methods temporarily then running a .apply to call the original method once you had done your processing. I found a method you hadn't redefined so I created an example with both an Object.assign
and a .push
the theory being that since Tabulator doesn't touch Object.assign
it will retain reactivity and on the flip side using .push will break reactivity but will still update the table content.
<template>
<div ref="table"></div>
{{tableData}}
</template>
<script setup>
import {ref,reactive, onMounted} from 'vue';
import {TabulatorFull as Tabulator} from 'tabulator-tables';
const table = ref(null);
const tabulator = ref(null);
const tableData = reactive([]);
fetch("https://api.nationalize.io/?name=aaron",{method:"GET",headers:{"Content-Type":"application/json"}})
.then(response => response.json())
.then(data => {
Object.assign(tableData,data.country)
tableData.push(...data.country);
}).catch(error => { console.error(error)})
onMounted(() => {
tabulator.value = new Tabulator(table.value, {
data: tableData, //link data to table
reactiveData:true, //enable data reactivity
columns: [
{ title: "Country ID", field: "country_id" },
{ title: "Probability", field: "probability"},
],
});
})
</script>
<style></style>
This confirmed that it related to the Tabulator reactivity module, so I stepped through the code for the .push
. As expected it does the standard processing then does a .apply
to call the vanilla method passing in the context and arguments
result = self.origFuncs.push.apply(data, arguments);
Which then runs Vue's version which does a very similar thing..
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(key => {
instrumentations[key] = function (...args) {
pauseTracking();
const res = toRaw(this)[key].apply(this, args);
resetTracking();
return res;
};
});
From the minimal debugging I have conducted it looks like the tabulator data reactivity module runs it's version of .push
Part of that process includes a self.block
to prevent infinite looping.
It then runs a .apply
- which triggers a call to Vue which does it's own version of .apply
( as seen in the snippet above) which in turn triggers another instance of the tabular reactivity module but since the very first instance added a self.block
the processing fails the if
statement which means Vue's version of .apply is essentially terminated by tabulators function.
I hope that makes as much sense as it does in my head!
Hi,
I faced the same issue. Any update on this?
Thanks
The same here, the reactivity does not work when the data are loaded from an API, but it works just fine if I initialize the table object after the data are finished loading. Also adding new elements in the array do not reflect on the table. Unfortunately using the library at this state in Vuejs is simply impossible due to these critical bugs. Hopefully these issues will be addressed as this seems to be a great library with many options otherwise. For now have to look somewhere else and find something more suitable for my basic use cases.
Hey Folks!
Recativity in Nuxt3
IN the example below, we have a simple table that uses reactive data on an object thats passed into the tabulator instance on mounted
In this simple example, we expect the tabulator table to update after 1000 miliseconds, however it does not
The data in the vue/nuxt devtools shows that the array has indeed been updated with the new array item however the table does not reflect those changes
<template>
<div ref="tableDiv" />
</template>
<script setup lang="ts">
const props = defineProps({
data: {
type: Array,
required: true
},
cols: {
type: Array,
required: true
},
options: {
type: Object,
default: () => ({})
}
})
const tableDiv = ref(null)
const tabulator = ref(null)
const cols = ref([
{ title: "ID", field: "ID" },
{ title: "Name", field: "Name" }
])
let data = reactive([
{ ID: 1, Name: "John" },
{ ID: 2, Name: "Jane" }
])
// static defaults for basic table
const defaults = {
layout: "fitColumns",
responsiveLayout: "collapse",
reactiveData: true,
data: data,
columns: cols.value
}
// Merge defaults with any options passed in
const options = ref(Object.assign({}, defaults, props.options))
onMounted(() => {
// instantiate Tabulator when element is mounted
// @ts-ignore
tabulator.value = new Tabulator(tableDiv.value, options.value)
})
setTimeout(() => {
data = [{ ID: 3, Name: "Bob" }]
}, 1000)
</script>
In a more complex example, we call our API, set the local data and update it based on a filter. Again, the vue API updates the array, but tabulators table is not updated in the UI. Seems that reactive data is not working 😦 seems to be a universal issue with vue/nuxt users! Appreciate it!
What I don't understand why they label it as suggested feature and not as bug fix.
FIX:
Alright, in my code above we have a watch
however, it wasn't returning the prop value. This works now and makes the data reactive:
watch(() => props.data, (newData) => {
// update table data when props.data changes
tabulator.value.replaceData(newData)
}, { deep: true })
Notice the watch()
now returns the props.data - this is a vue 3 composition API thing. You can't watch the prop directly, you must return it!
ALSO: the reactiveData: true
does nothing seemingly, so I removed it
NOW, the this ref
const tabulator = ref<any>(null)
gets the updated value(array) and boom, its reactive!
What I don't understand why they label it as suggested feature and not as bug fix.
see fix below!
I'll echo what other's have said. This seems like a great library, but as far as I can tell it's completely broken when trying to use with Vue 3. The document here should be removed until the issues have been fixed.
I got a small test working locally with Nuxt and vue 3
<script lang="ts" setup>
/**
*
* Component Description:Desc
*
* @author Reflect-Media <Leamsigc>
* @version 0.0.1
*
* @todo [ ] Test the component
* @todo [ ] Integration test.
* @todo [✔] Update the typescript.
*/
import "tabulator-tables/dist/css/tabulator.min.css";
import {
TabulatorFull as Tabulator,
type ColumnDefinition,
} from "tabulator-tables";
const table = ref(null); //reference to your table element
const tabulator = ref<Tabulator | null>(null); //variable to hold your table
const columns = ref<ColumnDefinition[]>([
{ title: "Name", field: "name", width: 150 },
{ title: "Age", field: "age", hozAlign: "left", formatter: "progress" },
{ title: "Favourite Color", field: "col" },
{ title: "Date Of Birth", field: "dob", hozAlign: "center" },
{ title: "Rating", field: "rating", hozAlign: "left", formatter: "star" },
{
title: "Passed?",
field: "passed",
hozAlign: "center",
formatter: "tickCross",
},
]);
const data = ref<Record<string, any>[]>([]);
onMounted(() => {
if (!table.value) return;
tabulator.value = new Tabulator(table.value, {
data: data.value, //link data to table
reactiveData: true, //enable data reactivity
columns: columns.value, //define table columns
layout: "fitDataStretch",
});
// Set time out and create a mock promise
setTimeout(() => {
console.log("set timeout");
data.value = [
{ id: 1, name: "Oli Bob", age: "12", col: "red", dob: "" },
{ id: 2, name: "Mary May", age: "1", col: "blue", dob: "14/05/1982" },
{
id: 3,
name: "Christine Lobowski",
age: "42",
col: "green",
rating: "5",
dob: "22/05/1982",
},
{
id: 4,
name: "Brendon Philips",
age: "125",
col: "orange",
dob: "01/08/1980",
},
{
id: 5,
name: "Margret Marmajuke",
age: "16",
col: "yellow",
dob: "31/01/1999",
},
];
tabulator.value?.setData(data.value);
}, 5000);
});
</script>
<template>
<div class="max-w-screen-xl w-full">
<h3>Table here</h3>
<div ref="table"></div>
</div>
</template>
<style scoped>
</style>
Hope this help, This project seems to be a really nice project
Just to be clear, you're using the API like anyone would using plain javascript. .setData
is doing all the work here. There's no need for the 'data' property to exist at all, you just gather your data however you want and use .setData
to set it. There's also no need for reactiveData: true
in this case. Again, .setData
is doing all the work of replacing the current data with new data and redrawing the table.
The original point of the post still stands. The example is broken and the claims of it working with Vue reactivity should be removed. For anyone having trouble using Tabulator with Vue, just forgo any attempts at using Vue refs (except for referencing the div table) and use the Tabulator API as it's described in the documentation.
Yeah, that make sense, but in the documentation as well is this disclaimer
Local Data Only Reactive data functionality is only available with local data arrays set on the data property or the setData function. It is not available on ajax data sources.
That why I assume that to update the data was ok to set it this way, but it can be that is not the case, but let me have a look at it and see if I can find an example where ref to the data is working correctly
https://tabulator.info/docs/5.6/reactivity
Once a data array has been added to the data property it will be watched for changes using any of the standard row manipulation functions, push, pop, shift, unshift, and splice. Using any of these functions on the array will automatically trigger an update on the table
From what I understood base on the documentation is that you set the data, from the ref data then you can push,pop,shift or slice. The changes will be visible in the ui
https://tabulator.info/docs/5.4/frameworks
As with many newbie simpley copy and paste the code from Composition API example for Vue 3.
No devs will able to understand what is gong on and create a negative experience that made us spent hours without success.