0

this is my vue comp, which i am trying to validate form, the errors already generated by Zod , Only need to display the error in corespondent element , here the validation done correctly , the errors goes into 'error' array which define in 'data', so, need to get the correct error from errors array and display it in 'span'

        <template>
       <div id="basic" class="w-full details-model  relative hidden rounded-md mx-auto  mt-2 py-3 px-3 bg-white border border-gray-300  opend-src" style="animation-name: animat-top;animation-duration: 0.4s;box-shadow: rgb(82 63 104 / 12%) 0px 0px 10px 0px;">
          <span class="absolute inline-block w-4 h-4 bg-white top-[-.5rem] sm:left-20 sm:mr-4 sm:rotate-45"></span>
          <form id="rank-form" class="mainpower-form flex flex-wrap gap-y-3 gap-x-4 pt-4 justify-between" enctype="multipart/form-data" >
             <div class="flex-100 addrs-ctrl relative rounded-md mb-6">
                <button  class="inline-flex openDisBtn group relative w-full border !border-gray-300 items-center px-3 py-2.7 text-sm font-medium text-gray-400 rounded-md focus:!border-blue-500 focus:outline-none peer-focus:!text-blue-500 focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800" type="button">
                   <span v-if="selectedRank" class="font-normal" >{{selectedRank}} </span>
                   <span v-else class="text-sm text-gray-400 font-normal">Selecet Rank</span>
                   <svg class="w-2.5 h-2.5 ml-auto" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
                      <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
                   </svg>
                   <label  class="px-2 bg-white ml-2 z-20 font-medium absolute text-md text-gray-600  -translate-y-6 scale-76 top-[0.74rem]  origin-[0] left-0 peer-focus:text-blue-500">Ranks</label>
                </button>
                <div class="z-50 dtls-menu absolute top-12 hidden bg-white rounded-md border !border-gray-300 shadow w-full mt-2 dark:bg-gray-700" style="box-shadow: rgb(0 0 0 / 3%) 0px 12.5px 10px, rgb(0 0 0 / 6%) 0px 100px 80px">
                   <div v-if="ranks.length" class="h-48 px-2 pb-3 pt-1 overflow-y-auto text-sm text-gray-700 dark:text-gray-200" >
                      <div v-for="rank in ranks" :key="rank.id" class="flex py-2.5 px-2 border-b items-center border-b-gray-300  bg-gry-100 hover:bg-gray-200 dark:hover:bg-gray-600">
                         <input :id="rank.id" type="radio" name="rank_name" v-model="selectedRank"  :value="rank.rank_name" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
                         <label :for="rank.id" class="w-full ml-2 mb-0 text-sm font-medium text-gray-900 capitalize rounded dark:text-gray-300">{{rank.rank_name}}</label>
                      </div>
                   </div>
                   <div v-else>
                      Loading disciplines...
                   </div>
                </div>
             </div>
             <div class="flex-48">
                <div class="relative z-0  mb-6 border border-gray-200 group rounded-md focus-within:!border-blue-400 ">
                   <input type="text" name="rank"  id="rank " v-model="rank" class="placeholder:font-normal placeholder:text-sm block py-2.7 font-normal pl-3 w-full text-md text-gray-900 bg-transparent appearance-none dark:text-white dark:border-gray-600 dark:focus:border-blue-500 focus:outline-none focus:ring-0 focus:!pl-4  focus:border-blue-600 peer" placeholder="rnking in selecetd year" required />
                   <label for="rank" class="px-2 bg-white ml-2 font-medium absolute text-sm text-gray-700  duration-300 transform -translate-y-6 scale-76 top-[0.74rem] -z-10 origin-[0] left-0 peer-focus:text-blue-500">Ranking</label>
                </div>
             </div>
             <div class="flex-48">
                <div class="relative z-0 mb-6 border border-gray-200 group rounded-md focus-within:!border-blue-400 ">
                   <input type="text" name="research" id="research" class="block py-2.7 font-normal pl-3 w-full text-md text-gray-900 bg-transparent appearance-none dark:text-white dark:border-gray-600 dark:focus:border-blue-500 focus:outline-none focus:ring-0 focus:!pl-4  focus:border-blue-600 peer" placeholder="university research number .... " required />
                   <label for="research" class="px-2 bg-white ml-2 font-medium absolute text-sm text-gray-700  duration-300 transform -translate-y-6 scale-76 top-[0.74rem] -z-10 origin-[0] left-0 peer-focus:text-blue-500">Research Output</label>
                </div>
             </div>
             <div class="flex-100 addrs-ctrl relative rounded-md mb-6">
                <button  class="inline-flex openDisBtn group relative w-full border !border-gray-300 items-center px-3 py-2.7 text-sm font-medium text-gray-400 rounded-md focus:!border-blue-500 focus:outline-none peer-focus:!text-blue-500 focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800" type="button">
                   <span v-if="selectedYear" class="font-normal" >{{selectedYear}} </span>
                   <span v-else class="text-sm text-gray-400 font-normal">Selecet Year</span>
                   <svg class="w-2.5 h-2.5 ml-auto" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
                      <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
                   </svg>
                   <label  class="px-2 bg-white ml-2 z-20 font-medium absolute text-sm text-gray-600  -translate-y-6 scale-76 top-[0.74rem]  origin-[0] left-0 peer-focus:text-blue-500">Disciplines</label>
                </button>
                <div class="z-10 dtls-menu absolute top-12 hidden bg-white rounded-md border !border-gray-300 shadow w-full mt-2 dark:bg-gray-700" style="box-shadow: rgb(0 0 0 / 3%) 0px 12.5px 10px, rgb(0 0 0 / 6%) 0px 100px 80px">
                   <div v-if="rankingYears.length" class="h-48 px-2 pb-3 pt-1 overflow-y-auto text-sm text-gray-700 dark:text-gray-200" >
                      <div v-for="year in rankingYears" :key="year" class="flex py-2.5 px-2 border-b items-center border-b-gray-300  bg-gry-100 hover:bg-gray-200 dark:hover:bg-gray-600">
                         <input :id="year" type="radio" name="year" v-model="selectedYear"  :value="year" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
                         <label :for="year" class="w-full ml-2 mb-0 text-sm font-medium text-gray-900 capitalize rounded dark:text-gray-300">{{year}}</label>
                      </div>
                   </div>
                   <div v-else>
                      Loading disciplines...
                   </div>
                </div>
             </div>
             <div class="form-group flex-100">
                <button  type="button" id="basicId" class="w-full mt-4 text-white bg-gradient-to-r from-blue-500 via-blue-600 to-blue-700 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-blue-300 dark:focus:ring-blue-800 shadow-lg shadow-blue-500/50 dark:shadow-lg dark:shadow-blue-800/80 font-medium rounded-lg text-sm px-5 py-2.5 text-center" @click=" (event) => addRank(event)">Save Basic Info</button >
             </div>
          </form>
       </div>
    </template>
    <script>
       import { object, string, array } from "zod";
       const schema = object({
         rank_name: string().min(1).max(255),
         rank: string().min(1).max(255),
         research: string().min(1).max(255),
         year: string().min(1).max(4),
       });
       export default {
           name:'RanksInfo',
            props:{
               ranks :{
                   type: Array,
                   required: true
               },
               university_id:String
            },
             mounted() {
         },
        data(){
            return{
               
               selectedYear : '' ,
               selectedLevel : [],
               scholarship :{},
               rankingYears : ['2020','2021'],
               selectedRank : 'selecte rank',
               id:'',
               errors: null,
               rank: '',
             research: '',
            }
        },
       
         methods:{  
               addRank(event){
                   alert("efrg")
                   const formElem = document.getElementById("rank-form");
                   const formData = new FormData(formElem);
                   const csrfToken = document.head.querySelector('meta[name="csrf-token"]').content;
                   formData.append('uni_id', this.university_id);
                   formData.append('_token', csrfToken);
       
                   const formDataObject = Object.fromEntries(formData.entries());
       
                   try{
                        schema.parse(formDataObject); 
                        if (formData.get('rank_name') == '') {
                        if(formData.get('rank_name') == '' ){ 
                       }
                       else{
                           event.preventDefault();
                           fetch("/admin/university-rank/save", {
                               method: "POST",
                               body: formData,
                           })
                               .then(response => response.json())
                               .then(data => {
                               if(data.result){
                               }
                               if(data.error){
                                   console.log("error--->" + data.error);
                               }
                               
                               })
                               .catch(error => {
                              
                               });
                       }
                        }
                   }catch (error) {
                       this.errors = error.errors;
                   }
                  
               },
        
         },
       };
    </script>

1 Answer 1

0

Instead of using schema.parse(formDataObject);, I would suggest using schema.safeParse(formDataObject); .

The safeParse method doesnt throw an error, and instead returns an object with a success field.

const validatedSchema = schema.safeParse(formDataObject);

if (!validatedSchema.success) {
 // the error object should give you access to specific field errors if you dig into it. Its only a matter of assigning this to a reactive and using it in your template now
 const error = validatedSchema.error.format();
 return;
}

// correct data
const data = validatedSchema.data; 


The error.format(); format turns the erorr object into a JSON that is just easily usable.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.