如何仅在vue中使用Zod显示html元素中的zod错误

问题描述 投票:0回答:1

这是我的vue comp,我正在尝试验证表单,Zod已经生成了错误,只需要在对应元素中显示错误,这里验证正确完成,错误进入'error'数组,该数组在'中定义data',所以,需要从错误数组中获取正确的错误并将其显示在'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>
forms vue.js validation zod
1个回答
0
投票

我建议使用

schema.parse(formDataObject);
,而不是使用
schema.safeParse(formDataObject); 

safeParse
方法不会抛出错误,而是返回一个带有
success
字段的对象。

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; 


© www.soinside.com 2019 - 2024. All rights reserved.