Vue 3 - Can't get the params value on route change - vue.js

I am calling this funciton
chooseProject(e) {
let token = this.extractFirstText( e.title );
let userId = this.projects.user_id;
this.$router.push('/dashboard/'+token );
localStorage.setItem( 'lastProjectToken_'+userId, this.projects.name + ' ('+token+')');
},
When I change the HTML select options:
<v-select
v-model="selectedItem"
label="title"
:options="option" #input="chooseProject"
/>
As you can see that it's going to this URL: dashboard/---a token ---- page
This route is already defined:
{
path: '/dashboard/:token',
name: 'select-project',
component: () =>
import ('#/views/apps/projects/project-select/SelectProject.vue'),
},
So, when I first change the select option then on SelectProject.vue page I can get the token value but after that If I change the select option I can't get the token value.
SelectProject.vue page:
created() {
let _this = this;
_this.projectToken = _this.$route.params.token;
// data
console.log( 'token is = ' + _this.projectToken );
_this.$http.get("/ecommerce/data").then((response) => {
_this.data = response.data;
// ? Your API will return name of logged in user or you might just directly get name of logged in user
// ? This is just for demo purpose
const userData = getUserData();
_this.data.congratulations.name = userData.username;
});
},
Is there anything wrong in my code?
Note: I am new in Vue JS :)

get params in vue 3 with composition API
Step 1
import { useRoute} from "vue-router";
Step 2
const route = useRoute();
step 3
route.params.token

Related

Populate input data with result from API nuxt 3

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,
});

Nested useFetch in Nuxt 3

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.

vuex 3.6 why is my state object undefined when using it inside a method

I am building an application using vue 2.6.11 and vuex 3.6.0
The page I am building is for an event registration. The ActiveEvent is fetched from the database (Event ID, Date, Location etc) using an API
The registration form first asks for an email address. On blur of the email field we then fire the checkEmail(). This should do one or two API calls. The first call checks to see if we have the email address in the database and returns the ParticipantID, and if we do then a second call is made to see if the participant is already registered against this event using Event.EventID and ActiveUser.ParticipantID
The stucture of the page being loaded is a page component <EventDetail> called from the router. This has a main component <EventRegistration> which calls two separate sub-components: <EventRegistrationBlurb> which gets the state.ActiveEvent passed as a prop and <EventRegistrationForm> which is fetching the state.ActiveEvent directly. The outer component <EventRegistration> is responsible for fetching the Event data from the API and setting state.ActiveEvent which is does successfully,
What I am failing to understand is why when I call checkEmail in my component, this.ActiveEvent is undefined. The puter component is fetching the API and setting the state correctly as the blurb component is correctly rendering it. If I put the ActiveEvent object into the template for the EventRegistrationForm it renders correctly, it is just not being set in time for the binding to be made to the method checkEmail()
I have the following code in my sub-component <EventRegistrationForm>: (NOTE, ActiveEvent is set by an outer component and does get set correctly)
methods: {
...mapActions(['CheckParticipantByEmail']),
async checkEmail () {
const payload = {
email: this.form.email,
EventID: this.ActiveEvent.EventID // <-- THIS IS UNDEFINED???
}
await this.CheckParticipantByEmail(payload)
}
},
computed: {
...mapState(['ActiveEvent', 'ActiveUser'])
}
and then in my store:
state: {
ActiveEvent: {},
ActiveUser: {}
},
mutations: {
SET_ACTIVE_EVENT (state, payload) {
state.ActiveEvent = payload
},
CHECK_PARTICIPANT_BY_EMAIL (state, payload) {
state.ActiveUser = payload
},
GET_PARTICIPANT_FOR_EVENT (state, payload) {
state.ActiveUser = payload
}
},
actions: {
async CheckParticipantByEmail ({ commit }, payload) {
console.log('payload', payload)
const baseUrl = process.env.VUE_APP_API_URL
const url = `${baseUrl}getParticipantbyEmail`
const { email, EventID } = payload
const response = await axios.post(
url,
{
EmailAddress: email
}
)
const User = await response.data[0]
commit('CHECK_PARTICIPANT_BY_EMAIL', User)
if (User.ParticipantID > 0) {
const baseUrl = process.env.VUE_APP_API_URL
const url2 = `${baseUrl}getParticipantForEvent`
const payload2 = {
ParticipantID: User.ParticipantID,
EventID: EventID
}
alert('URL2: ' + url2)
alert('payload2 participant: ' + payload2.ParticipantID)
alert('payload2 event: ' + payload2.EventID)
const response2 = await axios.post(
url2,
payload2
)
// console.log('response: ', response.data[0])
const payload3 = response2.data[0]
commit('GET_PARTICIPANT_FOR_EVENT', payload3)
}
}
}
As usual, it turns out to be an interface error between the chair and the keyboard. This page is normally accessed from a list of events which is an array of objects where the identifier is EventID. When calling the separate events the identifier is just ID so the code in the payload2 should read
const payload2 = {
ParticipantID: User.ParticipantID,
EventID: ID // <- NOTE change of identifier.
}
I think I will update the API to return a consistent identifier and avoid the headache later on. Only wasted about 3 hours on this...

Vuex Getters Come Back as Undefined

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.

Axios-Make multiple request at once (vue.js)

How to make multiple requests in parallel using axios and vue ?
Since axios can be used by React and Vue it is pretty much the same code.
Make sure to read axios docs, you can understand it from there.
Anyway, I am going to show you an example:
<template>
<div>
<button #click="make_requests_handler">Make multiple request</button>
{{message}} - {{first_request}} - {{second_request}}
</div>
</template>
And the script:
import axios from 'axios'
export default {
data: () => ({
message: 'no message',
first_request: 'no request',
second_request: 'no request'
}),
methods: {
make_requests_handler() {
this.message = 'Requests in progress'
axios.all([
this.request_1(), //or direct the axios request
this.request_2()
])
.then(axios.spread((first_response, second_response) => {
this.message = 'Request finished'
this.first_request = 'The response of first request is' + first_response.data.message
this.second_request = 'The response of second request is' + second_response.data.message
}))
},
request_1() {
this.first_request: 'first request began'
return axios.get('you_URL_here')
},
request_2() {
this.second_request: 'second request began'
return axios.get('another_URL', { params: 'example' })
}
}
}
You can pass your asynchronous calls to Promise.all.
As long as each of them returns a promise they will execute at the same time.
I'm using store.dispatch but you could equally use axios calls or fetch.
In this example i'm making the calls when the vue component gets created:
...
async created() {
const templates = this.$store.dispatch(TEMPLATES_LOAD);
const userTemplates = this.$store.dispatch(USER_TEMPLATES_LOAD);
const players = this.$store.dispatch(OTHER_PLAYERS_LOAD);
return await Promise.all([templates, userTemplates, players])
.then(() => {
console.log('Loaded data for form elements');
});
}