I have a simple registration endpoint that I want to allow users to register in my vue application, I also want to display appropriate errors to the vue client from my backend.
The error JSON structure is as follows:
{ "errors": { "Password": [ "Password is required", "Minimum length of 8 characters is required" ], "UserName": [ "Username is required", "Minimum length of 6 characters is required" ], "EmailAddress": [ "Email Address is required", "Invalid Email Address" ], "ConfirmPassword": [ "Confirm Password is required" ] } }
I have a composable with a register function like this:
export default function useAuth() { let errors = ref({}) const register = (request) => { errors = {} AuthService.register(request) .then(res => { console.log("res: "+ res) }) .catch(err => { const errList = err.response.data.errors; errors = errList // Here i'm getting a reponse console.log(errors) }) } return { register, errors } }
I also have a form component which is just a simple form with v-models added:
<script> // Imports.. export default { components: {}, setup() { const { register, errors} = useAuth(); const request = { userName: "", emailAddress: "", password: "", confirmPassword: "", }; const handleSubmit = () => { register(request); // empty object console.log(errors) }; return { errors, request, handleSubmit }; }, }; </script>
In my composable I can log out the error response like this
Error response
I tried unregistering the error in the form component but now I just get an empty object (I'm using reactive to handle this error object in the composable)
Empty object response from composable items
P粉5761849332023-09-07 12:38:14
Looks like you are returning an array, not an object.
So to gain access you need to execute errors[0].Password
.
Are you going to use an object or an array (might be useful if you have multiple errors)?
If the array was expected and you needed to check the Password
property for all errors, you would do something like this:
errors.find(err => !!err.Password).Password
P粉1808446192023-09-07 09:50:32
There are multiple errors in it, making it difficult for me to provide a concise answer that fits your needs. Instead, I quickly put together a code snippet that works according to your principles. Going forward, I'll try to highlight things to watch out for and provide some good advice for the future.
async function()
to wait for the response of Promise
console.log
proves using async
) // YOUR CODE const handleSubmit = () => { register(request); // call PROMISE () // empty object console.log(errors) };
This does not provide results immediately; it takes some time to run on a separate thread. As a result, the JavaScript script moves forward almost immediately. Normally this will result in an error if you want to use the result immediately because the response hasn't arrived yet.
So when you try to access the new result for errors
you will see it is empty even after console.log
after 1-2 seconds it It will no longer be empty because register()
has been executed.
// SUCCESSFULLY USING const handleSubmit = async () => { await register(request); // call PROMISE () AND WAIT response by await // empty object console.log(errors) };
Wait
- Wait for process to end - MDN Documentation
Asynchronous functions
- What is neededawait
Using - MDN Documentation
ref()
on VueJS? Store the saved values of ref()
, reactive()
, compulated()
, etc. in variables that cannot be modified. Always use const when declaring these variables.
More Information - StackOverflow Answers
// YOUR CODE let errors = ref({})
const errors = ref({})
You use the .value
suffix in one instance but not in another. Well, what happens is that the result of a ref()
variable is always stored in .value
. You can manipulate it accordingly.
// YOUR CODE let errors = ref({}) // and... errors = {...}
const errors = ref({}) // and... errors.value = {...}
How to use ref()
? - VueJS Documentation
I have commented these lines to understand the code better. I hope this is understandable.
/** ** Need more function for example ** !!! The vue is below !!! */ // CDN Vue Import const { createApp, ref, computed } = Vue // isValideEmail() (JUST EXAMPLE FOR SNIPPET) // Helper function to validate email address function isValidEmail(email) { const emailRegex = /^\S+@\S+\.\S+$/; return emailRegex.test(email); } // AuthService (JUST EXAMPLE FOR SNIPPET) class AuthServiceClass { errors constructor() { this.errors = {}; } register(inputs) { // Reset Errors this.errors = {}; console.log(inputs) // Check the UserName field if (!inputs.userName) { this.errors.UserName = (this.errors?.UserName ?? []).concat("Username is required"); } if (!inputs.userName || inputs.userName.length < 6) { this.errors.UserName = (this.errors?.UserName ?? []).concat("Minimum length of 6 characters is required"); } // Check the EmailAddress field if (!inputs.emailAddress) { this.errors.EmailAddress = (this.errors?.EmailAddress ?? []).concat("Email Address is required"); } if (!inputs.emailAddress || !isValidEmail(inputs.emailAddress)) { this.errors.EmailAddress = (this.errors?.EmailAddress ?? []).concat("Invalid Email Address"); } // Check the Password field if (!inputs.password) { this.errors.Password = (this.errors?.Password ?? []).concat("Password is required"); } if (!inputs.password || inputs.password.length < 8) { this.errors.Password = (this.errors?.Password ?? []).concat("Minimum length of 8 characters is required"); } // Check the ConfirmPassword field if (!inputs.confirmPassword) { this.errors.ConfirmPassword = (this.errors?.ConfirmPassword ?? []).concat("Confirm Password is required"); } // Return with Promise because its just a simulate your really AuthService.register return new Promise((resolve, reject) => { if (this.errors.length !== 0) { reject({ errors: this.errors }); } else { resolve({ success: true, errors: null }); } }); } } // Import AuthService (JUST EXAMPLE FOR SNIPPET) const AuthService = new AuthServiceClass() // Declare useAuth() (JUST EXAMPLE FOR SNIPPET) function useAuth() { const errors = ref({}) const register = async (request) => { await AuthService.register(request) .then(res => { console.log("AuthService Register Successfully Response", res) }) .catch(err => { console.log("AuthService Register Error Response", err) const newErrors = err.errors; errors.value = newErrors }) } return { register, errors } } /** ** !!!!!! ** Here's started vue code snippet */ // Component.vue const app = createApp({ setup() { // Get register() and errors Ref const { register, errors } = useAuth(); // Declare Ref Object for inputs const request = ref({ userName: "", emailAddress: "", password: "", confirmPassword: "", }); // Declare Submit Function (async for Promise check !!!) const handleSubmit = async () => { console.log('') // just log console.log('Detect New Handle') // just log // call register() function with our value of inputs // wait answer by "await" await register(request.value); console.log('HandleSubmit - Check Error List', errors.value) // just log }; // Just Extra Computed Value, not important // return true/false const hasError = computed(() => Object.keys(errors.value).length > 0) return { hasError, errors, request, handleSubmit } }, }).mount('#app')
.input { display: flex; flex-direction: column; gap: 2px; margin-bottom: 10px; max-width: 200px; } .error { color: red; }
<!-- Component.vue --> <script src="https://unpkg.com/vue@3.3.4/dist/vue.global.prod.js"></script> <div id="app"> <div> <!-- Display Errors --> <div v-if="hasError"> <!-- errors is an object, so you can foreach its keys --> <div v-for="inputName of Object.keys(errors)"> <span>{{ inputName }}:</span> <!-- name-array pairs can be directly printed as error messages --> <div v-for="errorMessage of errors[inputName]" class="error"> {{ errorMessage }} </div> </div> </div> <!-- Inputs --> <!-- request is an object, so you can foreach its keys --> <div class="input" v-for="inputName of Object.keys(request)"> <label>{{ inputName }}</label> <input v-model="request[inputName]" /> </div> <!-- Submit Button --> <button @click="handleSubmit">Submit</button> </div> </div>