skip to Main Content

I have a simple registration endpoint that I want to allow for a user to register in my vue app, I also want to display appropriate errors from my backend to the vue client.

The error JSON structure looks like this:

{
    "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 am able to log out the error response like this

Error response

I try logging out my errors in my Form component but now I am just getting an empty object (I am using reactive to handle this error object in my composable)

Empty object response from composable

2

Answers


  1. It seems like you are returning an array, not an object.

    So to get access, you would do errors[0].Password.

    Did you intend to use an object or an array (might be useful if you have mutliple errors)?

    If the array is intended and you need to check all errors for the Password property, you would do something like this:

    errors.find(err => !!err.Password).Password
    
    Login or Signup to reply.
  2. Reflection on your code

    Multiple errors are present in it, making it difficult for me to provide a concise answer that suits your needs. Instead, I quickly put together a code snippet that works based on your principles. Going forward, I will try to highlight what to watch out for and provide some good advice for the future.

    – Use async function() to can waiting response of Promise

    bad BAD (if you want to use the result immediately, currently console.log justifies using async)
    // YOUR CODE
    const handleSubmit = () => {
      register(request); // call PROMISE () 
      // empty object
      console.log(errors)
    };
    

    This doesn’t provide an immediate result; it requires some time to run on a separate thread. As a result, the JavaScript script moves forward almost immediately. Typically, if you want to use the result immediately, this will result in an error because the response hasn’t arrived yet.

    So when you try to access the new result of errors, you see that it’s empty, even though after the console.log, after 1-2 seconds, it won’t be empty anymore because register() has finished executing.

    ok OK
    // SUCCESSFULLY USING
    const handleSubmit = async () => {
      await register(request); // call PROMISE () AND WAIT response by await
      // empty object
      console.log(errors)
    };
    

    await – Wait process end – MDN Docs
    async function – What need to await using – MDN Docs

    – How to use ref() on VueJS?

    1.

    Store the values held by ref(), reactive(), computed(), etc. in variables that cannot be modified. Always use const when declaring these variables.

    More information – StackOverflow Answer

    bad BAD
    // YOUR CODE
    let errors = ref({})
    
    ok OK
    const errors = ref({})
    
    2.

    You use the .value suffix in one instance and not in another. Well, the situation is that the result of ref() variables is always stored in .value. You can manipulate it accordingly.

    bad BAD
    // YOUR CODE
    let errors = ref({})
    
    // and...
    errors = {...}
    
    ok OK
    const errors = ref({})
    
    // and...
    errors.value = {...}
    

    How to use ref()? – VueJS Docs


    Sample code with the outlined logic

    I commented the lines for better code comprehension. I hope it’s 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/[email protected]/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>

    I had to complement your code with a few demo functions and implemented the vue.js CDN version to make the sample code workable. Of course, you only need to review the relevant parts in your own code to see what matches or differs. For example, I assume you didn’t create the AuthServiceClass yourself but rather a third party, so it should work correctly, but you will definitely find differences in your own Vue file.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search