Vuejs and webpack loader - can't load data on a datatable - javascript

I'm using Webpack-Loader to load VueJS in my Django project. I'm very new to Vue, and i'm trying to create a simble component that fetches data from the backend using an API call and shows this data on Vuetify datatable.
The issue with my code is that while the request is executed, nothing is shown on the datatable, so the table shows but nothing is inside of it.
I'm having an hard time understanding if there is a problem with the code or if the problem is with my config or my imports.
Here is the component where i'm performing the request and show the data on the datatable:
App.vue
<template>
<v-simple-table dark>
<template v-slot:default>
<thead>
<tr>
<th class="text-left">
Asset
</th>
<th class="text-left">
Total
</th>
</tr>
</thead>
<tbody>
<tr
v-for="item in balances"
:key="item.asset">
<td>{{ item.asset }}</td>
<td>{{ item.total }}</td>
</tr>
</tbody>
</template>
</v-simple-table>
</template>
<script>
export default {
data() {
return {
balances: [],
}
},
mounted() {
this.fetchData()
},
methods: {
fetchData() {
fetch('http://127.0.0.1:8000/binance/getbalance')
.then(response => response.json())
.then(data => {
this.balances = data
console.log(data)
})
}
}
}
</script>
main.js
import Vue from "vue/dist/vue.js";
import Vuex from "vuex";
import storePlugin from "./vuex/vuex_store_as_plugin";
import App from './App.vue'
import Vuetify from "vuetify";
import "vuetify/dist/vuetify.min.css";
Vue.use(Vuetify);
Vue.use(Vuex);
Vue.use(storePlugin);
Vue.config.productionTip = false;
new Vue({
el: '#app',
render: h => h(App),
})
Here is my vue.config.js
const BundleTracker = require("webpack-bundle-tracker");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
const pages = {
'main': {
entry: './src/main.js',
chunks: ['chunk-vendors']
},
}
module.exports = {
pages: pages,
filenameHashing: false,
productionSourceMap: false,
publicPath: process.env.NODE_ENV === 'production'
? 'static/vue'
: 'http://localhost:8080/',
outputDir: '../django_vue_mpa/static/vue/',
chainWebpack: config => {
config.optimization
.splitChunks({
cacheGroups: {
moment: {
test: /[\\/]node_modules[\\/]moment/,
name: "chunk-moment",
chunks: "all",
priority: 5
},
vendor: {
test: /[\\/]node_modules[\\/]/,
name: "chunk-vendors",
chunks: "all",
priority: 1
},
},
});
Object.keys(pages).forEach(page => {
config.plugins.delete(`html-${page}`);
config.plugins.delete(`preload-${page}`);
config.plugins.delete(`prefetch-${page}`);
})
config
.plugin('BundleTracker')
.use(BundleTracker, [{filename: '../vue_frontend/webpack-stats.json'}]);
// Uncomment below to analyze bundle sizes
// config.plugin("BundleAnalyzerPlugin").use(BundleAnalyzerPlugin);
config.resolve.alias
.set('__STATIC__', 'static')
config.devServer
.public('http://localhost:8080')
.host('localhost')
.port(8080)
.hotOnly(true)
.watchOptions({poll: 1000})
.https(false)
.headers({"Access-Control-Allow-Origin": ["*"]})
}
};
Versions used:
Vue 2, vuetify#2.3.21
The data is a very simple JSON array of less than 20-30 elements, it looks like this:
[{"asset": "XYZ", "total": 123}, {"asset": "XYZ2", "total": 456"} ... ]
The request is being executed, since console.log() will print the data, the table won't show any data, it's just empty.
Here are the only errors i found in my console:
vuetify.js?ce5b:42906 [Vuetify] Multiple instances of Vue detected
[Vue warn]: $attrs is readonly.
found in
---> <VSimpleTable>
<App> at src/App.vue
<Root>
[Vue warn]: $listeners is readonly.
found in
---> <VSimpleTable>
<App> at src/App.vue
<Root>
EDIT
This isn't going to append data to the table, which means that the problem is most likely not my data:
.then(data => {
this.balances = [{"asset": 1, "total": "1"}, {"asset": 2, "total": 2}, {"asset": 3, "total": 3}]
})

Vuetify didn't have simple-table in 1.xx versions. Instead they used v-data-table instead it seems.
I've made a codesandbox to test this:
https://codesandbox.io/s/vuetify-data-table-forked-4pxqb?file=/src/components/AppTable.vue
Vuetify 2.3.xx
Vuetify 1.4.xx

It looks like i was importing in the wrong way. Changing my imports to:
import Vue from "vue";
import App from "./App.vue";
import Vuetify from "vuetify";
import "vuetify/dist/vuetify.min.css";
fixed the problem entirely. I still don't understand what changes, so any kind of explanation is very welcome!

Related

Vuejs implementing Vue-Select2 not showing on result

After installing vue3-select2-component with their document when i implementing that. it doesn't show in output on html but i have the html of that in source code
BTW: i'm using inertiajs on Laravel framework
install:
// npm install
npm install vue3-select2-component --save
Use as component:
import {createApp, h} from 'vue'
import BootstrapVue3 from 'bootstrap-vue-3'
import IconsPlugin from 'bootstrap-vue-3'
import {InertiaProgress} from "#inertiajs/progress";
import {createInertiaApp, Head} from '#inertiajs/inertia-vue3'
import {Link} from "#inertiajs/inertia-vue3"
///...
import Select2 from 'vue3-select2-component';
import {createStore} from "vuex"
///...
createInertiaApp({
resolve: async name => {
return (await import(`./pages/${name}`)).default;
},
setup({el, App, props, plugin}) {
createApp({render: () => h(App, props)})
.use(plugin)
.use(bootstrap)
.use(BootstrapVue3)
.use(IconsPlugin)
.use(VueSweetalert2)
.component('Link', Link)
.component('Select2', Select2)
.mount(el)
},
title: title => 'azizam - ' + title
}).then(r => {
});
vuejs page which i want to use into that:
<template>
<Select2 v-model="myValue" :options="myOptions"
:settings="{ settingOption: value, settingOption: value }"
#change="myChangeEvent($event)"
#select="mySelectEvent($event)" />
</template>
<script>
import menubar from "./menubar";
import emulator from "./emulator";
import {mapActions} from "vuex";
import notification from "../../../partials/notification";
export default {
name: "image",
data() {
return {
caption: '',
myValue: '',
myOptions: ['op1', 'op2', 'op3']
}
},
components: {
menubar,
emulator,
notification
},
methods: {
...mapActions([
'changeBreadcrumb'
]),
myChangeEvent(val){
console.log(val);
},
mySelectEvent({id, text}){
console.log({id, text})
}
},
mounted() {
const payload = {
title: 'محصولات',
subTitle: 'ایجاد محصول تک عکس در سامانه'
};
this.changeBreadcrumb(payload);
}
}
</script>
console log:
Warning - slinky.min.js is not loaded. application.js:336:21
[Vue warn]: A plugin must either be a function or an object with an "install" function. vendor.js:10544:17
[Vue warn]: Plugin has already been applied to target app. vendor.js:10544:17
Use of Mutation Events is deprecated. Use MutationObserver instead. content.js:19:11
Source map error: Error: request failed with status 404
Resource URL: http://127.0.0.1:8000/js/vendor.js?id=594b688c9609a79fb52afd907a69c736
Source Map URL: tooltip.js.map
in console as you can see i don't get any error for this component
html source code:
<select2 options="op1,op2,op3" settings="[object Object]"></select2>
and then webpack:
const mix = require('laravel-mix');
mix.js('resources/js/app.js', 'public/js')
//.sass('resources/scss/app.scss','public/css')
.extract()
.vue({
version: 3,
options: {
compilerOptions: {
isCustomElement: (tag) => ['Select2'].includes(tag),
},
},
})
.postCss('resources/css/app.css', 'public/css', [
//
])
.version();
The problem is you've configured Vue to treat <Select2> as a custom element, so the actual component does not get rendered.
The fix is to remove that configuration:
const mix = require('laravel-mix');
mix.js('resources/js/app.js', 'public/js')
//.sass('resources/scss/app.scss','public/css')
.extract()
.vue({
version: 3,
//options: {
// compilerOptions: {
// isCustomElement: (tag) => ['Select2'].includes(tag), ❌ remove this
// },
//},
})
.postCss('resources/css/app.css', 'public/css', [
//
])
.version();

Vuex store value not set in mounted

I have had problems where I am getting a null value from my store... sometimes, and only on some values. If anyone could point me in the right direction and explain why it is wrong... I would be really grateful. So here is the deal my store.getters.getApiKey is sometimes "" and sometimes not.
So... in the component vue below, it is not null on the first reference
{{this.$store.getters.getApiKey}}
and then within the mounted section, store.getters.getHostUrl is set, but store.getters.getApiKey keeps returning "".
Here are the details:
the Component.vue
<template>
<div class="countryCodes">
<p>ApiKey : {{this.$store.getters.getApiKey}}</p>
<p>CountryCode Data is {{ page }}</p>
<div class="CountryCodes">
<tr v-for="ccdata in content_list" v-bind:key="ccdata.guid">
<td>{{ ccdata.guid }}</td>
<td>{{ ccdata.name }}</td>
<td>{{ ccdata.code }}</td>
</tr>
</div>
</div>
</template>
import axios from "axios";
import store from "#/store";
export default {
name: 'CountryCodes',
data () {
return {
page: "",
content_list: []
}
},
mounted () {
axios({ method: "GET", "url": store.getters.getHostUrl + "
"/api/"+store.getters.getApiKey+"/countryCodes" }).then(result => {
this.page = result.data.page;
this.content_list = result.data.contents;
}, error => {
console.error(error);
});
},
methods: {
}
}
</script>
Then my store (store.js) looks like this...
import Vuex from "vuex";
import Vue from 'vue';
Vue.use(Vuex)
export default new Vuex.Store({
state: {
apiKey: "",
hostUrl:""
},
mutations: {
SET_APIKEY(state, value) { state.apiKey = value; },
SET_HOST_URL(state, value) {state.hostUrl = value; }
},
getters: {
getApiKey(state) { return state.apiKey; },
getHostUrl(state) { return state.hostUrl; }
}
})
finally in my main.js I commit the data to the store... :
import Vue from 'vue'
import App from './App.vue'
import router from './router/index.js'
import store from './store.js'
new Vue({
el: '#app',
render: h => h(App),
router,
store,
mounted: function() {
console.log(this.$route.query)
store.commit('SET_APIKEY', this.$route.query.api)
store.commit("SET_HOST_URL", location.origin.toString())
}
})
I have the same problem when trying to build a http service, where the store is null for the apikey. What magic am I missing?
Usually the mounted hook of child component is called before the mounted of parent component.
From Vue Parent and Child lifecycle hooks
If you try to console.log on both mounted hook you will see the order of execution (but I'm still not sure why your store.getters.getHostUrl is set).
So you need a watcher to run your code after your store has value.
Example code:
...
computed: { // or use another getters
url () {
if (!store.getters.getHostUrl || !store.getters.getApiKey) return
return `${store.getters.getHostUrl}/api/${store.getters.getApiKey}/countryCodes`
}
},
watch: {
url (value) {
...
}
}
...
CodeSandbox
So.... there were two ways of solving this..... thank you to both comments.
Switch my mounted in the main.js to created- the diagram above explains why... as well as the nice article.
add "await store.getters.getApiKey"

how to prevent or stop parent component to re-render in vuejs

Hi Guys i'm trying to create a look something like Bootstrap Nav Tabs but with Vuejs and Vue Router i also want to change the url in browser
here is my code for VueRouter
it is working fine but the Parent component(UserProfile) get re-render every time i switch between UserProfilePosts or UserDetails because i know my code going to be larger and this is not a good user experience,Thanks
{
path:'/:id',
component:UserProfile,
children: [
{ path: '', component: UserProfilePosts },
{ path: 'details', component: UserDetails },
],
meta:{
requiresAuth:true
}
}
Main Component(UserProfile):
<template>
<div class="container-fluid">
<h1>UserProfile</h1>
<router-link to="/username">Post's</router-link>
<router-link to="/username/details">Details</router-link>
<router-view></router-view>
</div>
<script>
export default{
created(){
console.log('created');
}
}</script>
You can try using Vuex with vex-persist. Vue refresh and reload the html each time it is asked. I am new to vue and this was how I implemented it, though it may not be the best solution.
VueX is the one central source of truth that your components can look for information. It will be easier passing down as prop and all the components just head to the 'store' for information
This stores the information as a local / session storage. For more information check out : https://github.com/championswimmer/vuex-persistuex-persist
import Vue from 'vue'
import Vuex from 'vuex'
import VuexPersistence from 'vuex-persist'
const vuexLocal = new VuexPersistence({
storage: window.sessionStorage
})
Vue.use(Vuex)
export default new Vuex.Store({
plugins: [vuexLocal.plugin],
state: {
database: []
},
mutations: {
pushToDatabase: (state, val) => {
state.database.push (val}
}
},
getters: {
getData: state => {
return state.database
}
}
})

How to mock a Vuex store in VueJS test-utils parentComponent

I'm using Jest with vue-test-utils trying to test if a child component reacts to an $emit event in the parent component.
VueJS test-utils library provides a parentComponent option to be passed when mounting/shallow mounting the component.
Everything is working fine except that even though I instantiate the component with a mocked Vuex store, the parent component throws a
TypeError: Cannot read property 'state' of undefined
on a this.$store.state.something.here piece of code in the parent component.
How can I mock the Vuex store there?
The component mount looks like this:
const wrapper = shallowMount(ChildComponent, {
store,
localVue,
parentComponent: ParentComponent,
mocks: {
$t: msg => msg,
},
});
Any ideas on how to solve this?
it may not be the complete answer to OP questions, but since I had been debugging around for the last 2h and finally found MY problem, I would like to posted here to help someone in the future.
I was trying to mock and mount the following component:
<template>
<div test="object-list-div">
<h1 test="component-title">{{ objectName }}</h1>
<table class="table">
<thead>
<tr test="table-row-title">
<th scope="col" test="table-column-title" v-for="(value, name, index) in objectData[0]" :key="index">{{ name }}</th>
</tr>
</thead>
<tbody>
<tr test="table-row-data" v-for="(ivalue, iname, i) in objectData" :key="i">
<td test="table-cell-data" v-for="(jvalue, jname, j) in ivalue" :key="j">{{ jvalue }}</td>
</tr>
</tbody>
</table>
</div>
export default {
props: [
'objectName',
'objectData'
],
computed: {
visibleColums() {
return this.$store.state.Config_ShowColumn;
}
}
}
with the following wrapper code
wrapper = shallowMount(ObjectList, {
mocks: {
$store: {
state: {
Config_ShowColumn: [
"Field1",
"Field2",
"Field3",
"Field4",
"Field5",
]
}
}
}
});
I got OP error, but in my case the component was expecting two Props at the moment of creation. Since it did not receive this, it got stuck.
This is working now:
import { shallowMount } from "#vue/test-utils";
import { expect } from "chai";
import ObjectList from "#/components/Object-List.vue";
wrapper = shallowMount(ObjectList, {
propsData: {
objectName: "Ticket",
objectData: [
{
Field1: "Field1",
Field2: "Field2",
Field3: "Field3",
Field4: "Field4",
Field5: "Field5",
},
]
},
mocks: {
$store: {
state: {
Config_ShowColumn: [
"Field1",
"Field2",
"Field3",
"Field4",
"Field5",
]
}
}
}
});
Hope it helps someone.
Tried the solution Richard proposed but without much success, even though his guess was right.
The solution was far simnpler than I envisioned, I just stopped instantiating the Vuex.Store and just have the mocked $store in vue-test-utils config like so:
import { createLocalVue, shallowMount, config } from '#vue/test-utils';
config.mocks.$store = {
state: {
user: {
sexy: true
},
},
};
I had no need to use an actual instance of Vuex as I only needed to mock the actual data so this worked perfectly.
How are you creating the mock store? It should be something like
const storeOptions = {
state: {...},
getters: {...},
mutations: {...}
}
const mockStore = new Vuex.Store(storeOptions)
Since this.$store is undefined, I suspect you might just be passing the options object to shallowMount.

How to keep the information of a component when going from one component to another in vue js

If I have two components, the first one is called A:
<template>
<input required type='text' v-model.trim="number">
<input type="date" v-model="date" >
<button #click='allRecords(number,date)'>ok</button>
<table >
<thead>
<th>Coordonnées</th>
</thead>
<tbody>
<tr v-for='contact in contacts'>
<td #click="seeDetails(contact.id)" > {{ contact.data.to }}
</td>
</tr>
</tbody>
</table>
</template>
<script lang="js">
import axios from 'axios';
import Vue from 'vue';
export default {
name: 'A',
props: [],
data() {
return {
contacts: [],
number: '',
date: new Date().toISOString().slice(0,10),
nombre:0
}
},
methods: {
allRecords: function(number,date) {
axios.get(`/api/contacts?number=${number}&date=${date}`)
.then(response => {
this.contacts = response.data.list;
this.nombre = response.data.count;
})
.catch(error => {
console.log(error);
});
},
seeDetails (id) {
this.$router.push({ name: 'B', params: { id }});
},
}
</script>
the 2nd is called B:
<template>
<div> {{details.data.add }}</div>
</template>
<script lang="js">
import axios from 'axios';
export default {
name: 'B',
props: [],
mounted() {
const id = this.$router.currentRoute.params.id;
this.fetchContactData(id);
},
data() {
return {
details: []
}
},
methods: {
fetchContactData(id){
axios.get(`/api/recherche/${id}`)
.then(response => {
this.details = response.data
})
.catch(error => {
console.log(error);
});
},
},
}
</script>
I would like when I leave my component B has my component A to have the information of A which corespondent to the result that I had in B without needing to enter again the date and the number.
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
You have waded into the problem of application state, and views can differ. The recommended solution is vuex. For simple situations, I like to keep app state in a global javascript variable. Your vue components don't need to pass state, but they refer to a single source of truth outside of vue, which they can all display and modify. So you're app state is a contacts array, and your B component, which needs a better name, will just push rows onto this array. When you return to A, your page will reflect the new data.
I see you want to show the details of a specific contact based on its ID.
But is your router configured correctly?
Dynamic Route Matching:
routes: [
{ path: '/contacts/:contact', component: B }
]
See more at https://router.vuejs.org/guide/essentials/dynamic-matching.html#reacting-to-params-changes

Categories