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
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>
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?
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>
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() {}
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