I have a Form.vue
component that is used in other parent components to submit data from the user to the server.
The Form.vue
has a prop to indicate whether its parent component has passed its own validation checks (the parent could be anything from a HR record to a video upload). This is how it goes:
Form.vue
export default {
name: "ComponentForm",
props: {
PassedParentValidation: {
type: Boolean,
default: true
}
},
emits: ['Publishing'],
setup(props, context) {
...
async function sendForm() {
context.emit('Publishing') // Triggers parent to perform its own validation
// not waiting for props to update here
if(!props.PassedParentValidation){
const Err = new Error()
Err.message = 'Failed parent validation'
Err.code = 400;
throw Err;
}
else {
//Send the form
}
}
}
A parent component will use the Form.vue
child component a bit like this:
Parent.vue
<template>
<h1>Your HR record</h1>
<Component-Form :PassedParentValidation="PassedValidation" @Publishing="validateRecord" />
</template>
<script>
import ComponentForm from "../components/Form.vue";
export default {
name: 'ViewParent',
"Component-Form": ComponentForm,
setup() {
const PassedValidation = ref(null);
async function validateRecord(){
if (SomeReallyComplexConditionThatCouldTake10SecondsToComplete) {
PassedValidation.value = true;
} else if (!SomeReallyComplexConditionThatCouldTake10SecondsToComplete) {
PassedValidation.value = false;
}
return {
validateRecord,
PassedValidation
}
}
</script>
Here are the steps that I expect to happen:
- User submits the form which runs
sendForm()
inForm.vue
sendForm()
tells theParent.vue
that it is publishingParent.vue
does a validation check usingvalidateRecord()
- If
validateRecord
fails validation, thenprops.PassedParentValidation
should be false and throw an error inForm.vue
- If
validateRecord
passes validation, then it setsPassedValidation
to true which should update theprop
inForm.vue
astrue
i.e.props.PassedParentValidation === true
and not throw an error
What is happening is that Form.vue
is not 'awaiting' the prop.PassedParentValidation
to update before running the rest of the sendForm()
code.
So if props.PassedParentValidation === false
, it throws an error. But if the user submits the form again and PassedValidation === true
in Parent.vue
, then that is not being passed to Form.vue
in time.
That is, Form.vue
is not waiting for the prop to change value and so even if the parent's PassedValidation === true
it still remains false in Form.vue
and throws an error.
How can I overcome this?
SomeReallyComplexConditionThatCouldTake10SecondsToComplete
is currently just checking if some data exists in the parent e.g. Array.isArray(MyData) && MyData.length
but it will grow to accomodate many checks in the parent. I cannot know how complex it will be become.
I have a Form.vue
component that is used in other parent components to submit data from the user to the server.
The Form.vue
has a prop to indicate whether its parent component has passed its own validation checks (the parent could be anything from a HR record to a video upload). This is how it goes:
Form.vue
export default {
name: "ComponentForm",
props: {
PassedParentValidation: {
type: Boolean,
default: true
}
},
emits: ['Publishing'],
setup(props, context) {
...
async function sendForm() {
context.emit('Publishing') // Triggers parent to perform its own validation
// not waiting for props to update here
if(!props.PassedParentValidation){
const Err = new Error()
Err.message = 'Failed parent validation'
Err.code = 400;
throw Err;
}
else {
//Send the form
}
}
}
A parent component will use the Form.vue
child component a bit like this:
Parent.vue
<template>
<h1>Your HR record</h1>
<Component-Form :PassedParentValidation="PassedValidation" @Publishing="validateRecord" />
</template>
<script>
import ComponentForm from "../components/Form.vue";
export default {
name: 'ViewParent',
"Component-Form": ComponentForm,
setup() {
const PassedValidation = ref(null);
async function validateRecord(){
if (SomeReallyComplexConditionThatCouldTake10SecondsToComplete) {
PassedValidation.value = true;
} else if (!SomeReallyComplexConditionThatCouldTake10SecondsToComplete) {
PassedValidation.value = false;
}
return {
validateRecord,
PassedValidation
}
}
</script>
Here are the steps that I expect to happen:
- User submits the form which runs
sendForm()
inForm.vue
sendForm()
tells theParent.vue
that it is publishingParent.vue
does a validation check usingvalidateRecord()
- If
validateRecord
fails validation, thenprops.PassedParentValidation
should be false and throw an error inForm.vue
- If
validateRecord
passes validation, then it setsPassedValidation
to true which should update theprop
inForm.vue
astrue
i.e.props.PassedParentValidation === true
and not throw an error
What is happening is that Form.vue
is not 'awaiting' the prop.PassedParentValidation
to update before running the rest of the sendForm()
code.
So if props.PassedParentValidation === false
, it throws an error. But if the user submits the form again and PassedValidation === true
in Parent.vue
, then that is not being passed to Form.vue
in time.
That is, Form.vue
is not waiting for the prop to change value and so even if the parent's PassedValidation === true
it still remains false in Form.vue
and throws an error.
How can I overcome this?
SomeReallyComplexConditionThatCouldTake10SecondsToComplete
is currently just checking if some data exists in the parent e.g. Array.isArray(MyData) && MyData.length
but it will grow to accomodate many checks in the parent. I cannot know how complex it will be become.
1 Answer
Reset to default 0You can use a watch to monitor changes to the PassedParentValidation prop and update the internal state accordingly.
<template>
<div>
<!-- Your form here -->
<button @click="sendForm">Submit</button>
</div>
</template>
<script>
export default {
name: "ComponentForm",
props: {
PassedParentValidation: {
type: Boolean,
default: true
}
},
emits: ['Publishing'],
setup(props, { emit }) {
const isParentValid = ref(props.PassedParentValidation); // Internal state to react to prop changes
watch(
() => props.PassedParentValidation,
(newVal) => {
isParentValid.value = newVal; // Update internal state when prop changes
}
);
async function sendForm() {
emit('Publishing'); // Notify parent to perform validation
// Wait for the parent's validation process to complete
await nextTick();
// Check the updated state after validation
if (!isParentValid.value) {
const Err = new Error();
Err.message = 'Failed parent validation';
Err.code = 400;
throw Err;
} else {
// Send the form if validation passes
console.log("Form submitted successfully!");
}
}
return {
sendForm,
isParentValid
};
}
};
</script>
:onPublishing="validateRecord"
andawait props.onPublishing()
. There aren't many cases when callback props are suitable in vue but this is one of them – Estus Flask Commented Nov 16, 2024 at 22:52PassedValidation.value = true
> – volume one Commented Nov 17, 2024 at 11:14await props.onPublishing()
instead ofcontext.emit('Publishing')
,:onPublishing="validateRecord"
instead of@Publishing="validateRecord"
. This presumes that validateRecord can be asynchronous as its signature suggests, e.g. have server-side validation. Had the same case with forms, a callback instead of an event solved this – Estus Flask Commented Nov 17, 2024 at 12:57