vue-table-2 with Vuex - not able to change the count property - vue.js

I'm using the vue-table-2 component : https://github.com/matfish2/vue-tables-2 and I'm struggling to make it work as I want.
I have an API (using API Platform) which is already making all the pagination works. So when I fetch for the first time my list of companies it gives the first ten results + the total rows. I store all of this in my vuex store and I am able to display the list in my table (with useVuex false or true so I don't really understand how this parameter works). The issue is I cannot paginate because I only got ten results and can't get the total rows count to change so I do not get the pagination element at the bottom and can't bind something to it to fetch the other pages later.
Since I'm pretty new to VueJs I can't figure out how this should work with my API. Here is my code so far:
My DataTable element :
<v-client-table name="company" :columns="columns" :data="companies" :options="options" :theme="theme" id="dataTable">
<b-button slot="actions" slot-scope="props" variant="secondary" size="sm" class="btn-pill">Edit</b-button>
</v-client-table>
And my script :
<script>
import Vue from 'vue'
import { ClientTable, Event } from 'vue-tables-2'
import { mapGetters } from 'vuex'
Vue.use(ClientTable)
export default {
name: 'DataTable',
components: {
ClientTable,
Event,
},
data: function() {
return {
columns: ['name', 'actions'],
options: {
headings: {
name: 'Name',
actions: 'Actions',
},
sortable: ['name'],
filterable: ['name'],
sortIcon: {
base: 'fa',
up: 'fa-sort-asc',
down: 'fa-sort-desc',
is: 'fa-sort',
},
pagination: {
chunk: 5,
edge: false,
nav: 'scroll',
},
},
useVuex: true,
theme: 'bootstrap4',
template: 'default',
}
},
computed: {
...mapGetters({
companies: 'companyModule/companies',
totalCompanies: 'companyModule/totalCompanies',
}),
},
}
</script>
This is in my component loading the data where I specify how many items per page I want my api to send me and the page I want:
created() {
this.$store.dispatch('companyModule/FETCH_COMPANIES', {
page: 1,
nbItemPerPage: 10,
})
},
My store looks like this:
import ApiService from '#/services/APIService'
export const companyModule = {
strict: true,
namespaced: true,
state: {
companies: [],
totalCompanies: 0,
},
getters: {
companies: state => state.companies,
totalCompanies: state => state.totalCompanies,
},
mutations: {
SET_COMPANIES(state, data) {
state.companies = data.companies
state.totalCompanies = data.totalCompanies
},
},
actions: {
FETCH_COMPANIES(context, payload) {
payload.entity = 'companies'
return ApiService.get(payload).then(data => {
context.commit('SET_COMPANIES', data)
})
},
},
}
When I received my data, I stored everything in my companies state and for now I'm storing everything I'm getting from my API and this looks like this :
{
"#id": "/api/admin/companies/1",
"#type": "Company",
"id": 1,
"name": "Test Company",
}
Thanks in advance for your help !

watch: {
'companies': {
handler (newValue, oldValue) {
this.totalRows=this.companies.length;
},
deep: true
}
},
Vue “watch” is a powerful reactive option attribute to the vue framework that helps front-end developers listen to changes made on a value and then react to it.
So, once the totalRows is set, you can assign it to the table attributes and the table will change accordingly.
For my case, I used it for bootstrap table as follows:
<b-pagination
:total-rows="totalRows"
:per-page="perPage"
></b-pagination>

Related

How do you push an object into another object when the data within it gets changed?

I have a very large array of objects containing various errors logs. There are 1015 objects in total. At the moment I am saving the entire array every time I make a small change to a value in one of the objects. This leads to timeout errors because it takes too long to go through the whole array.
So instead I would like to figure out how I can change it so that the program ONLY saves an object if that object has been changed on the frontend.
So if I have 1015 objects and I only change something in object no. 2, then only object no. 2 should be saved on submit.
I was thinking, maybe it would be possible to first let the program look for any changes. Then IF a change has occured it will move that particular object to a new (empty) object, which I can then submit.
So, in my code example, I would like to have this function implemented on the computed property "Fields" which has the key "done". This contains a checkbox that sets the value error.done to true or false. So I would like for the program to check if this specific value has changed. If it has changed from true to false or vice versa I would like to send this object to a new object.
eg. if errors.done is set from true to false, then move the changed object to a new object called changedValue.
<template>
<b-container>
<b-card class="mt-4">
<h5>{{ $t('events') }}</h5>
<b-table
:items="errors"
:fields="fields"
:per-page="[10, 25, 50]"
selectable
:select-mode="'single'"
#row-selected="onRowSelected"
#row-clicked="showModal"
sort-desc
/>
</b-card>
<error-log-entry-modal ref="errorLogEntryModal" :selected-error-log="selectedRows"/>
<button #click="submit">Submit</button>
</b-container>
</template>
<script>
import {errorService} from '#/services/error';
import ErrorLogEntryModal from '#/components/error-log/ErrorLogEntryModal';
import moment from 'moment';
export default {
components: {
ErrorLogEntryModal,
},
props: {
ownerId: String
},
data() {
return {
errors: null,
selectedRows: []
};
},
computed: {
fields() {
return [
{
key: 'done',
label: '',
thStyle: 'width: 1%',
template: {
type: 'checkbox',
includeCheckAllCheckbox: true,
}
},
{
key: 'priority',
label: this.$t('errorLogs.priority'),
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: 'errorMessage',
label: this.$t('message'),
sortable: true
},
]
},
},
methods: {
load(){
errorService.getErrorLogs().then(result => {
result.data.forEach(log => log.stackTraceShort = log.stackTrace.substring(0,30));
this.errors = result.data
})
},
submit(){
return errorService.setStatusOnErrorEntryLog(this.errors).then( result => {
console.log(result)
})
},
onRowSelected(fields){
this.selectedRows = fields
},
showModal(){
if (this.selectedRows) {
this.$refs.errorLogEntryModal.show()
}
},
},
created() {
this.load()
},
};
</script>
If I have understood correctly the selected rows correspond to errors.done ? In this case you can just edit the onRowSelected method like this :
onRowSelected(fields){
this.selectedRows.push(fields)
},
Then replace this.errors by this.selectedRows in you're submit method ?

VueJs Vuetify Click on table row event not reading item

i am new to VueJs and Vuetify and i have a script that read data from a Json and display it into a Table.is a glossary type app. I have the option to select from different languages to be shown into the table. The problem is that when i click on a row i would like to display into a popup (alert) the item information. What i did is not working at all, is just showing the Alert popup but without information.
The format of the Json is:
{"glossary":[
{"id":2,"English":{"term":"contact","definition":"the state of physical touching"},"Vietnamese":{"term":"tiếp xúc"},"Simplified_Chinese":{"term":"接触"},"Arabic":{"term":"ملامسة"},"Swahili":{"term":"mgusano"}}]}
<v-data-table dense light :headers="selectedHeaders" :item-key="id" #click:row="showAlert(item)" :items="glossary.glossary" class="elevation-1" :single-expand="true" :disable-sort=true :search="search">
<template #item.ar.term="{item}">
<div style="text-align:right;">
<span>{{item.ar.term}}</span>
</div>
</template>
</v-data-table>
<script>
import About from '#/views/About.vue'
import json from '#/assets/data/glossary.json'
export default {
name: 'App',
components: { About },
data () {
return {
publicPath: process.env.BASE_URL,
glossary: json,
search: '',
value: [],
expanded: [],
selectedHeaders: [],
dialog: false,
headers: [
{ text: 'English', value: 'English.term' },
{ text: 'Vietnamese', value: 'Vietnamese.term' },
{ text: 'Arabic', value: 'Arabic.term' },
]
}
},
methods: {
filter(value, search, item) {
return value != null &&
search != null &&
typeof value === 'string' &&
value.toString().toLocaleLowerCase().indexOf(search.toLocaleLowerCase()) !== -1
},
showAlert(a){
if (event.target.classList.contains('btn__content')) return;
alert('Extra Information:! \n'+this.English.term );
console.log(this);
}
watch: {
value(val) {
this.selectedHeaders = val ;
}
},
created() {
this.selectedHeaders = this.headers;
}
}
</script>
You are not using a which is the item passed. this just returns the vue object. What you might want is this
showAlert(a){
if (a.target.classList.contains('btn__content')) return;
alert('Extra Information:! \n'+a.English.term );
console.log(a);
}
Here is a codepen that I found that should help you understand it better https://codepen.io/nsiggel/pen/KRdGgE. Note: I did not write this codepen.

How to generate dynamic data table through api call in vuetify in vuejs

I am trying to generate dynamic data table through Vuetify ,in vuejs but dont see any example in vuetify official documentation,does anyone mind sharing an example
By "dynamic" I'm guessing you mean data loads asynchronously from an API. The Vuetify docs have numerous examples of data tables including one that simulates requesting data from an API. A very basic implementation looks something like (note: this example uses the random user generator API:
<template>
<v-data-table
:headers="headers"
:items="people"
item-key="login.uuid"
:loading="loading"
:options.sync="options"
:server-items-length="totalPeople"
#pagination="updatePage"
#update:options="customSort"
></v-data-table>
</template>
<script>
import axios from 'axios'
export default {
data: () => ({
apiURL: 'https://randomuser.me/api/',
headers: [
{ text: 'Name', value: 'name', align: 'start' },
{ text: 'Country', value: 'country' },
{ text: 'DOB (Age)', value: 'dob' },
{ text: 'Contacts', value: 'contacts' },
],
loading: false,
options: {
page: 1,
itemsPerPage: 10,
sortBy: ['name'],
sortDesc: [true],
},
people: [],
}),
mounted () {
this.getPeople()
},
methods: {
async getPeople (page = 1, results = 10, seed = 'example') {
this.loading = true
const params = { params: { page, results, seed } }
try {
this.people = (await axios.get(this.apiURL, params)).data.results
this.loading = false
} catch (error) {
console.log(error)
this.loading = false
}
},
updatePage (pagination) {
const { itemsPerPage: results, page } = pagination
this.pagination = pagination
this.getPeople({ page, results })
},
customSort (options) {
// The Random User API does NOT support sorting, but if it did, you
// would need to make an API call that returned a sorted
// list of results based on the sort parameter(s)
console.log(options)
},
},
}
</script>
For a more complex example that uses more of Vuetify's features, check out this codepen.

Nuxt: Mounting dynamic components to the DOM that is not rendered yet

Scenario:
I am using Nuxt in universal mode. The app works with a headless CMS that provides a simple layout of components that should be rendered, along with component names and their props - something like this:
[
{
"tag": "div",
"class": "col",
"component": "dogs",
"props": {
"dogs": [
{
"id": 1,
"name": "Barky"
},
{
"id": 2,
"name": "Jumpy"
}
]
}
},
{
"tag": "div",
"class": "col",
"component": "cats",
"props": {
"cats": [
{
"id": 1,
"name": "Miouwy"
},
{
"id": 2,
"name": "Fluffy"
}
]
}
}
]
As I understand, I have to apply the components to the DOM before Nuxt makes the "snapshot" and delivers it to the client. My plan was to mount the components in the created() lifecycle - which is, only looking by the names, not the way to go.
The main problem:
I want to mount components dynamically to the DOM, which does not exist yet.
As an example of the thing I want to escape from - mounting the components after Nuxt delivered the snapshot - in the mounted() lifecycle.
<template>
<section class="container">
<div ref="layout-container" class="row"></div>
</section>
</template>
<script>
import { mapGetters } from 'vuex';
import Vue from 'vue';
import Dogs from '#/components/Dogs';
import Cats from '#/components/Cats';
export default {
components: {
Dogs,
Cats
},
fetch({ store }) {
store.dispatch('landing/updateContent');
},
computed: {
...mapGetters({
content: 'landing/content',
})
},
beforeCreate() {
Vue.component('dogs', Dogs);
Vue.component('cats', Cats);
},
mounted() {
this.content.forEach((item) => {
const CompClass = Vue.component(item.component);
const instance = new CompClass({ propsData: item.props }).$mount();
this.$refs['layout-container'].appendChild(instance.$el);
})
}
};
</script>
Big thanks for any directions in advance!
EDIT: Repo with this example: https://github.com/jpedryc/nuxt-test-render
SOLUTION
My main problem was creating a rendered layout and trying to hook it up after the DOM was already delivered to the client. Instead, I should render the component within the virtual Vue DOM - right before the "snapshot" moment.
And that's what I did eventually - not mount a rendered component:
<template>
<section class="container">
<page-component/> <!-- A component without <template>, but with render() -->
</section>
</template>
The PageComponent consists only of:
import ...
export default {
components: {
Dogs,
Cats,
},
beforeCreate() {
Vue.component('dogs', Dogs);
Vue.component('cats', Cats);
},
render: function (h) {
return createDynamicLayout(h);
},
}
The createDynamicLayout(h) is just a simple function that creates a tree of:
return h('div', { 'class': 'row' }, [
h('div', { 'class': 'col' }, h(<somwhere_call_dogs_component>)),
h('div', { 'class': 'col' }, h(<somwhere_call_cats_component>)),
])
Try implementing this with dynamic components
template:
<component :is="item.component" />
script:
components: { cats: Cats, dogs: Dogs },
Then you don't need the direct DOM manipulation hack. Which can't work when Nuxt is running on the server (because you don't have a real DOM on the server) and also prevents the reactivity system from picking up changes in the pets array.

Vue Js Components does not get destroyed?

I'm currently working on an very versatile dashboard to display various data.
For the frontend I'm using the latest nuxt and vue version.
My dashboard has many kinds of variations to display data (for example pie charts, line charts,...) these are described in components which are called dynamically.
The Problem is that when I browse from the Page "/" to another (for example "/foo") the interval gets fired again and crashes the app.
That happenes after the lifecycle hook destroyed. I tried to define the interval as an variable and stop it in the beforeDestroy hook but it did not help.
let interval= setInterval(this.fetchData.bind(null, configuration, connector), configuration.refreshTime)
/* later */
clearInterval(interval);
Do you see an error?
Thank you.
Thats the relevant code:
Template
<no-ssr>
<v-container grid-list-md>
<v-layout row wrap v-masonry transition-duration="0.5s" item-selector=".flex" column-width="#grid-sizer">
<v-flex xs1 sm1 md1 lg1 x1 id="grid-sizer"></v-flex>
<component :key="dashboardItem.id" v-for="(dashboardItem,index) in dashboardItems" :is="dashboardItem.type" :connector="dashboardItem.connector"
:type="dashboardItem.type" :configuration="dashboardItem.configuration" :id="dashboardItem.id" :index="index"></component>
</v-layout>
</v-container>
</no-ssr>
Script
import OnlyValue from '#/components/dashboardItems/OnlyValue.vue'
import TrafficLight from '#/components/dashboardItems/TrafficLight.vue'
import ChartAllHover from '#/components/dashboardItems/ChartAllHover.vue'
import PieChart from '#/components/dashboardItems/PieChart.vue'
import Section from '#/components/dashboardItems/Section.vue'
import Gauge from '#/components/dashboardItems/Gauge.vue'...
export default {
name: 'HomePage',
head () {
return {
title: "Dashboard"
}
},
computed: {
...mapGetters({
isAuthenticated: "users/isAuthentificated",
profileName: "profiles/name",
dashboardItems: "profiles/dashboardItems"
})
},
mounted() {
if (typeof this.$redrawVueMasonry === 'function') {
this.$redrawVueMasonry()
}
},
components: {
OnlyValue,
TrafficLight,
ChartAllHover,
PieChart,
Section,
Gauge
}
}
When calling a components it looks the following:
import dashboardItem from '~/mixins/dashboardItem'
export default {
name: "gauge",
mixins: [dashboardItem],
props: {
connector: {
type: String,
required: true
},
type: {
type: String,
required: true
},
configuration: {
type: Object,
required: true
},
id: {
type: Number,
required: true
},
index: {
type: Number,
required: true
}
},
data: () => ({
initOptions: {
renderer: 'svg'
},
options: {
tooltip: {
formatter: "{c}%"
},
series: [{
name: null,
type: 'gauge',
detail: {
formatter: '{value}%'
},
data: null
}]
},
isLoading: true
}),
methods: {
getData(configuration, connector) {
this.fetchData(configuration, connector)
setInterval(this.fetchData.bind(null, configuration, connector), configuration.refreshTime)
},
fetchData(configuration, connector) {
this.getSingleValue(configuration, connector)
.then(data => {
this.isLoading = false
let percent = (data.value / configuration.max) * 100
percent = Math.round(percent * 10) / 10
this.$nextTick(function () {
this.$refs.gauge.mergeOptions({
series: [{
name: data.title,
data: [{
value: percent,
name: data.title
}]
}]
})
})
this.$redrawVueMasonry()
})
.catch(e => console.log(e))
}
},
mounted () {
this.getData(this.configuration, this.connector)
}
}