I'm having trouble accessing the properties of my sent object from my parent component.
In my child component, I would like to access "found" in the object "lettersState" (see code below). When I try to access "found" in my child component. refer the error message
Property 'found' does not exist on type 'Object'.
parent component :
const state = reactive({
guesses: ['', '', '', '', '', ''],
solution: '',
currentRow: 0,
lettersState: {
found: [] as string[],
miss: [] as string[],
hint: [] as string[],
}
})
<template>
<Keyboard #handleKeyboard="handleKeyboard" :lettersState="state.lettersState" />
</template>
child :
const props = defineProps<{lettersState:Object}>()
console.log(props.lettersState.found)
const props = defineProps<{lettersState:Object}>()
tells typescript that your props look like this:
{
lettersState: {}
}
But looking at :lettersState="state.lettersState" you want it to be:
type LetterState = {
found: string[]
miss: string[]
hint: string[]
}
const props = defineProps<{letterState: LetterState}>()
You're passing :lettersState="state.lettersState", and then you access console.log(props.lettersState.found). If you look closer, you can see that letterState does not exist in what you passed as a prop: You only passed the value corresponding to letterState. Therefore, there is no need to access the letterState key. Try this:
const props = defineProps<{
found: string[],
miss: string[],
hint: string[]
}>()
console.log(props.found)
Related
I want to implement a props checker function, which is used to handle the situation with unexpected props problem. Below is an example,
<GrandFater :scope="scope" :id="id" v-bind="$attrs" #error="error" v-on="$listeners">
<Father>
<GrandSon />
<Father/>
<GrandFather />
Then, in the component GrandSon, I will pass these two props to a function to make further process, let's say authorize. Now, what I want to do is to implement a props checker function to check whether the two props is valid. For example, the prop scope is a string, but it cannot be missed and it also cannot be an empty string.
It cannot be missed means it must be declared in <GrandFather> component, such as
<GrandFater :id="id" v-bind="$attrs" #error="error" v-on="$listeners">
<Father>
<GrandSon />
<Father/>
<GrandFather />
In above example, the prop scope missed and I want to capture the error in my function.
class GrandFather extends Vue {
#Prop({ type: String }) id!: string;
#Prop({ type: String }) scope!: string;
mounted(){
if(propChecker(this)) return;
authorize(this);
}
}
In another JS file, I implement the propChecker like this:
export function parameterChecker(vm) {
let error: String = "";
switch (vm) {
case !vm.id:
error = "Parameter Id missing.";
break;
case !vm.id.trim():
error = "Parameter Id cannot be empty.";
break;
case !vm.scope:
error = "Parameter scope missing.";
break;
case !vm.scope.trim():
error = "Parameter scope cannot be empty.";
break;
default:
return false;
}
vm.$emit("error", error);
return true;
}
But now the question is if I miss a prop, such as scope, it will report an error:
[Vue warn]: Error in mounted hook: "TypeError: Cannot read properties of undefined (reading 'trim')".
Now the question is how could I implement my goal, and why this error will happen?
I would recommend using the prop validation (see the website here).
You can pass the props of your component using the props object which also contains object. Then you can either specify default parameters (if those aren't pass) or even use the required prop (which will throw an error if the props isn't pass).
All props are optional by default, unless required: true is specified.
Example :
props: {
myFirstProp: {
type: Object,
default: () => {scope: null},
required: true,
},
mySecondProp: {
type: String,
default: 'defaultValue'
},
myThirdProp: {
type: Boolean,
default: true
},
}
i want to pass a String to my child component like this, but previously i want to print it
this is my parent component
{{left.leftA}} // here show me 8
<CustomCard
:left="left.leftA"
export default {
name: 'Parent',
data() {},
setup() {
onMounted(async () => {
const answer = await getData(name)
left.value = answer.response //{leftA:'A', leftB:'B'...}
})
and in my child component i have this declaration
export default {
name: 'CustomCard',
props: {
left: {
type: String,
required: true,
},
i am getting this warning:
[Vue warn]: Invalid prop: type check failed for prop "left". Expected String with
value "undefined", got Undefined
Does it have something to do with how I am loading the data? is it ok to use onMounted?
This is happening because the initial value for value is null. So, on initial render it throws the warning, but upon another render it has the correct prop type (a string) and renders correctly.
You have 3 options. Allow '' as an option on the prop or don’t render the component until you have the correct data or make use of computed Property.
Option-1
{{left.leftA}} // here show me 8
<CustomCard
:left="left.leftA ? left.leftA : ''"
Option-2
{{left.leftA}} // here show me 8
<CustomCard v-if="loaded"
:left="left.leftA"
and in onMounted(}
onMounted(async () => {
const answer = await getData(name)
left.value = answer.response //{leftA:'A', leftB:'B'...}
// Set the loaded flag as true here. Also make sure its set as false inside the setup()
})
Option-3
{{left.leftA}} // here show me 8
<CustomCard
:left="sendVal"
In computed....
computed: {
sendVal() {
if(left && left.left1) return left.left1;
return '';
}
}
I am trying to send an array containing arrays which in turn contains objects to one component from another, but the content from the array seems to be empty in the child component.
I have tried sending the data as a String using JSON.Stringify() and also as an array
My parent component:
data: function(){
return{
myLineItems : []
}
},
created(){
this.CreateLineItems();
},
methods:{
CreateLineItems(){
let myArrayData = [[{"title":"Title1","value":2768.88}],[{"title":"Title2","value":9}],[{"title":"Title3","value":53.61},{"title":"Title4","value":888.77},{"title":"Title5","value":1206.11},{"title":"Title6","value":162.5}]]
this.myLineItems = myArrayData;
}
}
My parent component's template:
/*
template: `<div><InvoiceChart v-bind:lineItems="myLineItems"></InvoiceChart></div>`
My child component:
const ChildComponent= {
props: {
lineItems: {
type: Array
}
},
mounted() {
console.log(this.lineItems);
}
};
The parent component is created as so (inside a method of our main component):
var ComponentClass = Vue.extend(InvoiceDetails);
var instance = new ComponentClass({
propsData: { invoiceid: invoiceId }
});
instance.$mount();
var elem = this.$refs['details-' + invoiceId];
elem[0].innerHTML = "";
elem[0].appendChild(instance.$el);
If I try to do a console.log(this) inside the childcomponent, I can see the correct array data exist on the lineItems property..but i can't seem to access it.
I have just started using VueJS so I haven't quite gotten a hang of the dataflow here yet, though I've tried reading the documentation as well as similar cases here on stackoverflow to no avail.
Expected result: using this.lineItems should be a populated array of my values sent from the parent.
Actual results: this.lineItems is an empty Array
Edit: The problem seemed to be related to how I created my parent component:
var ComponentClass = Vue.extend(InvoiceDetails);
var instance = new ComponentClass({
propsData: { invoiceid: invoiceId }
});
instance.$mount();
var elem = this.$refs['details-' + invoiceId];
elem[0].innerHTML = "";
elem[0].appendChild(instance.$el);
Changing this to a regular custom vue component fixed the issue
Code - https://codesandbox.io/s/znl2yy478p
You can print your object through function JSON.stringify() - in this case all functions will be omitted and only values will be printed.
Everything looks good in your code.
The issue is the property is not correctly getting passed down, and the default property is being used.
Update the way you instantiate the top level component.
Try as below =>
const ChildComponent= {
props: {
lineItems: {
type: Array
}
},
mounted() {
console.log(this.lineItems);
}
};
I am creating then passing an object using pdfjs in to a child Vue component. When I do so, I can access the object itself, but I cannot access any properties of the object.
This is the case during all of the lifecycle hooks.
<i-slide-deck-pdf // calling child vue component
v-if="true"
:slideDeckItem="fetchPDF('/static/intropdf.pdf')"
:current-user-progress="currentUserProgress"
#i-progress="putProgressTracker"
#i-slide-change="onSlideChange"
/>
...
fetchPDF(url) { // function being used to create the object
let pdfItem = new Object();
import(
'pdfjs-dist/webpack'
).
then(pdfjs => pdfjs.getDocument(url)).
then(pdf => {
pdfItem.pdf = pdf;
pdfItem.pages = range(1, pdf.numPages).map(number => pdf.getPage(number));
pdfItem.pageCount = pdfItem.pages.length;
})
return pdfItem;
},
...
props: { // prop call in child component
slideDeckItem: {
type: Object,
required: true
},
}
Console log
Thanks in advance.
This is because the async call hasn't completed, so you are just returning an empty object, to fix this you want to set a value inside the then portion of your code, and bind this to your prop, so:
fetchPDF(url) { // function being used to create the object
let pdfItem = new Object();
import(
'pdfjs-dist/webpack'
).
then(pdfjs => pdfjs.getDocument(url)).
then(pdf => {
pdfItem.pdf = pdf;
pdfItem.pages = range(1, pdf.numPages).map(number => pdf.getPage(number));
pdfItem.pageCount = pdfItem.pages.length;
// This should be inside the "then"
this.slideDeckItem = pdfItem;
})
},
You'll then want to declare slideDeckItem in your parent data property, and bind that to your component's prop:
<i-slide-deck-pdf
v-if="true"
:slideDeckItem="slideDeckItem"
:current-user-progress="currentUserProgress"
#i-progress="putProgressTracker"
#i-slide-change="onSlideChange"
/>
I've made a JSFiddle, to give you the basic idea, although I've used a timeout to simulate the async call: http://jsfiddle.net/ga1o4k5c/
You may also want to take a look at how Promises work
I am trying to create a component who task is to simply add a class on whatever element is passed to it as slot when its just one node
Usage:
<my-component>
<button>hello</button>
</my-component>
Output:
<button class="added-by-component">hello</button>
I tried with <template> tag, but <slot> isn't allowed to be on the root.
Even with render() function, I tried returning the passed slot's vnode after modifying the class property:
render (createElement) {
var vnode = this.$slots.default[0]
vnode.data = vnode.data || {}
vnode.data.class = { 'added-by-component': this.someCondition }
return vnode
}
Even this doesn't work as expected. I don't get the class added even when the condition is true.
const Wrapper = {
functional: true,
render (h, ctx) {
const slots = ctx.slots()
const node = slots.default[0]
node.data.staticClass = [
'my-class',
node.data.staticClass || ''
].join(' ')
return node
}
}
You can use data.class too but it would need extra type handling.