This question already has answers here:
Access vue instance/data inside filter method
(3 answers)
Closed 2 years ago.
I'm creating a simple Vuejs div component (to show a specific value) which needs to receive: a lists, a placeholder and a value as props. What I'm trying to do is displaying the value with the data from my database, if the user picks a new value from the lists, it should take that new value and display it. However, if the user never picks a new value and the data from the database is empty, it should display the placeholder.
So I have used filters to achieve this. However, it outputs an error: "Cannot read property 'lists' of undefined", which comes from the filters (I know because it outputs no error if I comment out the filters). When I changed the filter to this:
filters: {
placeholderFilter () {
return this.placeholderText || this.placeholder
}
}
It says:""Cannot read property 'placeholderText' of undefined"". So I was wondering if the filters properties executed before the data and props properties. What is the execution order of them? I have attached some of the relevant code down below. Anyway, If you could come up with a better way to achieve this. I would appreciate it!
Here is my component:
<template>
<div>{{ placeholderText | placeholderFilter }}</div>
<li #click="pickItem(index)" v-for="(list,index) in lists" :key="index">{{ list }}</li>
</template>
<script>
export default {
props: {
lists: {
type: Array,
required: true
},
value: {
type: [String, Number],
default: ''
},
placeholder: {
type: String,
default: ''
}
},
data () {
return {
selected: -1,
placeholderText: this.value || this.placeholder
}
},
methods: {
pickItem (index) {
this.selected = index
}
},
filters: {
placeholderFilter () {
return this.lists[this.selected] || this.placeholderText || this.placeholder
}
}
}
</script>
And this is where I use it:
<my-component
placeholder="Please type something"
value="Data from database"
lists="['option1','option2','option3']"
>
</my-component>
Filters aren't bound to the component instance, so they simply don't have access to it through the this keyword. They are meant to always be passed a parameter and to return a transformed version of that parameter. So in other words, they're just methods. They were removed in Vue 3 entirely probably for that reason.
And yeah, what you're looking for here is a computed!
Related
I would like to take the output of one query (a TRPC query on Prisma) and use this as the dependent input in a future query.
I followed the dependent documentation for React Query but running into type errors that the return of the first may possibly be undefined (e.g. product is possibly 'undefined'):
const { data: product } = api.product.getUnique.useQuery({ id: pid });
const options = api.option.getAll.useQuery(
{
product: product.productSize,
region: product.productRegion,
},
{ enabled: !!product }
);
Does the inclusion of enabled not already handle this? If not, what is the correct way to adapt for Typescript.
Just casting the product value as a boolean return any truthy value (f.e if product will be equal to {} it will still result in true, that means that product won't necessarily have the productSize or productRegion properties, I would change it first to:
{ enabled: !!product && product.productSize && product.productRegion }
If that doesn't fix the typescript error, you as a developer can know for sure that the values are actually there so what you can use the as keyword in typescript to tell it that you know for sure that the type is what you want it to be:
(In this example I assumed that the values are string but you can change it to number or whatever the true value of them are)
const options = api.option.getAll.useQuery(
{
product: product.productSize as string,
region: product.productRegion as string,
},
{ enabled: !!product && product.productSize && product.productRegion }
);
I'm trying to make a function where users can added multiple resume posts (from child component) to an array (in parent).
The problem is, every object I push to parent array stays reactive with the child form/object. So if I for example clear the form in child component, the Object I pushed to the parent array gets all it's values cleared as well. How to I emit and push the post-object to parent array and stop it from being reactive, so I can add new/more resume posts?
CreateProfile.vue
<template>
<ResumePostInput :resume_posts="form.resume_posts" #resumeHandler="handleResume"/>
</template>
<script>
export default {
data() {
form: {
resume_posts: []
}
}
methods: {
handleResume(post) {
this.form.resume_posts.push(post)
}
}
}
</script>
ResumePostInput.vue
<template
-- Input fields binded to post object --
</template>
<script>
export default {
emits: ["resumeHandler"],
props: {
resume_posts: Array
},
data() {
return {
post: {
title: '',
sub_title: '',
text: '',
year_from: '',
year_to: '',
type: ''
}
}
},
methods: {
addResume() {
this.$emit("resumeHandler", this.post)
}
}
}
</script>
you emit unknown property, it's a post, not posts
and learn about JS object, there are copy by reference & value
maybe you just need to update your addResume method like this
addResume() {
const post = JSON.parse(JSON.stringify(this.post))
this.$emit("resumeHandler", post)
}
It's not the problem that the object is reactive but that it's the same object because objects are passed by reference in JavaScript. If it's modified in one place, it's modified everywhere.
In order to avoid this, the object needs to be explicitly copied. For shallow object this can be done with object spread:
this.$emit("resumeHandler", {...this.post})
For deeply nested objects, multiple spreads or third-party clone function can be used.
I am using VueJS in my first real life webdev project, and am having trouble trying to query Firebase's firestore. I am using VueFire plugin for Vue FYI. I have users in a 'Patient' collection in firebase, and can access this list of patients by using the following query:
const db = firebase.firestore();
export default {
...
data () {
return {
email: firebase.auth().currentUser.email,
therapistData: [],
patientsList: []
}
},
firestore () {
return {
therapistData: db.collection('Therapist').doc(this.email),
patientsList: db.collection('Patient').where('practiceName', 'array-contains', 'TestPractice')
}
}
Each patient contains an array practiceName, and each therapist contains a string practiceName. patientsList should give me an object containing every patient in the Patient collection where their practiceName array contains the currently logged in user's (the Therapist) practiceName.
When hard coding in a string such as 'TestPractice' in my where query, the patientsList is populated as expected, and I can display it using the following vue code:
<div v-for="(userItem, idx) in patientsList) :key="idx">
<p>{{ patientsList[idx].firstName }} {{ patientsList[idx].lastName }}</p>
</div>
So this code is all working properly. My issue is that I am trying to dynamically (based on the logged in Therapist) pull the practiceName value, and pass it as the third argument to my patientsList where query. So it should read:
patientsList: db.collection('Patient').where('practiceName', 'array-contains', this.therapistData.practiceName)
When I save this code, it gives me an error saying that where() expects a valid third argument, and got "undefined". Why is this undefined? When I render in vue {{ therapistData.practiceName }} or
<button #click="patientsList(therapistData.practiceName)">Test method</button>
...
methods: {
patientsList: function(practice) {
console.log('Practice: ' + practice)
}
}
I can see the proper value of therapistData.practiceName print in the console. Can anyone please help me understand or fix this issue? Why is practiceName showing as undefined when trying to use it in my where() query?
Maybe you need to do programmatic binding
export default {
data() {
return {
therapistData: [],
}
},
watch: {
therapistData: {
// call it upon creation too
immediate: true,
handler(therapistData) {
this.$bind('practiseName', therapistData.practiseName)
},
},
},
}
In a Vue Js component, I need to loop through an object on the mounted hook that's in local storage in Vuex to update the data properties as you can see in code example.
I'm trying to update this.title, this.body, this.id whereby the rightHere variable in the loop is outputting these names as string values as the var you can see.
this.rightHere
...is the problem I know, and is obviously trying to target a data property "rightHere" which doesn't exist. But I don't know how else to overcome this in javascript and make rightHere output the string as needed? So how do I use this in a loop to dynamically change but tell Vue to update this. on each iteration?
data() {
return {
title: '',
body: '',
id: '',
}
},
mounted() {
for (var rightHere in this.$store.getters.getObject) {
if (this.$store.getters.getObject.hasOwnProperty(rightHere )) {
this.rightHere = this.$store.getters.getObject[rightHere ]
}
}
},
You would typically set the key in your template. It's a reserved word.
<div v-for='item in items' :key='$store.getters.getKey(item)'>{{item.title}}</div>
Suppose I have an array feedsArray, the example value may look like this:
this.feedsArray = [
{
id: 1,
type: 'Comment',
value: 'How are you today ?'
},
{
id: 2,
type: 'Meet',
name: 'Daily sync up'
}
]
Suppose I have registered two components: Comment and Meet, Each component has a prop setting as the following:
props: {
feed: Object
}
and the main component has the following definition:
<component v-for="feed in feedsArray" :feed="feed" :key="feed.id" :is="feed.type"></component>
As you can see, it uses is property to select different component. My question is, how to detect feed object change in the child component ? Like when I set
this.feedsArray[0] = {
id: 1,
type: 'Comment',
value: 'I am not ok'
}
How can the Comment component detect the changes ? I tried to add a watcher definition in the child component like the following:
watch: {
feed: {
handler (val) {
console.log('this feed is changed')
},
deep: true
}
},
But it doesn't work here. Anyone know how to solve this ?
Do not assign directly to an array using index - use splice() instead, otherwise JavaScript can not detect that you have changed the array.
If you want to change only the value of an already existing key of an object - then simply update it e.g. this.feeds[0].value = 'I am not okay any more';
This works for existing keys only - otherwise you have to use this.$set(this.feeds[0], 'value', 'I am not okay any more');