iT邦幫忙

2023 iThome 鐵人賽

DAY 19
0
SideProject30

30 天用 Rust 打造 QR Code 製造機系列 第 19

Day 19 - 驗證輸入的文字是否正確

  • 分享至 

  • xImage
  •  

現在當選擇類別後,不管輸入什麼文字,結果都可以送出表單,並且產生錯誤的 QR code,這樣並不是我們想要的結果,所以這篇文章將會示範如何設定正規表達式來驗證輸入的文字。

Pattern

在 React Hook Form 中,可以使用 pattern 來設定正規表達式,所以我們可以在 FormInputs 中加入 pattern 來設定正規表達式:

const TextInput: FC<TextInputProps> = ({ register, pattern, errors }) => (
  <div className='mb-4'>
    <label className='block font-bold mb-2'>輸入文字</label>
    <input
      type='text'
      className='w-full p-2 border rounded focus:outline-none focus:ring focus:border-blue-300'
      {...register('text', {
        required: true,
        pattern:
          /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/
      })}
    />
  </div>
)

這裡使用了 pattern: /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/ 來驗證輸入的文字是否為 URL,如果不是的話,就會顯示錯誤訊息。

所以現在當輸入錯誤的 URL 時,就不會送出表單。

不過這裡有一個問題,就是如果選擇其他的類別時,就沒有辦法正確驗證相對應的格式,例如選擇電話,輸入正確的電話格式,也沒辦法送出表單,所以我們需要動態設定 pattern

動態設定 pattern

page.tsx 先從 useForm 中取出 watch,並且監聽 qrType 的變化。然後設定一個 pattern 的物件,裡面包含了各個類別的正規表達式:

const qrType = watch('qrType', 'URL') // 記得預設為 URL

const validationPatterns = {
  URL: /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/,
  電話: /^(\+?\d{1,3}[-.\s]?)?\d{10}$/,
  地址: /.+/,
  Email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
}

const pattern = validationPatterns[qrType as keyof typeof validationPatterns]

然後使用 keyof typeof 來取得 validationPatterns 的 key 的型別,並且使用 qrType as keyof typeof validationPatterns 來設定 pattern 的型別。

接下來就可以在 TextInput 傳入 pattern

<TextInput register={register} pattern={pattern} />

繼續設定 TextInput

interface TextInputProps {
  register: UseFormRegister<FormInputs>
  pattern: RegExp
}

const TextInput: FC<TextInputProps> = ({ register, pattern }) => (
  <div className='mb-4'>
    <label className='block font-bold mb-2'>輸入文字</label>
    <input
      type='text'
      className='w-full p-2 border rounded focus:outline-none focus:ring focus:border-blue-300'
      {...register('text', { required: true, pattern })}
    />
  </div>
)

這樣子就可以動態地按照選取的類別,驗證輸入的文字是否符合設定的格式。

顯示錯誤訊息

現在雖然可以驗證輸入的文字,但是並沒有顯示錯誤訊息,所以我們需要 React Hook Form 提供的 errors 來顯示錯誤訊息。

一樣先從 page.tsx 取出 errors

const {
  register,
  handleSubmit,
  setValue,
  watch,
  formState: { errors }
} = useForm<FormInputs>()

並宣告一個變數,來判斷是否有錯誤,等一下會用到:

const hasErrors = Object.keys(errors).length > 0

然後在 TextInput 傳入 errors,還有送出表單按鈕,以及新增一些提示訊息:

<main className='flex min-h-screen flex-col items-center justify-between p-24'>
  <div className='container mx-auto p-4'>
    <h1 className='text-3xl mb-4'>QR Code 製造器</h1>
    <form onSubmit={handleSubmit(onSubmit)} className='flex flex-col gap-y-5'>
      <SelectType register={register} />
      <TextInput register={register} pattern={pattern} errors={errors} />
      <div className='flex items-center justify-evenly'>
        <SizeSlider register={register} setValue={setValue} />
        <ColorPicker
          register={register}
          label='選擇顏色'
          name='qrColor'
          setValue={setValue}
        />
        <ColorPicker
          register={register}
          label='選擇背景顏色'
          name='qrBgColor'
          setValue={setValue}
        />
      </div>
      <div className='flex justify-center mt-5'>
        <button
          type='submit'
          className={`${
            hasErrors ? 'bg-gray-400' : 'bg-green-500'
          } hover:bg-green-100 text-white hover:text-slate-700 font-bold py-2 px-4 rounded text-center`}
        >
          產生 QR Code
        </button>
      </div>
    </form>
    {imgSrc && Object.keys(errors).length < 1 ? (
      <div className='flex justify-center mt-10'>
        <Image src={imgSrc} width={500} height={500} alt='QR Code Image' />
      </div>
    ) : (
      <p>{hasErrors ? '請輸入正確的資訊' : '請點擊「產生 QR Code」按鈕'}</p>
    )}
  </div>
</main>

TextInput 這部分也要設定 errors 的型別,以及修改一下錯誤的樣式:

import { FC } from 'react'
import { UseFormRegister, FieldErrors } from 'react-hook-form'
import { FormInputs } from '../page'

interface TextInputProps {
  register: UseFormRegister<FormInputs>
  pattern: RegExp
  errors: FieldErrors<FormInputs>
}

const TextInput: FC<TextInputProps> = ({ register, pattern, errors }) => (
  <div className='mb-4'>
    <label className='block font-bold mb-2'>輸入文字</label>
    <input
      type='text'
      className={`w-full p-2 border rounded focus:outline-none focus:ring ${
        errors?.text
          ? 'focus:border-red-500 border-red-500 ring-red-500'
          : 'focus:border-blue-300'
      }`}
      {...register('text', { required: true, pattern })}
    />
  </div>
)

到這裡就完成了,現在可以正確地驗證輸入的文字,並且顯示錯誤訊息。

demo

修正小問題

雖然現在已經可以做到按照選擇類別去規範輸入的文字,輸入正確才能顯示 QR code,不過有一個問題是,當得到正確的 QR code 後,再次選擇不同類別,並輸入不符合的規範後,確實能夠擋下來,但是當輸入正確的格式後,會立刻顯示上一個 QR code,這時候都還沒按送出表單的按鈕,那麼有什麼方式可以解決?

其實蠻簡單的,只要在選擇時,把 imgSrc 清空就可以了:

import { useState, useEffect } from 'react'

// 省略其他程式碼

const [imgSrc, setImgSrc] = useState<string | null>(null)

const qrType = watch('qrType', 'URL')

useEffect(() => {
  setImgSrc(null)
}, [qrType])

總結

現在這個 QR code 製造器已經可以正確地驗證輸入的文字,並且顯示錯誤訊息,也可以正確地顯示 QR code,並且可以動態地切換類別,前端的部分暫時完成了!明天將會接續實作後端的部分,明天見👋!


上一篇
Day 18 - React Hook Form 做好你的表單驗證
下一篇
Day 20 - 使用 Zustand 來做狀態管理
系列文
30 天用 Rust 打造 QR Code 製造機30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言