Can't set Ref data property in Vue3, item undefined - javascript

Essentially I'm using Vue3 and Nuxt3. I'm trying to grab the :id param from the URL and if it matches an ID in a JSON it updates a ref data point called 'exists'. My console log shows all that is working. The issue is this.exists is showing as undefined. I attempted the const self = this; trick, to no avail.
Full warning:
500: Cannot set properties of undefined (setting 'exists')
<template>
<div class="single-hunt">
<div class="container">
Single Hunt
</div>
</div>
</template>
<script>
import hunt from '../../assets/api/hunts.json'
import { onMounted } from 'vue';
import { useRoute } from 'vue-router'
export default {
setup() {
const route = useRoute();
const id = route.params.id;
const exists = ref(false);
onMounted(() => {
for (let i = 0; i < hunt.length; i++) {
if (hunt[i].address == id) {
console.log('exists')
this.exists = true;
break;
}
}
})
return {
hunt,
exists
}
}
}
</script>
<styles src="../../assets/css/singlehunt.css"/>

You should import ref from vue: import { ref } from 'vue';
https://vuejs.org/guide/essentials/reactivity-fundamentals.html#reactive-variables-with-ref

Related

vue3 props passed to child component are undefined

Im struggling with passing data to my child component.
On level of parent componenet, I'm doing nested axios call to retrieve the data. Then I pass this data to my child component. Data is correct, but child component sees it as undefined.
It looks like the child component is rendered before the actual call is finished.
parent:
<template>
<User_Menu v-if="user_info" :user="user_info" :user_tickets="user_tickets"/>
</template>
<script setup lang="ts">
import User_Menu from "#/components/User/User_Menu.vue"
import { useRoute } from 'vue-router'
import { ref, reactive, onMounted } from 'vue'
import { UserQrInstance } from '#/interfaces/user';
import { UserTickets } from '../interfaces/tickets';
import { post_call, get_call} from "#/api"
import { error_notification } from "#/error_notifications"
const route = useRoute()
const user_id = ref(0)
const user_info = reactive({} as UserQrInstance)
const user_tickets = reactive([] as UserTickets)
const get_user_tickets = async (qr: string) => {
try
{
const response = await post_call('tickets/get_tickets', {qr_code: qr})
Object.assign(user_tickets, response.data)
}
catch (e){
error_notification(e)
}
}
const get_user = async () => {
try
{
const response = await get_call(`users/find_user/${user_id.value}`)
Object.assign(user_info, response.data)
await get_user_tickets(response.data.qr_code)
}
catch (e){
error_notification(e)
}
}
onMounted(() => {
user_id.value = route.params.id
get_user()
})
</script>
child:
<template>
{{props.user}}
</template>
<script setup lang="ts">
const props = defineProps<{user: Ref<UserQrInstance>, user_tickets: Ref<UserTickets>}>()
console.log(props.user) -> ok
console.log(props.user.name) -> undefined
</script>
How can I make it working? Some delay or something?

react axios URL concatenation returns 404 not found

I am trying to display dynamic data based on record id coming from useParams hook variable id. But when I concatenated the id value, it returns not found 404 error. Although the id value is returned as valid id when I console it, the concatenation doesn't work.
Here is my code
import React, { useEffect, useRef, useState } from "react";
import SignaturePad from "react-signature-canvas";
import offer from "./assets/offer.PNG";
import { Document, Page } from "react-pdf";
// Import the main component
import { Viewer } from "#react-pdf-viewer/core"; // install this library
// Plugins
import { defaultLayoutPlugin } from "#react-pdf-viewer/default-layout"; // install this library
// Import the styles
import "#react-pdf-viewer/core/lib/styles/index.css";
import "#react-pdf-viewer/default-layout/lib/styles/index.css";
// Worker
import { Worker } from "#react-pdf-viewer/core"; // install this library
import axios from "axios";
import { useParams } from "react-router-dom";
const Signature = (props) => {
const id = useParams();
const [numPages, setNumPages] = useState(null);
const baseURL = "http://127.0.0.1:8000/rent/" + id;
const [datas, setData] = useState([]);
useEffect(() => {
axios
.get(baseURL)
.then((response) => {
setData(response.data);
})
.then(
(response) => {},
(err) => {
console.log("No Data To Show");
}
)
.catch((err) => {
return false;
});
}, []);
// Create new plugin instance
const defaultLayoutPluginInstance = defaultLayoutPlugin();
console.log(docId);
return (
<div className="p-10 flex flex-col space-y-24 font-serif justify-center items-center">
<img src={imgg} />
{datas?.file && (
<>
<Worker workerUrl="https://unpkg.com/pdfjs-dist#2.6.347/build/pdf.worker.min.js">
<Viewer
fileUrl={datas?.file}
plugins={[defaultLayoutPluginInstance]}
/>
</Worker>
</>
)}
</div>
);
};
export default Signature;
Here is the value of id which is dynamically changing.
But when I pass the value of id as follows it works fine.
const baseURL =
"http://127.0.0.1:8000/rent/ce779e1d-3afb-4aa7-82e8-5bf74c4af0a7";
But when I concatenate the id variable it returns 404 not found error.
const baseURL =
"http://127.0.0.1:8000/rent/"+id;
What's my mistake here?
useParams hook of React Router returns an object with params.
You should to use something like that:
const { id } = useParams();
in the case if your params is called id.
More you can see here, in the documentation: https://v5.reactrouter.com/web/api/Hooks/useparams

Can't delay using data in Vue for after getting value from database

I need to compare string that I get from firebase in document.check1 with some strings (written hard in function below) and show Content. I know how to call it out in button but I want to check it right after entering the page - not after clicking. When I try to do it - I get error that it has no value. How can I make it "wait" for the data to collect automaticaly?
<template>
<router-link to="/konto">Back</router-link>
<div v-if="document">
<div>
<span>1:</span>
{{ document.check1 }},
<span>2:</span>
{{ document.check2 }},
<span>3:</span>
{{ document.check3.length }}
</div>
</div>
<button v-if="itWorkOk" #click="documentCheck">Show Content after finding result</button>
<div v-if="isOther">
<p>Content</p>
</div>
</template>
<script>
import getUser from "../composables/getUser";
import getDocument from "../composables/getDocument";
import { ref } from "#vue/reactivity";
export default {
props: ["id", "document"],
setup(props) {
const { error, document } = getDocument("AllData", props.id);
const { user } = getUser();
const itWorkOk = ref(true);
const result1 = ref("");
const isOther = ref("");
const documentCheck = async () => {
const isItOk = document.value.check1
if (isItOk == "Result One") {
result1.value = true;
itWorkOk.value = false;
} else {
isOther.value = true;
itWorkOk.value = false;
}
};
return {
error, user, document, documentCheck, result1, isOther, itWorkOk,
};
},
};
</script>
The error (when I put function to call immediately):
Uncaught (in promise) TypeError: document.value is null
The getDocument code:
import { ref, watchEffect } from 'vue'
import { projectFirestore } from '../firebase/config'
const getDocument = (collection, id) => {
const document = ref(null)
const error = ref(null)
let documentRef = projectFirestore.collection(collection).doc(id)
const unsub = documentRef.onSnapshot(doc => {
if(doc.data()) {
document.value = {...doc.data(), id: doc.id}
error.value = null
} else {
error.value = "Document does not exist"
}
}, err => {
console.log(err.message)
error.value = 'Couldn't get the document'
})
watchEffect((onInvalidate) => {
onInvalidate(() => unsub());
});
return { error, document }
}
export default getDocument
If I correctly understand your question, you need to fetch the Firestore data in one of the lifecycle hooks before the component is mounted, for example created or mounted.
In the lifecycle hook, you asynchronously read the data from Firestore and compare the result to the desired values.
I used setTimeout(function () { methotToCall(); }, 1000); thanks to this thread - stackoverflow.com/questions/24849/… and it "worked" so I'm gonna close this one. I'm sure this is not the right method, but it will work for a time. Thank's for help :)

How do you ensure that a data set from a reusable function is available in a template

I have a reusable file called: useLanguage.js
import { defineComponent, ref, computed, onMounted, getCurrentInstance} from "vue";
import { useStore } from "vuex";
export default function useLanguage() {
const store = useStore();
const data = ref(null);
const profileLanguages = computed(() => {
return store.getters.allProfiles.data;
});
const currentDataSet = computed(() => {
return store.getters.currentProductSpecification.name
});
function availableLanguages() {
const obj = {}
const arr = [];
let i;
const object = profileLanguages.value;
const data = JSON.parse(currentDataSet.value)
for (i = 0; i < object.length; i++) {
arr.push(object[i].language)
obj[object[i].language] = '';
}
let y;
for (const key in data) {
arr.push(key)
obj[key] = data[key];
}
data.value = obj;
console.log(data.value) // Its filled as: {eng: 'english'}
}
function updateData(data) {
currentDataSet.value.name = JSON.stringify(data);
}
return {
data,
availableLanguages,
updateData
}
}
In my template file called: Filter.vue I call the file useLanguage again and use several functions:
<template>
<div class="modal fade" id="es_modal_edit_text" tabindex="-1" aria-modal="false">
<div v-for="(value, name) in data" :key="name" class="fv-row mb-7">
<label class="fs-6 fw-bold mb-2">{{ t("authorized.languageLabels." + name) }}</label>
<input type="text" class="form-control form-control-solid" placeholder="" v-model="data[name]" #input="updateData(data)">
</div>
</template>
<script>
import { defineComponent, ref, computed, onMounted, onBeforeMount } from "vue";
import useLanguage from '#/composables/useLanguage';
export default defineComponent({
props: ['currentId'],
setup() {
const { t } = useI18n();
const { availableLanguages, data, updateData } = useLanguage();
function update() {
this.currentProductSpecification.name = JSON.stringify(this.data);
}
availableLanguages()
return {
data,
t,
updateData
}
}
});
</script>
The problem I'm experiencing is that within this template I'm trying to create a input field. But data is null still. I think it has to do with the fact that data is not available while the template is being created because the function is not ready ( availableLanguages() )
How to solve this?
I don't really have enough code to go off of here, but a naive solution to your problem would be having a dataLoaded flag in your component that gets switched to true once the function finishes. You can then use this dataLoaded flag in a v-if to display a loading indicator, and once it's loaded it'll automatically display the requested data.
Something like this:
<template>
<div v-if="!dataLoaded">
<p>Loading Data...</p>
</div>
<div v-else>
<!-- Your old code goes here -->
</div>
</template>
<script setup>
import { ref } from "vue";
const dataLoaded = ref(false);
const data = ref({});
onMounted() {
// Return the value that data should be, then set the loaded flag to true
data.value = availableLanguages();
dataLoaded.value = true;
}
</script>
This way, Vue will watch the reactive dataLoaded value and wait until it turns true. Once it does, it'll automatically replace the div in the template to the proper one, and all the data required will be there.

Why does router.query return an empty object in NextJS on first render?

My url is: http://localhost:3000/company/60050bd166cb770942b1dadd
I want to get the value of the id by using router.query. However when I console log router.query, it returns an empty object first and then return the object with data. This results in bugs in other parts of my code as I need the value of the id to fetch other data.
This is my code:
import { useRouter } from 'next/router';
import styles from './CompanyId.module.css';
import { useQuery } from '#apollo/client';
import { COMPANY_DETAILS } from '../../queries/company';
const CompanyDetails = () => {
const router = useRouter();
console.log(router.query);
const { loading, data } = useQuery(COMPANY_DETAILS, {
variables: { _id: companyId },
});
return (
<div className={styles.container}>
{loading ? <h1>Loading</h1> : <h1>{data.company.name}</h1>}
</div>
);
};
export default CompanyDetails;
My program is crashing right now because the companyId variable is empty on the first render. Is there anyway to go around this problem?
In Next.js:
Pages that are statically optimized by Automatic Static Optimization will be hydrated without their route parameters provided, i.e query will be an empty object ({}).
After hydration, Next.js will trigger an update to your application to provide the route parameters in the query object.
I solved it by using useLazyQuery instead of useQuery, and wrapped the function inside useEffect.
The problem was that NextJS's router.query returns an empty object on the first render and the actual object containing the query comes in at the second render.
This code works:
import React, { useEffect } from 'react';
import { useRouter } from 'next/router';
import styles from './CompanyId.module.css';
import { useLazyQuery } from '#apollo/client';
import { COMPANY_DETAILS } from '../../queries/company';
const CompanyDetails = () => {
const router = useRouter();
const [getCompany, { loading, data }] = useLazyQuery(COMPANY_DETAILS);
useEffect(() => {
if (router.query.companyId) {
getCompany({ variables: { _id: router.query.companyId } });
}
}, [router.query]);
if (loading) return <h1>Loading....</h1>;
return (
<div className={styles.container}>
{data && <h1>{data.company.name}</h1>}
</div>
);
};
export default CompanyDetails;

Categories