iT邦幫忙

2024 iThome 鐵人賽

DAY 5
0

Sanity 是可以定義”必填”、”欄位限制條件”及”提示訊息”等諸如此類的。

export default defineType({
  // ...
  fields: [
    defineField({
      name: 'title', // 必填
      title: '標題',
      type: 'string',
    }),
    defineField({
      name: 'slug', // 必填
      title: '網址',
      type: 'string',
    }),
    defineField({
      name: 'subtitle',
      title: '副標題',
      type: 'string',
    }),
    defineField({
      name: 'heroImage',
      title: '封面圖片',
      type: 'image',
    }),
    defineField({
      name: 'content', // 必填
      title: '內容',
      type: 'text',
    }),
    defineField({
      name: 'publishedAt', // 預設今天
      title: '發布日期',
      type: 'date',
    }),
    defineField({
      name: 'tags',
      title: '標籤',
      type: 'array',
      of: [{type: 'string'}],
    }),
  ],
})

在我們的範例中, 標題, 網址 等等的欄位就會是必填欄位,而"封面圖片"則是可以加個限制檔案類型、圖片尺寸等等的屬性。

要設定欄位為必填只要設定欄位的 validation ,他是一個可呼叫的方法,並且提過設定方法回傳的參數就可以對欄位的屬性進行設定了。

不多說,直接先來把標題欄位設定為必填作為範例:

defineField({
  name: 'title',
  title: '標題',
  type: 'string',
  validation: (rule) => rule.required(), // 對 rule 進行設定欄位屬性
})

https://ithelp.ithome.com.tw/upload/images/20240919/20101989iBs7DR75mP.png

將 rule 設定為 required() 就可以將欄位設為必填。
不只可以設定為必填,甚至可以進一步設定長度規則、錯誤提示訊息等等的屬性。

defineField({
  name: 'title',
  title: '標題',
  type: 'string',
  validation: (rule) => rule
	  .required()
	  .min(5).max(25)
	  .error('標題長度需介於 5 到 25 字元間'),
})

https://ithelp.ithome.com.tw/upload/images/20240919/20101989CfwOydbthD.png

規則不只可以只有一條,可以有多項規則、分別設定每一項規則的錯誤等級。
像是網址欄位可以做類似這樣的設定:

defineField({
  name: 'slug',
  title: '網址',
  type: 'string',
  validation: (rule) => [
    rule.required(),
    rule.lowercase().error('網址必須是小寫字母'),
    rule.regex(/^[\p{L}\p{N}_-]+$/u).error('網址包含了不允許的字元'),
  ],
}),

但是網址欄位其實可以不用這麼麻煩的設定規則,網址欄位可以用 Sanity 現有的資料型別 slug
slug 可以指定要參照那欄位,並且用預設的格式產生合法的 url 欄位:

defineField({
  name: 'slug',
  title: '網址',
  type: 'slug',
  validation: (rule) => rule.required(),
  options: {
    source: 'title',
    maxLength: 80,
  },
}),

直接這樣定義參照 title 欄位,在 title 打完後直接點擊 slug 欄位旁邊的 Generate 就會自動產生 slug 的內容了。

https://ithelp.ithome.com.tw/upload/images/20240930/20101989Y1hPenAIxR.png

驗證圖片寬高比

要對 Sanity 的檔案或圖片等等的資源進行驗證是相對複雜的,因為不僅要用到 rule 的 custom() 方法,還需要取得圖片資源才可以進行細部驗證。

Sanity 預設在取得檔案或圖片時,不會直接取得資源的資料,而是取得資源的參照,像這樣:

{
  "_type": "image",
  "asset": {
    "_type": "reference",
    "_ref": "image-d1d57a17182e42c1bf0a46b61e0ed9893950fe24-770x480-jpg"
  }
}

要取得近一步的資料內容就必須使用 _ref 的資料再進行訪問,在這邊不要搞得那麼複雜,這邊直接使用社群開發的 @sanity/asset-utils 專案。

至於 @sanity/asset-utils 這個專案主要就是對於 Sanity 調用資源的流程做了封裝,讓我們可以簡單使用。細部就不在這邊講了,直接來使用吧。

先安裝起來:

npm install --save @sanity/asset-utils

再來引入專案內

import {getImageDimensions} from '@sanity/asset-utils'

// ...
	defineField({
	  name: 'heroImage',
	  title: '封面圖片',
	  type: 'image',
	  validation: (rule) =>
	    rule.custom((image) => {
	      const {aspectRatio} = getImageDimensions(image?.asset?._ref || '')
	      const isValid = Math.abs(aspectRatio - 1.604) <= 0.1
	      if (isValid) return true
	      return '建議尺寸為 16:10 (1920x1200)'
	    }),
	}),
// ...

getImageDimensions() 方法將圖片的 _ref 傳進去後,它就資動幫我們把寬、高、比例都回傳了。是不是很放便?

進行判斷後就可以回傳了,custom() 的回傳接受兩個,一個是 true,也就是驗證通過了,另一個接受的則是錯誤原因,可以像我寫的那樣傳入訊息。
( 記得不可以傳入 false 哦,它是不接受的 )

記得 custom() 驗證一樣可設定錯誤的等級,預設等級是 error,並且將 return 的錯誤訊息,
一樣可以在最後設定 warning()error(),設定錯誤的等級。

import {getImageDimensions, getExtension} from '@sanity/asset-utils'
// ...
	defineField({
	  name: 'heroImage',
	  title: '封面圖片',
	  type: 'image',
	  validation: (rule) => [
	    rule.required().error('封面圖片是必填欄位'),
	    rule
	      .custom((image) => {
	        try {
	          const {aspectRatio} = getImageDimensions(image?.asset?._ref || '')
	          const isValid = Math.abs(aspectRatio - 1.604) <= 0.1
	          if (isValid) return true
	          return '建議尺寸為 16:10 (1920x1200)'
	        } catch (error) {
	          return true
	        }
	      })
	      .warning(),
	    rule
	      .custom((image) => {
	        try {
	          const fileType = getExtension(image?.asset?._ref || '')
	          if (fileType !== 'jpg' && fileType !== 'png') {
	            return '圖片必須是 .jpg 或 .png 格式'
	          }
	          return true
	        } catch (error) {
	          return true
	        }
	      })
	      .error(),
	  ],
	}),
// ...

經過最後的設定後,刻意上傳一張比例不對的 .svg 圖檔後,就會看到這樣的錯誤訊息。
https://ithelp.ithome.com.tw/upload/images/20240919/20101989HYDmgszdng.png


上一篇
Day 4 - Sanity preview 設定
下一篇
Day 6 - Sanity initialValue ( 欄位預設值 )
系列文
用 Sanity 跟 Nextjs 重寫個人部落格30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言