VueJS handles errors in api calls on form components
P粉964682904
P粉964682904 2023-09-06 22:01:08
0
2
577

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粉964682904
P粉964682904

reply all(2)
P粉576184933

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粉180844619

Reflect on your code

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.

- Use async function() to wait for the response of Promise

Bad (if you want to use the results immediately, currently 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.

OK
// 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

- How to use ref() on VueJS?

1.

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

not good
// YOUR CODE
let errors = ref({})
OK
const errors = ref({})
2.

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.

not good
// YOUR CODE
let errors = ref({})

// and...
errors = {...}
OK
const errors = ref({})

// and...
errors.value = {...}

How to use ref()? - VueJS Documentation



Sample code with outlined logic

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>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template