Mutate object to use in MDbootstrap Vue - vue.js

I am using MDBootstrap for Vue js, the way to use datatable is to to use prop :data="data" which I calling here tableData and I have columns ready but I need to mutate the rows inside my object,
I couldn't do that with rows:this.data or rows:data which is where I handle getting data from my server.
How can I handle this and mutate rows inside tableData ?
<template>
<div class="container-fluid">
<ProgressSpinner v-if="isLoading"/>
<mdb-datatable :data="tableData" striped bordered/>
</div>
</template>
<script>
import ProgressSpinner from './Preloader'
import DataTable from 'vue-materialize-datatable'
import { mdbDatatable } from 'mdbvue'
export default {
name: 'Companies',
data: () => ({
data: [],
tableData: {
columns: [
{ label: 'ID', field: 'id', sort: 'asc' },
{ label: 'Name', field: 'name' },
{ label: 'Phone', field: 'phone', sort: 'asc' },
{ label: 'Email', field: 'email', sort: 'asc' },
{ label: 'City', field: 'city', sort: 'asc' },
{ label: 'Join Date', field: 'joined_at' }
],
rows: []
},
}),
created() {
this.$store
.dispatch('allCompanies')
.then(() => {
this.data = this.$store.getters.getAllCompanies
})
.catch(() => {
this.customerErrors = this.$store.getters.customerError
})
},
computed: {
isLoading() {
return this.$store.getters.customersAreLoading
},
},
components: {
ProgressSpinner,
datatable: DataTable,
mdbDatatable
},
mounted() {
$('.container-fluid').bootstrapMaterialDesign()
}
}
</script>

You should assign the value to tableData.rows when available.
this.$store
.dispatch('allCompanies')
.then(() => {
this.tableData.rows = this.$store.getters.getAllCompanies
})

Related

Cannot read properties of undefined on formatter, but data is showing fine

I have a bootstrap table that shows a list of appliances. I am importing my data with Axios and for this specific table I am outputting data from two database tables, so I have one object which is called applianceReferences which stores another object called activeAppliances.
Not sure if it is relevant for this question, but just so you know.
Before talking about the problem, let me just post the whole code and below I will talk about the section that is giving me issues.
<template>
<b-container class="my-2">
<b-card v-if="showTable" class="ml-4 mr-4">
<b-table
search-placeholder="search"
:filter-included-fields="fields.map(f => f.key)"
include-filter
:items="applianceReferences"
:fields="fields"
/>
</b-card>
</b-container>
</template>
<script>
import {applianceService} from "#/services/appliance";
import CommonCollapsible from "#/components/common/CommonCollapsible";
import moment from 'moment';
export default {
components: { CommonCollapsible, CommonTable },
props: {
ownerId: String,
ownerType: String,
showDocuments: Boolean,
goToAppliances: "",
importAppliances: ""
},
data() {
return {
applianceReferences: [],
showTable: true
}
},
computed: {
fields() {
return [
{
key: 'referenceName',
label: this.$t('referenceName'),
sortable: true
},
{
key: 'activeAppliance.type',
label: this.$t('type'),
sortable: true,
},
{
key: 'activeAppliance.brandName',
label: this.$t('brand'),
sortable: true
},
{
key: 'activeAppliance.purchaseDate',
label: this.$t('purchaseDate'),
sortable: true,
template: {type: 'date', format: 'L'}
},
{
key: 'activeAppliance.warrantyDuration',
label: this.$t('warrantyDuration'),
sortable: true,
formatter: (warrantyDuration, applianceId, appliance) =>
this.$n(warrantyDuration) + ' ' +
this.$t(appliance.activeAppliance.warrantyDurationType ?
`model.appliance.warrantyDurationTypes.${appliance.activeAppliance.warrantyDurationType}` :
''
).toLocaleLowerCase(this.$i18n.locale),
sortByFormatted: (warrantyDuration, applianceId, appliance) =>
appliance.activeAppliance.warrantyDurationType === 'YEARS' ? warrantyDuration * 12 : warrantyDuration
},
{
key: 'activeAppliance.purchaseAmount',
label: this.$t('amount'),
sortable: true,
template: {
type: 'number', format: {minimumFractionDigits: '2', maximumFractionDigits: '2'},
foot: sum
}
},
{
key: 'actions',
template: {
type: 'actions',
head: [
{
text: 'overviewOfAppliances',
icon: 'fas fa-fw fa-arrow-right',
action: this.createAppliance
},
{
icon: 'fas fa-fw fa-file-excel',
action: this.importAppliance,
tooltip: this.$t('importAppliances'),
}
],
cell: [
{
icon: 'fa-trash',
variant: 'outline-danger',
action: this.remove
},
]
}
}
]
},
},
methods: {
load() {
Object.assign(this.$data, this.$options.data.apply(this));
this.applianceReferences = null;
applianceService.listApplianceReferences(this.ownerId).then(({data: applianceReferences}) => {
this.applianceReferences = applianceReferences;
this.applianceReferences.forEach( reference => {
applianceService.listAppliances(reference.id).then(result => {
this.$set(reference, 'appliances', result.data);
this.$set(reference, 'activeAppliance', result.data.find(appliance => appliance.active))
this.loaded = true
})
})
}).catch(error => {
console.error(error);
})
},
createAppliance(){
this.goToAppliances()
},
importAppliance(){
this.importAppliances()
},
},
watch: {
ownerId: {
immediate: true,
handler: 'load'
}
},
}
</script>
Okay, so the error occurs in this specific property:
{
key: 'activeAppliance.warrantyDuration',
label: this.$t('warrantyDuration'),
sortable: true,
formatter: (warrantyDuration, applianceId, appliance) =>
this.$n(warrantyDuration) + ' ' +
this.$t(appliance.activeAppliance.warrantyDurationType ?
`model.appliance.warrantyDurationTypes.${appliance.activeAppliance.warrantyDurationType}` :
''
).toLocaleLowerCase(this.$i18n.locale),
sortByFormatted: (warrantyDuration, applianceId, appliance) =>
appliance.activeAppliance.warrantyDurationType === 'YEARS' ? warrantyDuration * 12 : warrantyDuration
},
What I am basically doing here is combining two values from the object: warrantyDuration and warrantyDurationType and putting them in one single row in my bootstrap table.
The problem is that this is giving me an error: Cannot read properties of undefined (reading 'warrantyDurationType'
Yet the data actually outputs normally.
So what exactly does it want me to do?
I tried wrapping a v-if around the table to make sure that the application checks if the data exist before outputting it, but this does not solve the issue.
<div v-if="applianceReferences && applianceReferences.activeAppliance">
<b-card v-if="showTable" class="ml-4 mr-4">
<common-table
search-placeholder="search"
:filter-included-fields="fields.map(f => f.key)"
include-filter
:items="applianceReferences"
:fields="fields"
/>
</b-card>
</div>
Last, just to give you a full overview, my array looks like this:
Any ideas?

How to make nested properties reactive in Vue

I hava a component with complex nested props:
<template>
<div>
<a-tree :tree-data="data" :selected-keys="[selectedKey]" #select="onSelect" />
<div>
<a-input v-model="sheet[selectedKey].tableName" />
<ux-grid ref="previewTable">
<ux-table-column v-for="field in sheet[selectedKey].fields"
:key="field.name" :field="field.name">
<a-input slot="header" v-model="field.label" />
</ux-table-column>
</ux-grid>
</div>
</div>
</template>
<script>
export default {
props: {
previewData: { type: Array, default: () => [] }
},
data () {
return {
data: this.previewData,
selectedKey: '0-0-0',
sheet: { 'none': { tableName: null, fields: [] } }
}
},
created () {
this.data.forEach((file, fid) => {
file.sheets.forEach((sheet, sid) => {
this.$set(this.sheet, `0-${fid}-${sid}`, {
tableName: sheet.label,
fields: sheet.fields.map(field => ({ ...field }))
})
})
})
},
mounted () {
this.$refs.previewTable.reloadData(this.data[0].sheets[0].data)
},
methods: {
onSelect ([ key ], { node }) {
if (key !== undefined && 'fields' in node.dataRef) {
this.selectedKey = key
this.$refs.previewTable.reloadData(node.dataRef.data)
} else {
this.selectedKey = 'none'
this.$refs.previewTable.reloadData()
}
}
}
}
</script>
And previewData props is something like:
{
name: "example.xlsx",
filename: "80b8519f-f7f1-4524-9d63-a8b6c92152b8.xlsx",
sheets: [{
name: "example",
label: "example",
fields:[
{ label: "col1", name: "col1", type: "NUMBER" },
{ label: "col2", name: "col2", type: "STRING" }
]
}]
}
</script>
This component allows user to edit the label properties. I have to make Object sheet reactive to user input, and I tried $set and Object.assign, it works for sheets.label but fields[].label is still not reactive.
I wish to know what would be the declarative (and optimal) solution for it
You might need a watcher or computed property in React for previewData to be changed.

datatable vueJS don´t show data

I´m trayin integrate datatable in my component vue. For this i´m using this library:
https://jamesdordoy.github.io/
i´m install in my proyect, i has configured it, created my component and include in my view, but returned me this message:
Invalid prop: type check failed for prop "data". Expected Object, got Array
my component it´s:
<template>
<div>
<div class="row justify-content-center w-100">
<data-table :data="data" :columns="columns" #on-table-props-changed="reloadTable"></data-table>
</div>
</div>
</template>
<script>
export default {
data() {
return {
url: "/admin/vue/getAllUsers",
data: {},
tableProps: {
search: '',
length: 10,
column: 'id',
dir: 'asc'
},
columns: [
{
label: 'ID',
name: 'id',
orderable: true,
},
{
label: 'Name',
name: 'name',
orderable: true,
},
{
label: 'Email',
name: 'email',
orderable: true,
},
]
}
},
created() {
this.cargar(this.url);
},
methods:{
cargar(url = this.url, options = this.tableProps){
axios.get(url, { params: options })
.then((response) => {
this.data = response.data;
})
.catch((error) => console.error(error));
},
reloadTable(tableProps) {
this.cargar(this.url, tableProps);
}
},
}
</script>
i was trayed with response.data[0] the error disappear but my table it´s empty, i don´t know that i´m doing wrong.
In my controller i have this:
return User::all();
i´m working with laravel 8
thanks for help
you should pass props :items = "data" instead of :data = "data"

Vue Cant render data from axios inside datatable

i am trying to get data from axios and then render it in datable component
how you can see the hardcoded data is renderd every time , but i cant implement data from axios call,
i am loading same format data from axios and then try to update my hardocded rows but nothing happens, i am not shure thath i am doing it right way
can somone help
<template>
<div>
<mdb-datatable-2 v-model="data" />
</div>
</template>
<script>
import { mdbDatatable2 } from 'mdbvue';
export default {
name: 'Datatable',
components: {
mdbDatatable2
},
data() {
return {
data: {
columns: [
{
label: 'Account',
field: 'account',
sort: true
},
{
label: 'bt_mac',
field: 'bt_mac',
sort: true
},
{
label: 'rssi',
field: 'rssi',
sort: true
},
{
label: 'time_stamp',
field: 'time_stamp',
sort: true
}
],
rows: [{
account: 'Tiger Nixon',
bt_mac: 'System Architect',
rssi: 'Edinburgh',
time_stamp: '2011/04/25',
time: '2011/04/25'
},
{
account: 'Garrett Winters',
bt_mac: 'Accountant',
rssi: 'Tokyo',
time_stamp: '2018/04/25',
time: '2011/04/25'
},
{
account: 'Ashton Cox',
bt_mac: 'unior Technical Author',
rssi: 'San Francisco',
time_stamp: '2009/01/12',
time: '2011/04/25'
},]
}
}
},
methods: {
getSnomData () {
this.axios.get('http://172.27.11.174:1818/testJson').then((response) => {
console.log(this.data);
console.log(response.data.rows); this.rows = response.data;
console.log(this.data);
return response;
})
}
},
created() {
this.getSnomData();
}
,
mounted () {
this.getSnomData();
}
}
</script>
this.data.rows = response.data.data;
that helped in my case

Make computations in a child component after axios requests in a parent component

Here is my problem : I have a parent component Edit that makes several axios requests, the results of these requests are passed down to a child component (Menu in my example).
<template>
<div>
<p>
Order Id : {{ this.$route.params.id }}
</p>
<head-fields :menu="menu"></head-fields>
<div>
<b-tabs content-class="mt-3">
<b-tab title="Menu" active>
<Menu :menuItems="items" :nutritionalValues="nutritionalValues"></Menu>
</b-tab>
<b-tab title="Specifications">
<specifications :specifications="specifications">
</specifications>
</b-tab>
<b-tab title="Redundancies"><p>I'm the redundancies tab!</p></b-tab>
</b-tabs>
</div>
</div>
</template>
<script>
import HeadFields from "./HeadFields";
import Menu from "./Menu";
import Specifications from "./Specifications";
export default {
name: "Edit",
components: {HeadFields, Menu, Specifications},
data(){
return{
menu: {},
loaded: false,
items: {},
nutritionalValues: {},
specifications: {},
error:{}
}
},
created(){
this.find(this.$route.params.id);
},
methods:{
find(id){
axios.get('/menusV2/'+id)
.then(response => {
this.loading = false;
this.menu = response.data[0];
this.fetchMenu(this.menu.orderId);
this.fetchSpecifications(this.menu.orderId);
});
return this.menu;
},
fetchMenu(orderId){
// console.log(orderId);
axios
.get('/menusV2/'+orderId+'/menu')
.then(response => {
this.loading = false;
this.items = response.data.items;
this.nutritionalValues = response.data.nutritionalValues;
})
.catch(error => {
this.loading = false;
this.error = error.response.data.message || error.message;
})
},
fetchSpecifications(orderId){
axios
.get('/menusV2/'+orderId+'/specifications')
.then(response => {
this.loading = false;
this.specifications = response.data;
// this.checkSpecifications();
})
.catch(error => {
this.loading = false;
// this.error = error.response.data.message || error.message;
})
}
}
}
</script>
The data is passed down to the child component "Menu" as a prop :
<template>
<div class="panel panel-default">
<b-table
striped hover
:items="menuItems"
:fields="fields"
:primary-key="menuItems.pivotId"
>
</b-table>
</div>
</template>
<script>
export default {
name: "Menu",
props: ['menuItems', 'nutritionalValues'],
data() {
return {
loading: true,
perPage: ['10', '25', '50'],
rowSelected: true,
fields: [
{key: "meal", label: "Meal", sortable: true},
{key: "category", label: "Category", sortable: true},
{key: "itemName", label: "Name", sortable: true},
{key: "noOfServing", label: "Serving", sortable: true},
{key: "weight", label: "Weight", sortable: true},
{key: "calories", label: "Calories", sortable: true},
{key: "carbs", label: "Carbs", sortable: true},
{key: "proteins", label: "Proteins", sortable: true},
{key: "fats", label: "Fats", sortable: true},
]
}
},
mounted(){
this.checkSpecifications();
},
methods:{
searchIngredientSpecification(itemId, itemName, specifications){
//Checking of the ingredients name
for (var i=0; i < specifications.length; i++) {
if (specifications[i].itemName === itemName) {
console.log("Specification ! "+itemName);
}
}
//Checking of the nutritional properties
var ingredientNutritionalProperties = {};
axios
.get('/menusV2/'+itemId+'/ingredient/nutritionalProperties')
.then(response => {
ingredientNutritionalProperties = response.data;
});
console.log("Ingredient : " + itemName);
console.log(ingredientNutritionalProperties);
},
searchDishSpecification(itemId, itemName, specifications){
//Checking of the ingredients name
for (var i=0; i < specifications.length; i++) {
if (specifications[i].itemName === itemName) {
console.log("Specification ! "+itemName);
}
}
//Checking of the nutritional properties
var dishNutritionalProperties = {};
axios
.get('/menusV2/'+itemId+'/dish/nutritionalProperties')
.then(response => {
dishNutritionalProperties = response.data;
});
console.log("Dish : " + itemName);
console.log(dishNutritionalProperties);
var ingredientsDish = {};
var ingredientsNutritionalProperties = {};
axios
.get('/menusV2/'+itemId+'/getIngredients')
.then(response => {
ingredientsDish = response.data.ingredients;
ingredientsNutritionalProperties = response.data.nutritionalProperties;
});
console.log("Dish : " + itemName);
console.log(ingredientsDish);
console.log(ingredientsNutritionalProperties);
},
checkSpecifications(){
console.log("Check Specifications launched !");
console.log(this.menuItems);
var items = this.menuItems;
items.forEach(
element => {
switch(element.type){
case 'Ingredient':
this.searchIngredientSpecification(element.itemId,element.itemName,this.specifications);
break;
case 'Dish':
this.searchDishSpecification(element.itemId,element.itemName,this.specifications);
break;
}
}
);
},
}
}
</script>
The problem I have is around the methods in the child component that are fired before the menuItems prop is filled with data from the axios request.
I think that a possible fix to this problem would be to use computed properties or watchers but I don't really know if it will help me..
Here is the error that is thrown :
Thanks for your help !
You are getting the error because when the checkSpecifications method is run on mount, this.menuItems is not an array and forEach must only be used on arrays.
One option is to add a watcher to menuItems and only once it has been filled with a value (making sure it's an array) then run the checkSpecifications method.
Alternatively, you could define menuItems as an array and provide a default value.
props: {
menuItems: {
type: Array,
default: []
},
nutritionalValues: {
type: Array,
default: []
}
It's always good practice to define the type of your props.