Vue3 datatable not updating after Axios request - vue.js

Im building a datatable and it shows my test-data correctly.
However, after i do my Axios request, it doesnt update the state.tableData variable!
What am I doing wrong here?
I keep seeing a table with the value 'test1', and not 'test2' and 'test3'?
I am using Vue3 with the composition API.
This is the template part:
<template>
<div class="card">
<div class="card-body pt-0">
<Datatable
:table-data="state.tableData"
:table-header="state.tableHeader"
>
<template v-slot:cell-name="{ row: item }">
{{ item.name }}
</template>
</Datatable>
</div>
</div>
</template>
And the JS code:
<script lang="ts">
import { defineComponent, toRefs, reactive, ref, onMounted } from "vue";
import Datatable from "#/components/kt-datatable/KTDatatable.vue";
import axios, { AxiosRequestConfig, AxiosResponse} from 'axios';
export default defineComponent({
name: "listing",
components: {
Datatable,
},
setup() {
interface fieldDefinitions {
name: string;
}
const state = reactive({
tableData: ref<fieldDefinitions[]>([]),
tableHeader: [{
name: "Naam",
key: "name",
sortable: true,
}]
});
function getListings() {
const config: AxiosRequestConfig = {
method: 'get',
url: 'https://myurl',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
};
axios(config)
.then(function (response) {
//state.tableData = response.data.data;
state.tableData = [{'name':'test2'},{'name':'test3'}];
console.log(state.tableData);
})
.catch(function (error) {
console.log(error);
});
}
onMounted(async () => {
getListings()
});
return { state,getListings };
},
});
</script>

Not sure why you have tableData: ref<fieldDefinitions[]>([]), you are assigning state.tableData = [{'name':'test2'},{'name':'test3'}]; not state.tableData = ref([{'name':'test2'},{'name':'test3'}]). But even then, state.tableData.value is not needed since reactive unwraps refs. see docs. So as far as I can tell and reproduce, your code should be working properly to trigger reactivity.
The issue is likely due to your Datatable component is not updating properly.

Related

Vue dynamically adding routes from wordpress menus api, possible reaactivity problem

I have a vue application on the frontend and a wordpress api on the backend. I am hitting the menus api and dynamically adding routes to the frontend at run time.
This works great. Until I reset the page on one of the dynamic routes. The component does not load and mounted() is never called. At this point, I can click the router link in the nav bar and the page component renders as expected.
For example. In the wordpress admin, I create a page called hello-world and add it to the primary menu. Vue will hit the api and create a route with the same name. I then load up the page and it loads fine. I click the hello world link in the nav bar, and it renders beautifully.
Now, I'm sitting at http://website.com/hello-world, and I reset the page. The app mounts and the nav bar renders. However, the page component does not render. If I click the link in the nav bar again, then it renders fine.
I am thinking this is a reactivity problem, but I can't find it. Any suggestions?
Edit. Been pondering this. The router component is loaded, and fetches the menu items asynchronously. Now, Im already sitting on one of the dynamic routes, /hello-world. The app is now loaded and there doesn't exist yet a hello-world route, since the api request is still pending. Since there is no matching route, the vue application doesn't know which component to mount... Perhaps there is a way to make the router component itself reactive?
relevant router code...
store.dispatch("getPrimaryMenu").then(() => {
store.state.menu.items.forEach((item) => {
if (item.object === "post") {
router.addRoute({
path: `/${item.slug}`,
name: item.slug,
component: () => import("#/views/Post.vue"),
});
}
if (item.object === "page") {
router.addRoute({
path: `/${item.slug}`,
name: item.slug,
component: () => import("#/views/Page.vue"),
});
}
});
});
and my store...
export default createStore({
state: {
menu: {
items: [],
},
page: {
title: {},
content: {},
},
post: {
title: {},
content: {},
},
},
mutations: {
SET_MENU(state, data) {
state.menu = data
},
SET_PAGE(state, data) {
state.page = data
},
SET_POST(state, data) {
state.post = data
},
},
actions: {
getPrimaryMenu({ commit, state }) {
console.log('get menus')
return new Promise(async (resolve, reject) => {
try {
const { data } = await axios.get(
`http://sslchkr.com/wp-json/menus/v1/menus/primary`, {
headers: {
'Content-Type': 'application/json'
}
}
)
commit('SET_MENU', data)
resolve(data)
} catch (e) {
reject(e)
}
})
},
getPage({ commit, state }, payload) {
console.log('get page')
return new Promise(async (resolve, reject) => {
try {
const { data } = await axios.get(
`http://sslchkr.com/wp-json/wp/v2/pages/${payload.id}`, {
headers: {
'Content-Type': 'application/json'
}
}
)
commit('SET_PAGE', data)
resolve(data)
} catch (e) {
reject(e)
}
})
},
getPost({ commit, state }, payload) {
console.log('get post')
return new Promise(async (resolve, reject) => {
try {
const { data } = await axios.get(
`http://sslchkr.com/wp-json/wp/v2/posts/${payload.id}`, {
headers: {
'Content-Type': 'application/json'
}
}
)
commit('SET_POST', data)
resolve(data)
} catch (e) {
reject(e)
}
})
},
},
}
a page component...
I am matching the route name to an item slug from the menu object, and using that item object_id to fetch the page object.
<template>
<div class="page">
<div>
<h1>{{ page.title.rendered }}</h1>
</div>
<div v-html="page.content.rendered"></div>
</div>
</template>
<script>
export default {
name: "Page",
computed: {
menuItem() {
return this.$store.state.menu.items.find(
(item) => item.slug === this.$route.name
);
},
page() {
return this.$store.state.page;
},
},
mounted() {
this.$store.dispatch("getPage", { id: this.menuItem.object_id });
},
};
</script>
and the nav component for completeness...
<template>
<ul id="menu-primary list-inline">
<li
v-for="item in menu.items"
:key="item.ID"
class="nav-item list-inline-item"
>
<router-link :to="slash(item.slug)" class="nav-link">{{
item.title
}}</router-link>
</li>
</ul>
</template>
<script>
export default {
name: "Nav",
computed: {
menu() {
return this.$store.state.menu;
},
},
methods: {
slash(s) {
return `/${s}`;
},
},
};
</script>
Edit to include main.js and App.vue
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap/dist/js/bootstrap.js'
import 'vue-toastification/dist/index.css'
import { createApp } from 'vue'
import Toast, { POSITION } from 'vue-toastification'
import App from './App.vue'
import router from './router'
import store from './store'
let app = createApp(App)
app.use(store)
app.use(router)
app.use(Toast, { position: POSITION.TOP_CENTER })
app.mount('#app')
<template>
<link rel="stylesheet" :href="theme" />
<Nav />
<div class="container-fluid">
<div class="row padding-top">
<div class="col-md-2"></div>
<div class="col-md-8">
<router-view :key="$route.path" />
</div>
<div class="col-md-2"></div>
</div>
</div>
</template>
<script>
import Nav from "#/components/Nav.vue";
export default {
components: {
Nav,
},
computed: {
theme() {
return this.$store.state.theme;
},
},
mounted() {
this.$store.dispatch("getTheme");
},
};
</script>

NuxtJs API request with Axios locally to Lumen API

I am a beginner. I have a Lumen API. The project runs on http://localhost:8000/. In Postman the API is working fine. Now I want to call the API from a NuxtJs project using Axios. My NuxtJs project is running on http://localhost:3000/.
<template>
<div>
CV List
<v-row v-for="(applicant, i) in applicants" :key="i">
<v-col>
<h1>name: {{ applicant.name }}</h1>
<p>{{ applicant.email }}</p>
</v-col>
</v-row>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
async fetch({ store, error }) {
try {
await store.dispatch('applicants/fetchApplicants')
} catch (e) {
error({
statusCode: 503,
message: 'Unable to fetch applicants at this time. Please try again.',
})
}
},
computed: mapState({
applicants: (state) => state.applicants.applicants,
}),
}
</script>
my applicants.js file like this:
import CVService from '#/services/CVService.js'
export const state = () => ({
applicants: [],
applicant: {},
})
export const mutations = {
SET_APPLICANTS(state, applicants) {
state.applicants = applicants
},
}
export const actions = {
fetchApplicants({ commit }) {
return CVService.getApplicants().then((response) => {
commit('SET_APPLICANTS', response.data)
})
},
}
CVService is like this,
import axios from 'axios'
const apiClient = axios.create({
baseURL: `http://localhost:8000`,
withCredentials: false,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
})
export default {
getApplicants() {
return apiClient.get('/api/authors')
},
}
The console is showing error, 503 (Service unavailable). What causing is the problem?

Change dynamically text up to backendresponse

I created vuejs project with npm. This is App.vue:
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'app',
components: {
HelloWorld
}
}
</script>
This is helloworld.vue
<template>
<div class="hello">
<label>File
<input type="file" id="file" ref="file" v-on:change="handleFileUpload()"/>
</label>
<button v-on:click="submitFile()">Submit</button>
<span id="error" style="color: red; display: none;">{{message}}</span>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'HelloWorld',
props: {
msg: String
},
methods: {
submitFile() {
let formData = new FormData();
/*
Add the form data we need to submit
*/
formData.append('file', this.file); //todo get "file" string from external
/*
Make the request to the POST /single-file URL
*/
axios.post('http://localhost:8082/upload/'
+ this.bank
+ '/'
+ this.currency,
formData,
{
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then(function (response) {
console.log('SUCCESS!! + response', response);
this.message= "successfully uploaded"
})
.catch(function (response) {
console.log('FAILURE!!+ response', response);
this.message= "successfully uploaded"
});
}
}
}
</script>
It says when it compiles:
vue.runtime.esm.js?2b0e:619 [Vue warn]: Property or method "message" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
but i need to show a dynamic messagae according to backend response:
<span id="error" style="color: red; display: none;">{{message}}</span>
This is main.js:
new Vue({
render: h => h(App),
}).$mount('#app')
Also when i add data under props in helloworld.vue, like this:
export default {
name: 'HelloWorld',
props: {
msg: String
},
data: {
},
it says:
error: `data` property in component must be a function (vue/no-shared-component-data) at src\components\HelloWorld.vue:36:9:
I did not change or add any files after npm create. Should i create a new file?
this.message = "successfully uploaded" gives Uncaught (in promise) TypeError: Cannot set property 'message' of undefined
You have props:
props: {
msg: String
},
But you are using {{message}} instead.
data should be like this (if you need message property):
data(){
return {
message: '',
};
},
Add created method like this (if you need to put your msg props to message):
created(){
this.message = this.msg;
}
Upd.
Change your code to this (arrow function instead of function) (Upd.2 As mentioned #AJT82 in his answer right before my updates was made):
<script>
import axios from 'axios';
export default {
name: 'HelloWorld',
props: {
msg: String
},
methods: {
submitFile() {
let formData = new FormData();
/*
Add the form data we need to submit
*/
formData.append('file', this.file); //todo get "file" string from external
/*
Make the request to the POST /single-file URL
*/
axios.post('http://localhost:8082/upload/'
+ this.bank
+ '/'
+ this.currency,
formData,
{
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then((response) => {
console.log('SUCCESS!! + response', response);
this.message= "successfully uploaded"
})
.catch((response) => {
console.log('FAILURE!!+ response', response);
this.message= "successfully uploaded"
});
}
}
}
</script>

Vue JS : Can not update dom from created function

I created my project by
vue init webpack project
#vue/cli 4.0.5
Here is my App.vue.
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
Router file
let router = new Router({
routes: [
{
path: '/videos',
name: 'Videos',
component: Videos
}
]
})
Files under Videos folder
index.js
import Videos from './Videos'
export default Videos
Videos.vue
<template>
<div>
<ul>
<li v-for="video in videos" :key="video.index">
{{ video.index }} - {{ video.value }}
</li>
</ul>
<div class="button">
<cv-button #click="submit">Submit</cv-button>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
created: () => {
const _this = this
const url = process.env.API_URL
axios.get(url + 'api/hello', {mode: 'no-cors'})
.then(response => {
const resource = response.data
const videos = resource.videos
_this.videos = Object.keys(videos).map((key) => {
return {
index: key,
value: videos[key]
}
})
})
},
data: () => {
return {
videos: []
}
},
methods: {
submit: function () {
const url = process.env.API_URL
axios.get(url + 'api/videos')
.then(response => {
console.log(response)
const resource = response.data
const videos = resource.videos
this.videos = Object.keys(videos).map((key) => {
return {
index: key,
value: videos[key]
}
})
})
}
}
}
</script>
Basically, I want to get a list of videos inside created function but neither this.videos nor _this.videos worked. When I tried to log this inside the created function, I was seeing a {} JSON object, not VueComponent.
{
a: {computed: {}, data: f, ...},
videos: [{...},{...}]
}
When I tried to get the list by click on the button, which triggers the submit function, it worked as expected, and this was a VueComponent.
VueComponent {_uid: 23, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
I don't understand what happened here? Why I worked with the submit function but not inside the created function?
Thanks
With created: () => {} notation created function executes in global scope. Try created() {}

Cannot read property 'list' of null - VueJS

Im creating a Notification component using Vuex to store data API and called it in components but I have some issues
Component.vue
<template>
<div class="notification_check">
<div class="header">
<span>Notifications </span>
<span>(1)</span>
</div>
</div>
<ul v-for="notification in $store.state.notification.notifications.list" :key="notification.id">
{{notification.id}}
</ul>
</template>
<script>
export default {
data: function () {
return {
config: {
'headers': {
'Authorization': 'JWT ' + this.$store.state.token
}
},
notifications: this.$store.state.notification.notifications.list
}
},
created: function () {
this.getNotification()
},
methods: {
getNotification: function () {
this.$store.dispatch('getNotification', this.config)
}
}
}
</script>
This is Action Commit:
getNotification ({commit}, config) {
api.get(`/notifications/`, config)
.then(response => {
commit('GET_NOTIFICATION', response.data)
})
},
This is my data API which stored in Vuex Store. Please take a look:
Error occurs:
Cannot read property 'list' of null