react-jsonschema-formにて、ArrayのTemplateをカスタマイズしてみた

これは「react-jsonschema-formのカレンダー | Advent Calendar 2023 - Qiita」の18日目の記事です。

9日目の記事ではTemplateのカスタマイズとして

  • BaseImputTemplate
  • ObjectFieldTemplate
  • FieldTemplate

をためしました。

ただ、 react-jsonschema-form (RJSF)には他にもTemplateが用意されています。

そこで今回は、Array系のTemplateをカスタマイズしてみます。

 
目次

 

環境

  • react-jsonschema-form 5.15.0
  • React 18.2.0
  • React Router 6.20.1

 

Array系のデフォルトの表示

Templateをカスタマイズする前に、まずはArray系のデフォルトの表示を確認してみます。

以下では、 stringInputintegerInput のオブジェクトが要素であるArrayを定義しています。

import {RJSFSchema, UiSchema} from "@rjsf/utils";
import Form from "@rjsf/mui";  // MUI version
import validator from "@rjsf/validator-ajv8";

export const DefaultArrayFieldsForm = () => {
  const schema: RJSFSchema = {
    title: "Default Array Fields",
    type: "object",
    description: "root description",
    properties: {
      arrayInput: {
        type: "array",
        title: "array input title",
        description: "array input description",
        items: {
          type: "object",
          title: "item title",
          description: "item description",
          properties: {
            stringInput: {
              type: "string",
              title: "string title",
              description: "string description"
            },
            integerInput: {
              type: "integer",
              title: "integer title",
              description: "integer description"
            }
          }
        }
      }
    }
  }

  // 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}
        validator={validator}
        onSubmit={onSubmit}
      />
    </div>
  )
}

 
表示直後の状態は以下です。

 
続いて + ボタンをクリックすることで、Arrayの要素が入力可能な表示となります。

また、- ボタンで要素の削除が、 + ボタンで要素の追加が、それぞれできるようになっています。

 

Array系のTemplateをカスタマイズ

続いてArray系のTemplateをカスタマイズしていきます。

なお、JSON Schemaは変更せず、Templateだけ差し替えます。

 

ArrayFieldTitleTemplate

まずは ArrayFieldTitleTemplate をカスタマイズします。
https://rjsf-team.github.io/react-jsonschema-form/docs/advanced-customization/custom-templates#arrayfieldtitletemplate

 
ここでは titlered にしたTemplateを用意します。

import {ArrayFieldTitleProps, titleId} from "@rjsf/utils";

export const MyArrayFieldTitleTemplate = ({ title, idSchema }: ArrayFieldTitleProps) => {
  const id = titleId(idSchema)

  return (
    <h1 id={id} style={{color: "red"}}>{title}</h1>
  )
}

 
Formでtemplatesを差し替えます。

<Form
  schema={schema}
  validator={validator}
  onSubmit={onSubmit}
  templates={{
    ArrayFieldTitleTemplate: MyArrayFieldTitleTemplate
  }}
/>

 
表示すると、type="array"title の文字色が redに変わりました。

 

ArrayFieldDescriptionTemplate

続いて、 ArrayFieldDescriptionTemplate をカスタマイズします。
https://rjsf-team.github.io/react-jsonschema-form/docs/advanced-customization/custom-templates#arrayfielddescriptiontemplate

ここでは descriptionblue にしたTemplateを用意します。

import {ArrayFieldDescriptionProps, descriptionId} from "@rjsf/utils";

export const MyArrayFieldDescriptionTemplate = ({ description, idSchema }: ArrayFieldDescriptionProps) => {
  const id = descriptionId(idSchema)

  return (
    <p id={id} style={{color: "blue"}}>{description}</p>
  )
}

 
FormでTemplateを差し替えます。

      <Form
        schema={schema}
        validator={validator}
        onSubmit={onSubmit}
        templates={{
          ArrayFieldDescriptionTemplate: MyArrayFieldDescriptionTemplate
        }}
      />

 
表示すると、type="array"description の文字色が blueに差し替わりました。

 

ArrayFieldItemTemplate

カスタマイズ

次は ArrayFieldItemTemplate をカスタマイズしてみます。

今回は darkkhaki 色の文字列 Field Item を追加します。

import {ArrayFieldTemplateItemType} from "@rjsf/utils";

export const MyArrayFieldItemTemplate = (props: ArrayFieldTemplateItemType) => {
  return (
    <>
      <div style={{color: "darkkhaki"}}>Field Item</div>
      <div>{children}</div>
    </>
  )
}

 
FormでTemplateを差し替えます。

<Form
  schema={schema}
  uiSchema={uiSchema}
  validator={validator}
  onSubmit={onSubmit}
  templates={{
    ArrayFieldItemTemplate: MyArrayFieldItemTemplate
  }}
/>

 
表示して + ボタンをクリックすると、各itemの一番先頭に文字列が追加されました。

 

hasToolbar propsとは何か

ところで、公式ドキュメントを見たところ、 hasToolbar props の記載がありました。

hasToolbar : A boolean value stating whether the array item has a toolbar.

https://rjsf-team.github.io/react-jsonschema-form/docs/advanced-customization/custom-templates#arrayfielditemtemplate

 
しかし、 Toolbar とは何かの説明が見当たりませんでした。

そこでソースコードを見たところ、以下の実装がありました。
https://github.com/rjsf-team/react-jsonschema-form/blob/v5.15.1/packages/core/src/components/fields/ArrayField.tsx#L843-L850

const has: { [key: string]: boolean } = {
  moveUp: orderable && canMoveUp,
  moveDown: orderable && canMoveDown,
  copy: copyable && canAdd,
  remove: removable && canRemove,
  toolbar: false,
};
has.toolbar = Object.keys(has).some((key: keyof typeof has) => has[key]);

  これより

  • moveUp
  • moveDown
  • copy
  • remove

の値により hasToolbar の値が変わりそうでした。

ただ、上記のうち、 remove がデフォルトでは常に true なため、 hasToolbar は常に true となりそうでした。
Arrays | react-jsonschema-form

そこで、 uiSchemaで removablefalse にして挙動をためしてみることにします。

 
まずはTemplateで、現在の各値をコンソールへ出力します。

import {ArrayFieldTemplateItemType} from "@rjsf/utils";

export const MyArrayFieldItemTemplate = (props: ArrayFieldTemplateItemType) => {
  const { hasMoveUp, hasMoveDown, hasRemove, hasToolbar, children, index } = props

  console.log("=========================>")
  console.log(index)
  console.log("hasMoveUp", hasMoveUp)
  console.log("hasMoveDown", hasMoveDown)
  console.log("hasRemove", hasRemove)
  console.log("hasToolbar", hasToolbar)

  return (
    <>
      <div style={{color: "darkkhaki"}}>Field Item</div>
      <div>{children}</div>
    </>
  )
}

 
続いて、 uiSchema にて removable = false にします。

const uiSchema: UiSchema = {
  arrayInput: {
    'ui:options': {
      removable: false,
    }
  }
}

const onSubmit = ({formData}, _event) => {
  console.log(formData)
}

return (
  <div style={{width: '400px'}}>
    <Form
      schema={schema}
      uiSchema={uiSchema}
      validator={validator}
      onSubmit={onSubmit}
      templates={{
        ArrayFieldItemTemplate: MyArrayFieldItemTemplate
      }}
    />
  </div>
)

 
動作確認をします。

+ ボタンを1回クリックした時のコンソールでは、関係する値がすべて false になるため、 hasToolbarfalse となることが分かりました。

 
続いて、 + ボタンを2回クリックすると、 hasMoveDowntrue になったため、 hasToolbartrue になりました。

 

ArrayFieldTemplate

次は ArrayFieldTemplate をカスタマイズしてみます。
https://rjsf-team.github.io/react-jsonschema-form/docs/advanced-customization/custom-templates#arrayfieldtemplate

 
ここでは titleorange にしたTemplateを用意します。

import {ArrayFieldTemplateProps} from "@rjsf/utils";

export const MyArrayFieldTemplate = (props: ArrayFieldTemplateProps) => {
  const { title, items } = props

  return (
    <>
      <div style={{color: "orange"}}>{title}</div>
      {
        items && items.map((item) => {
          return (
            <div key={item.key}>{item.children}</div>
          )
        })
      }
    </>
  )
}

 
FormでTemplateを差し替えます。

<Form
  schema={schema}
  validator={validator}
  onSubmit={onSubmit}
  templates={{
    ArrayFieldTemplate: MyArrayFieldTemplate
  }}
/>

 
表示すると、titleがorangeになりました。一方、入力項目は非表示になってしまいました。

 
続いて、入力項目を表示できないかと考え、Formに formData を追加してみます。

<Form
  schema={schema}
  validator={validator}
  onSubmit={onSubmit}
  templates={{
    ArrayFieldTemplate: MyArrayFieldTemplate
  }}
  formData={{
    arrayInput: [
      {
        stringInput: "foo", integerInput: 100
      }
    ]
  }}
/>

 
すると、入力項目がformDataの値を伴って表示されました。

 

複数のArray系のTemplateを同時に利用する

最後に、複数のTempateを同時に利用したときにどのように表示されるかを確認します。

 

すべてのTempateを利用する

まずはすべてのTempateを利用した場合です。

<Form
  schema={schema}
  validator={validator}
  onSubmit={onSubmit}
  templates={{
    ArrayFieldTemplate: MyArrayFieldTemplate,
    ArrayFieldItemTemplate: MyArrayFieldItemTemplate,
    ArrayFieldTitleTemplate: MyArrayFieldTitleTemplate,
    ArrayFieldDescriptionTemplate: MyArrayFieldDescriptionTemplate
  }}
  formData={{
    arrayInput: [
      {
        stringInput: "foo", integerInput: 100
      }
    ]
  }}
/>

 
すると、 ArrayFieldTitleTemplate の変更だけが反映されました。

 

ArrayFieldTitleTemplate以外を同時に利用する

では ArrayFieldTitleTemplate を外すとどうなるかを確認します。

<Form
  schema={schema}
  validator={validator}
  onSubmit={onSubmit}
  templates={{
    ArrayFieldItemTemplate: MyArrayFieldItemTemplate,
    ArrayFieldTitleTemplate: MyArrayFieldTitleTemplate,
    ArrayFieldDescriptionTemplate: MyArrayFieldDescriptionTemplate
  }}
/>

 
すると、指定したすべての内容が反映されました。

 

ソースコード

Githubにあげました。
https://github.com/thinkAmi-sandbox/rjsf_advent_calendar_2023

今回のプルリクはこちら。
https://github.com/thinkAmi-sandbox/rjsf_advent_calendar_2023/pull/16