I have a nuxt 3 app that fetches data from the API. I would like to use that data to populate the input fields in the template but I keep getting an error.
Here is a snippet of my code
<script setup>
const config = useRuntimeConfig();
const route = useRoute();
const router = useRouter();
const { data: pkg } = useFetch(
() => '/api/id/1/'
);
const request = ref({
field: pkg.value.field_value,
});
When I console.log(pkg.value.field_value) I get the value printed on the browser developer tools console tab but on hard refresh, I get the error Cannot read properties of null (reading 'field_value')
The reason why I need to dynamically set the value of field is so that I am able to update it.
Anyone encountered that problem before and how did you address it
Add await to the useFetch function because at the first rendering the pkg is not available :
<script setup>
const config = useRuntimeConfig();
const route = useRoute();
const router = useRouter();
const { data: pkg } = await useFetch(
() => '/api/id/1/'
);
const request = ref({
field: pkg.value.field_value,
});
Related
How do you accomplish nested fetching in Nuxt 3?
I have two API's. The second API has to be triggered based on a value returned in the first API.
I tried the code snippet below, but it does not work, since page.Id is null at the time it is called. And I know that the first API return valid data. So I guess the second API is triggered before the result is back from the first API.
<script setup>
const route = useRoute()
const { data: page } = await useFetch(`/api/page/${route.params.slug}`)
const { data: paragraphs } = await useFetch(`/api/page/${page.Id}/paragraphs`)
</script>
Obviously this is a simple attempt, since there is no check if the first API actually return any data. And it is not even waiting for a response.
In Nuxt2 I would have placed the second API call inside .then() but with this new Composition API setup i'm a bit clueless.
You could watch the page then run the API call when the page is available, you should paragraphs as a ref then assign the destructed data to it :
<script setup>
const paragraphs = ref()
const route = useRoute()
const { data: page } = await useFetch(`/api/page/${route.params.slug}`)
watch(page, (newPage)=>{
if (newPage.Id) {
useFetch(`/api/page/${newPage.Id}/paragraphs`).then((response)=>{
paragraphs.value = response.data
})
}
}, {
deep: true,
immediate:true
})
</script>
One solution is to avoid using await. Also, use references to hold the values. This will allow your UI and other logic to be reactive.
<script setup>
const route = useRoute()
const page = ref()
const paragraphs = ref()
useFetch(`/api/page/${route.params.slug}`).then(it=> {
page.value = it
useFetch(`/api/page/${page.value.Id}/paragraphs`).then(it2=> {
paragraphs.value = it2
}
}
</script>
You can set your 2nd useFetch to not immediately execute until the first one has value:
<script setup>
const route = useRoute()
const { data: page } = await useFetch(`/api/page/${route.params.slug}`)
const { data: paragraphs } = await useFetch(`/api/page/${page.value?.Id}/paragraphs`, {
// prevent the request from firing immediately
immediate: false,
// watch reactive sources to auto-refresh
watch: [page]
})
</script>
You can also omit the watch option there and manually execute the 2nd useFetch.
But for it to get the updates, pass a function that returns a url instead:
const { data: page } = await useFetch(`/api/page/${route.params.slug}`)
const { data: paragraphs, execute } = await useFetch(() => `/api/page/${page.value?.Id}/paragraphs`, {
immediate: false,
})
watch(page, (val) => {
if (val.Id === 69) {
execute()
}
})
You should never call composables inside hooks.
More useFetch options can be seen here.
I've been struggling for 5 hours with the following issue.
I have a service file where I have API calls using Axios. In the store, I have an action that uses the service to pull a list of schools, then I commit the data to the mutations. If I console log the data on the mutation object, it works correctly and shows the data. However, when I call dispatch from the component inside the onMounted hook, I get an empty object. Any help is greatly appreciated. (see the code below)
store/schools.js
export const state = () => ({
mySchools: []
});
export const mutations = {
getSchools(state, data) {
state.schools = data;
console.log(state.schools); // works;
}
};
export const actions = {
async getMySchools({ commit }) {
await this.$getSchools().then(response => {
commit("getSchools", response.data);
});
}
};
portal/dashboard.vue
import {onMounted, ref, useStore} from "#nuxtjs/composition-api";
export default {
layout: 'portal',
setup() {
const store = useStore();
const schools = ref([]);
onMounted(async() => {
await store.dispatch('schools/getMySchools'); // is not pulling data
schools.value = store.state.schools.mySchools;
console.log(schools); // empty
});
return {
schools
}
}
};
Thank you
You shouldn't use await with then
try this
async getMySchools({ commit }) {
const response = await this.$getSchools();
commit("getSchools", response.data);
}
I'm assuming that your this.$getSchools() actually works since I'm not sure what that is and it's not part of the code
Im struggling using Axios and useRoute using Axios with Composition API. Here is the code on how to do it in using the Option API, how do I recreate it, the Vue-router docs not well documented at all right now.
async created() {
const result = await axios.get(`https://localhost:5001/api/artists/${this.$route.params.id}`
);
const artist = result.data;
this.artist = artist;
},
To convert that code to Composition API:
created() hook is effectively the same timing as setup(), so put that code in setup().
this.$route can be accessed via useRoute() (from vue-router#4).
artist can be declared as a data ref, returning it from setup() if used in the template.
To reactively fetch data from the API based on the id parameter, use watch on route.params.id. This watch call returns a function that stops the watcher, which is useful if you need to conditionally unwatch the reference.
import { useRoute } from 'vue-router'
import { ref, watch } from 'vue'
export default {
// 1
setup() {
const route = useRoute() // 2
const artist = ref(null) // 3
// 4
const unwatch = watch(() => route.params.id, (newId, oldId) => {
const result = await axios.get(`https://localhost:5001/api/artists/${newId}`)
artist.value = result.data
// run only once
unwatch()
})
return {
artist
}
}
}
I'm having a Vuex getters issue where the gitters return as undefined (in the Vue Dev Console and no errors are logged in the Chrome Dev Console).
If mapGetters() is commented out (like the example code below), the returned data is displayed on screen -> Providing if the user clicks into the link that has the data. The data will NOT display if the user enters the app directly at the point where the data should display.
There is a similar question but there is no accepted answer
Vue Console Logs:
STATE:
$_lgdHRS:Object
totHrs:129
GETTERS:
$_lgdHRS/totHrs:undefined
SomeContainer.vue
<script>
import store from '../../_store'
import { mapState, mapGetters } from 'vuex'
export default {
computed: {
...mapState('$_lgdHRS',{
totHrs : 'totHrs',
}),
// ...mapGetters('$_lgdHRS',{
// totHrs : 'totHrs',
// airHrs : 'airHrs',
// picHrs : 'picHrs',
// pmcHrs : 'pmcHrs',
// voHrs : 'voHrs',
// trngHrs : 'trngHrs'
// }),
},
created() {
this.storeKey = '$_lgdHRS';
if (!(this.storeKey in this.$store._modules.root._children)) {
this.$store.registerModule(this.storeKey, store);
}
},
mounted() {
this.$store.dispatch('$_lgdHRS/getLogSummary');
},
}
</script>
<template>
<total-summary :hours="totHrs" />
</template>
state.js
export const state = {
totHrs: Number,
}
getters.js
const totHrs = state => state.totHrs;
export default {
totHrs,
};
mutations.js
const
TOTAL_HRS_UPDATED = (state, totHrs) => {
state.totHrs = +totHrs;
};
export default {
TOTAL_HRS_UPDATED,
};
Most probably because you have just displatched the request in mounted and before the data is set into the state variable your component is displayed.
Hence you can trying using async await in mounted as well as in store actions.
Do refer the following link and check the last example in this.
https://vuex.vuejs.org/guide/actions.html
The problem was that I was nesting my variables as I usually would in other frameworks.
Example:
// NESTED VARS
const r = response
totHrs = r.total,
airHrs = r.airborne,
picHrs = r.PIC,
pmcHrs = r.PMC,
voHrs = r.VO,
trngHrs = r.training;
// CHANGE TO:
const r = response
const totHrs = r.total
const airHrs = r.airborne
const picHrs = r.PIC
const pmcHrs = r.PMC
const voHrs = r.VO
const trngHrs = r.training
I don't know enough to why but your input would be greatly appreciated in the comments.
Can someone help me understand how to pass data from post request to the nuxt page that is loaded. I dont know how to send the data to the page that will be loaded.
I want to be able to process the POST request, then send that data for usage on the following page. I am open to suggestions but I can't find proper documentation, tutorials or examples to accomplish this task.
I don't want to use axios here (with JSON type response), because I would prefer to send POST data and load new page. Therefor if page is reloaded, POST data must be submitted again.
const express = require('express')
const bodyParser = require('body-parser')
const { Nuxt, Builder } = require('nuxt')
const app = express()
const host = process.env.HOST || '127.0.0.1'
const port = process.env.PORT || 3000
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())
app.set('port', port)
// Import and Set Nuxt.js options
let config = require('../nuxt.config.js')
config.dev = !(process.env.NODE_ENV === 'production')
async function start() {
// Init Nuxt.js
const nuxt = new Nuxt(config)
// Build only in dev mode
if (config.dev) {
const builder = new Builder(nuxt)
await builder.build()
}
// Routes added
app.post('/events/booking', function (req, res, next) {
console.log('REQUEST:', req.body)
res.set('eventId', req.body.eventId)
res.set('moreData', ['some', 'more', 'data'])
next()
})
// Give nuxt middleware to express
app.use(nuxt.render)
// Listen the server
app.listen(port, host)
console.log('Server listening on http://' + host + ':' + port) // eslint-disable-line no-console
}
start()
I believe the source of your issue is the disconnect between Nuxt's implementation of Express, the deprecation/version-conflicts of bodyParser middleware and/or the Node event system.
I would personally take a step back by removing the custom express routing, handle the body parsing yourself in the middleware and take advantage of the Vuex store.
store/index.js
export const state = () => ({
postBody: null,
postError: null
})
export const mutations = {
postBody: (state, postBody) => {
state.postBody = postBody;
},
postError: (state, postError) => {
state.postError = postError;
},
}
export const getters = {
postBody: state => state.postBody,
postError: state => state.postError,
}
middleware/index.js
export default ({req, store}) => {
if (process.server && req && req.method === 'POST') {
return new Promise((resolve, reject) => {
req.on('data', data => resolve(store.commit('postBody', JSON.parse(data))));
req.on('error', data => reject(store.commit('postError', JSON.parse(data))));
})
}
}
pages/index.vue
<template>
<div>
<h1>Test page</h1>
<div v-if="postBody">
<h2>post body</h2>
<p>{{postBody}}</p>
</div>
<div v-if="postError">
<h2>post error</h2>
<p>{{postError}}</p>
</div>
<div v-if="!postError && !postBody">
Please post JSON data to this URL to see a response
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
middleware: 'post-data',
computed: mapGetters({
postBody: 'postBody',
postError: 'postError'
})
}
</script>
Below is a live and working example project of the above. POST JSON data using a client app (Postman, web form, etc) to see the posted data rendered on the page.
Live Code: https://glitch.com/edit/#!/terrific-velociraptor
Live Example: https://terrific-velociraptor.glitch.me/