これは「react-jsonschema-formのカレンダー | Advent Calendar 2023 - Qiita」の9日目の記事です。
7日目の記事でreact-jsonschema-form (RJSF) の Widget を、8日目の記事で Field をカスタマイズしてきました。
そこで今回は、残る Template のカスタマイズをためしてみます。
Custom Templates | react-jsonschema-form
RJSFが用意しているTemplateの一覧は、公式ドキュメントの以下のページに記載されています。
Custom Templates | react-jsonschema-form
それを見ると、 BaseInputTemplate
のようにWidgetのベースになっているものから、ArrayやError、さらにはButtonまで色々なものが用意されていることが分かります。
すべてをためすと分量が多くなってしまうことから、今回は
BaseImputTemplate
のカスタマイズObjectFieldTemplate
とFieldTemplate
のカスタマイズ
を見ていきます。
目次
環境
- react-jsonschema-form 5.15.0
- React 18.2.0
- React Router 6.20.1
BaseInputTemplateのカスタマイズについて
BaseInputTemplate
のカスタマイズ方法については以下にまとまっています。
BaseInputTemplate | Custom Templates | react-jsonschema-form
今回は、 onChange
を差し替え、入力値に変化があるたびにコンソールへ出力するようにカスタマイズします。
なお、 onChange
だけを差し替える場合、公式ドキュメントの MyBaseInputTemplate
の例のように、BaseInputTemplateに渡す props
を修正するだけで良さそうです。
まずはカスタムTemplateを用意します。
ここでは、
onMyChange
関数を用意し、BaseInputTemplate
のonChange
propsに渡す- MUIのテンプレートを使うよう
const { BaseInputTemplate } = Templates
とする
を行っています。
import {Templates} from "@rjsf/mui"; import {BaseInputTemplateProps} from "@rjsf/utils"; const { BaseInputTemplate } = Templates export const ConsoleLogInputMuiTemplate = (props: BaseInputTemplateProps) => { const { onChange, options } = props const onMyChange = (v) => { console.log(v) onChange(v === '' ? options.emptyValue || '' : v) } return <BaseInputTemplate {...props} onChange={onMyChange} /> }
続いて、 Form
のprops templates
にて、キー BaseInputTemplate
にカスタマイズしたテンプレートを渡します。
import {RJSFSchema} from "@rjsf/utils"; import Form from "@rjsf/mui"; import validator from "@rjsf/validator-ajv8"; import {ConsoleLogInputMuiTemplate} from "./templates/ConsoleLogInputMuiTemplate"; export const ConsoleLogInputMuiTemplateForm = () => { const schema: RJSFSchema = { title: "Console Log Input Mui Template Form", type: "object", required: [], properties: { stringInput: { type: "string", }, numberInput: { type: "number" } } } // onSubmitの引数の説明は以下 // https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/form-props#onsubmit const onSubmit = ({formData}, _event) => { console.log(formData) } return ( <div style={{width: '400px'}}> <Form schema={schema} templates={{BaseInputTemplate: ConsoleLogInputMuiTemplate}} validator={validator} onSubmit={onSubmit} /> </div> ) }
準備ができたので、実際に動かしてみます。
なお、 BaseInputTemplate
を差し替えたため、そのTemplateをベースにしたWidgetを使っている
- stringInput
- numberInput
の両方とも、何かを入力したらその値がコンソール出力されることを想定しています。
動かしてみると、想定通りの挙動となりました。
ObjectFieldTemplateとFieldTemplateについて
公式ドキュメントを見ていたところ、Field系のテンプレートとして
- ObjectFieldTemplate
- FieldTemplate
の2つを見つけました。
ただ、読んだだけではそれらの違いがあまり分からなかったため、実際にためしてみます。
ObjectFieldTemplateを単独で試す
まずは ObjectFieldTemplate
だけを使ってみます。
今回は
- propsの
required
がtrue
の場合は、tilte
を赤字で表示する - propsの
description
を表示しない
とカスタマイズしてみます。
最初にTemplateを用意します。
import {ObjectFieldTemplateProps} from "@rjsf/utils"; export const MyObjectFieldTemplate = ({ title, required, properties }: ObjectFieldTemplateProps) => { return ( <div style={{marginTop: 50, marginBottom: 30}}> {required && <span style={{color: "red"}}>{title}</span> } {!required && title} {properties.map((element) => ( <div>{element.content}</div> ))} <hr /> </div> ) }
続いてJSON SchemaとFormを含むコンポーネントを用意します。
今回は
type="object"
をネストする- ネストしたobjectのpropertiesのうち、
stringInput
だけrequired
にする
とし、どのように変化が起こるかを見てます。
なお、他の部分は、BaseInputeTemplate
のカスタマイズのときと同じような実装です。
import {RJSFSchema} from "@rjsf/utils"; import Form from "@rjsf/core"; import validator from "@rjsf/validator-ajv8"; import {MyObjectFieldTemplate} from "./templates/MyObjectFieldTemplate"; import {MyFieldTemplate} from "./templates/MyFieldTemplate"; export const MyObjectFieldTemplateForm = () => { const schema: RJSFSchema = { title: "root object title", description: "root object description", type: "object", required: ["nestObject"], properties: { nestObject: { type: "object", description: "nest object description", title: "nest object title", required: ["stringInput"], properties: { stringInput: { type: "string", title: "nest string title", description: "nest string description" }, numberInput: { type: "number", title: "nest number title", description: "nest number description" } } }, rootInput: { type: "string", description: "root input description", title: "root input title" } } } const templates = { ObjectFieldTemplate: MyObjectFieldTemplate } // onSubmitの引数の説明は以下 // https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/form-props#onsubmit const onSubmit = ({formData}, _event) => { console.log(formData) } return ( <div style={{width: '400px'}}> <Form schema={schema} templates={templates} validator={validator} onSubmit={onSubmit} /> </div> ) }
動かしてみたところ
- 最上位の
type="object"
の title は黒文字な一方、ネストしたobjectのtitleは赤文字 - 最上位の
type="object"
と同じ階層にあるdescriptionは消えているものの、propertiesの下で定義したdescriptionは残っている
という、やや気になる状態でした。
公式ドキュメントを読んだところ
Note: Array and object field templates are always rendered inside the FieldTemplate. To fully customize an object field template, you may need to specify both
ui:FieldTemplate
andui:ObjectFieldTemplate
.
とあったことから、 FieldTemplate
と組み合わせてみることにします。
ObjectFieldTemplateとFieldTemplateを組み合わせて使う
ここでは、先ほど用意した ObjectFieldTemplate
はそのまま流用し、 FieldTemplate
を新しく追加してためしてみます。
なお、ObjectFieldTemplate由来とFieldTemplate由来のどちらであるかをわかりやすくするため、文字の色を変えたり、区切り線を入れてみたりしています。
import {FieldTemplateProps} from "@rjsf/utils"; export const MyFieldTemplate = ({id, label, required, description, children}: FieldTemplateProps) => { return ( <> <p>{"===="}{label}{"====>"}</p> <label htmlFor={id}> {required && <span style={{color: "blue"}}>{label}</span> } {!required && <span style={{color: "purple"}}>{label}</span> } </label> {<span style={{color: "green"} }>{description}</span>} {children} <p>{"<===="}{label}{"===="}</p> </> ) }
続いてJSON SchemaとFormを含むコンポーネントを用意します。
先ほどのものとはtemplate変数の値だけ変えて違いを見てみます。
const templates = { ObjectFieldTemplate: MyObjectFieldTemplate, FieldTemplate: MyFieldTemplate }
動かしてみたところ、以下の結果となりました。
これより、
- 最上位の
type="object"
の title は黒文字な一方、ネストしたobjectのtitleは赤文字- 最上位のobjectの
required
は、すべてfalse
とみなされていた
- 最上位のobjectの
- 最上位の
type="object"
と同じ階層にあるdescriptionは消えているものの、propertiesの下で定義したdescriptionは残っている- propertiesの
description
は、FieldTemplate由来であり、FieldTemplateで制御してあげる必要があった
- propertiesの
ということが分かりました。
ソースコード
Githubにあげました。
https://github.com/thinkAmi-sandbox/rjsf_advent_calendar_2023
今回のプルリクはこちら。
https://github.com/thinkAmi-sandbox/rjsf_advent_calendar_2023/pull/8