自定义字段(Custom Fields)

页面摘要: 自定义字段通过新的字段类型扩展 Strapi,这些字段在内容类型构建器和内容管理器中表现得像原生字段。本文档中的说明涵盖了通过插件构建或安装字段,以及如何以编程方式注册它们。

自定义字段(Custom Fields)通过向内容类型和组件添加新类型的字段来扩展 Strapi 的能力。一旦通过插件创建或添加到 Strapi,自定义字段就可以像内置字段一样在内容类型构建器和内容管理器中使用。

  • Plan:免费功能
  • Role & permission:无
  • Activation:默认可用并已启用
  • Environment:在开发与生产环境中均可用

配置

现成的自定义字段可以在 Marketplace(应用市场) 上找到。一旦安装,无需其他配置,你就可以开始使用它们(请参阅 使用)。

你也可以开发自己的自定义字段。

开发你自己的自定义字段

尽管添加自定义字段的推荐方式是通过创建插件,特定于应用程序的自定义字段也可以在 src/index 和 src/admin/app 文件中找到的全局 register 函数 中注册。

当前限制
  • 自定义字段只能通过插件在应用市场上共享和分发。
  • 自定义字段无法向 Strapi 添加新的数据类型,必须使用 模型属性 文档中描述的现有的、内置的 Strapi 数据类型。
  • 你也不能修改现有的数据类型。
  • 专属于 Strapi 的特殊数据类型,例如关系、媒体、组件或动态区域数据类型,不能在自定义字段中使用。
WARNING

通过插件注册自定义字段需要创建并启用一个插件(参见插件开发)。

自定义字段插件同时包含服务端和管理面板两部分。自定义字段必须在两部分都注册后,才能在 Strapi 的管理面板中使用。

在服务端注册自定义字段

Strapi 的服务端需要感知所有自定义字段,以确保使用自定义字段的属性是有效的。

strapi.customFields 对象在 Strapi 实例上暴露一个 register() 方法。该方法用于在插件的服务端 register 生命周期 期间在服务端注册自定义字段。

strapi.customFields.register() 通过传入一个(或一组)带有若干参数的对象,在服务端注册一个或多个自定义字段。

用于在服务端注册自定义字段的参数:

参数(Parameter)说明类型(Type)
name自定义字段的名称String
plugin

(可选) | 创建自定义字段的插件的名称

❗️ 如果定义,管理面板注册中的 pluginId 值必须具有相同的值(请参阅 在管理面板中注册自定义字段) | String | | type | 自定义字段将使用的数据类型 | String | | inputSize

(可选) | 用于定义管理面板中自定义字段输入框宽度的参数 | Object |

可选的 inputSize 对象在指定时,必须包含以下所有参数:

参数(Parameter)说明类型(Type)
default输入框将在管理面板 12 列网格中占据的默认列数。
该值可以是 4、6、8 或 12。Integer
isResizable输入框是否可调整大小Boolean

示例:在服务端注册一个示例“color(颜色)”自定义字段:

在以下示例中,color-picker 插件是使用 CLI 生成器创建的(请参阅 插件开发):

JavaScript

module.exports = ({ strapi }) => {
strapi.customFields.register({
  name: "color",
  plugin: "color-picker",
  type: "string",
  inputSize: {
    // optional
    default: 4,
    isResizable: true,
  },
});
};

TypeScript

export default ({ strapi }: { strapi: any }) => {
strapi.customFields.register({
  name: "color",
  plugin: "color-picker",
  type: "string",
  inputSize: {
    // optional
    default: 4,
    isResizable: true,
  },
});
};

如果你没有通过 CLI 生成器搭建插件代码,也可以直接在 strapi-server.js 文件中声明自定义字段:

JavaScript

module.exports = {
register({ strapi }) {
  strapi.customFields.register({
    name: "color",
    plugin: "color-picker",
    type: "text",
    inputSize: {
      // optional
      default: 4,
      isResizable: true,
    },
  });
},
};

TypeScript

export default {
register({ strapi }: { strapi: any }) {
  strapi.customFields.register({
    name: "color",
    plugin: "color-picker",
    type: "text",
    inputSize: {
      // optional
      default: 4,
      isResizable: true,
    },
  });
},
};

在管理面板中注册自定义字段

WARNING

通过插件注册自定义字段需要创建并启用一个插件(参见插件开发)。

自定义字段必须在 Strapi 的管理面板中注册,才能在内容类型构建器和内容管理器中可用。

app.customFields 对象在 StrapiApp 实例上暴露一个 register() 方法。该方法用于在插件的 admin register 生命周期 期间在管理面板中注册自定义字段。

app.customFields.register() 通过传入一个(或一组)带有若干参数的对象,在管理面板中注册一个或多个自定义字段。

用于在服务端注册自定义字段的参数:

参数(Parameter)说明类型(Type)
name自定义字段的名称String
pluginId

(可选) | 创建自定义字段的插件的名称

❗️ 如果定义,服务端注册中的 plugin 值必须具有相同的值(请参阅 在服务端注册自定义字段) | String | | type | 自定义字段将使用的现有 Strapi 数据类型

❗️ 不能使用关系、媒体、组件或动态区域。 | String | | icon

(可选) | 自定义字段的图标 | React.ComponentType | | intlLabel | 名称的翻译 | IntlObject | | intlDescription | 说明的翻译 | IntlObject | | components | 在内容管理器中显示自定义字段所需的组件(请参阅 组件) | | | options

(可选) | 内容类型构建器要使用的选项(请参阅 选项) | Object |

示例:在管理面板中注册一个示例“color(颜色)”自定义字段:

在以下示例中,color-picker 插件是使用 CLI 生成器创建的(请参阅 插件开发):

JavaScript

import ColorPickerIcon from "./components/ColorPicker/ColorPickerIcon";

export default {
register(app) {
  // ... app.addMenuLink() goes here
  // ... app.registerPlugin() goes here

  app.customFields.register({
    name: "color",
    pluginId: "color-picker", // the custom field is created by a color-picker plugin
    type: "string", // the color will be stored as a string
    intlLabel: {
      id: "color-picker.color.label",
      defaultMessage: "Color",
    },
    intlDescription: {
      id: "color-picker.color.description",
      defaultMessage: "Select any color",
    },
    icon: ColorPickerIcon, // don't forget to create/import your icon component
    components: {
      Input: async () =>
        import('./components/Input').then((module) => ({
          default: module.Input,
        })),
    },
    options: {
      // declare options here
    },
  });
},

// ... bootstrap() goes here
};

TypeScript

import ColorPickerIcon from "./components/ColorPicker/ColorPickerIcon";

export default {
register(app) {
  // ... app.addMenuLink() goes here
  // ... app.registerPlugin() goes here

  app.customFields.register({
    name: "color",
    pluginId: "color-picker", // the custom field is created by a color-picker plugin
    type: "string", // the color will be stored as a string
    intlLabel: {
      id: "color-picker.color.label",
      defaultMessage: "Color",
    },
    intlDescription: {
      id: "color-picker.color.description",
      defaultMessage: "Select any color",
    },
    icon: ColorPickerIcon, // don't forget to create/import your icon component
    components: {
      Input: async () =>
        import('./components/Input').then((module) => ({
          default: module.Input,
        })),
    },
    options: {
      // declare options here
    },
  });
},

// ... bootstrap() goes here
};
组件(Components)

app.customFields.register() 必须传入一个带有 Input React 组件的 components 对象,以便在内容管理器的编辑视图中使用。

示例:注册一个 Input 组件:

在以下示例中,color-picker 插件是使用 CLI 生成器创建的(请参阅 插件开发):

JavaScript

export default {
register(app) {
  app.customFields.register({
    // …
    components: {
      Input: async () =>
        import('./components/Input').then((module) => ({
          default: module.Input,
        })),
    },
    // …
  });
},
};

TypeScript

export default {
register(app) {
  app.customFields.register({
    // …
    components: {
      Input: async () =>
        import('./components/Input').then((module) => ({
          default: module.Input,
        })),
    },
    // …
  });
},
};

传递给自定义字段 Input 组件的属性(Props):

属性(Prop)说明类型(Type)
attribute带有自定义字段底层 Strapi 类型及选项的 attribute 对象{ type: String, customField: String }
description在 配置视图 中设置的字段说明IntlObject
placeholder在 配置视图 中设置的字段占位符IntlObject
hint在 配置视图 中设置的字段说明,连同最小/最大 验证要求String
name在内容类型构建器中设置的字段名称String
intlLabel在内容类型构建器或配置视图中设置的字段名称IntlObject
onChange输入变更事件的处理程序。name 参数引用字段名称。type 参数引用底层的 Strapi 类型({ target: { name: String value: unknown type: String } }) => void
contentTypeUID字段所属的内容类型String
type自定义字段的 uid,例如 plugin::color-picker.colorString
value底层 Strapi 类型期望的输入值unknown
required字段是否必填boolean
error验证后收到的错误IntlObject
disabled输入是否被禁用boolean

自 Strapi v4.13.0 起,内容管理器中的字段可以通过 URLSearchParam field 自动聚焦。建议你的输入组件用 React 的 forwardRef 方法包裹;你应该将对应的 ref 传递给 input 元素。

示例:一个自定义的文本输入

在以下示例中,我们提供了一个受控的自定义文本输入。所有输入都应该是受控的,否则它们的数据在保存时不会被提交。

JavaScript

import * as React from "react";

import { useIntl } from "react-intl";

const Input = React.forwardRef((props, ref) => {
const { attribute, disabled, intlLabel, name, onChange, required, value } =
  props; // these are just some of the props passed by the content-manager

const { formatMessage } = useIntl();

const handleChange = (e) => {
  onChange({
    target: { name, type: attribute.type, value: e.currentTarget.value },
  });
};

return (
  <label>
    {formatMessage(intlLabel)}
    <input
      ref={ref}
      name={name}
      disabled={disabled}
      value={value}
      required={required}
      onChange={handleChange}
    />
  </label>
);
});

export default Input;

TypeScript

import * as React from "react";

import { useIntl } from "react-intl";

const Input = React.forwardRef((props, ref) => {
const { attribute, disabled, intlLabel, name, onChange, required, value } =
  props; // these are just some of the props passed by the content-manager

const { formatMessage } = useIntl();

const handleChange = (e) => {
  onChange({
    target: { name, type: attribute.type, value: e.currentTarget.value },
  });
};

return (
  <label>
    {formatMessage(intlLabel)}
    <input
      ref={ref}
      name={name}
      disabled={disabled}
      value={value}
      required={required}
      onChange={handleChange}
    />
  </label>
);
});

export default Input;
TIP

要更详细地了解提供给 customFields 的属性以及它们的用法,请查看 Strapi 代码库中的 ColorPickerInput file。

选项(Options)

app.customFields.register() 可以传入一个额外的 options 对象,包含以下参数:

传递给自定义字段 options 对象的参数:

选项参数(Options parameter)说明类型(Type)
base在内容类型构建器中字段的 Base settings(基本设置) 标签页可用的设置Object 或 Array of Objects
advanced在内容类型构建器中字段的 Advanced settings(高级设置) 标签页可用的设置Object 或 Array of Objects
validator返回对象的验证器函数,用于清理输入。使用 yup schema object。Function

base 和 advanced 设置都接受一个对象或对象数组,每个对象是一个设置分区。每个设置分区可以包含:

  • 一个 sectionTitle,以 IntlObject 的形式声明该分区的标题
  • 以及一个 items 列表,作为对象数组。

items 数组中的每个对象可以包含以下参数:

项参数(Items parameter)说明类型(Type)
name输入的标签。
必须使用 options.settingName 格式。String
description在内容类型构建器中使用的输入说明String
intlLabel输入标签的翻译IntlObject
type输入的类型(例如 select、checkbox)String

示例:为一个示例“color(颜色)”自定义字段声明选项:

在以下示例中,color-picker 插件是使用 CLI 生成器创建的(请参阅 插件开发):

JavaScript

// imports go here (ColorPickerIcon, pluginId, yup package…)

export default {
register(app) {
  // ... app.addMenuLink() goes here
  // ... app.registerPlugin() goes here
  app.customFields.register({
    // …
    options: {
      base: [
        /*
          Declare settings to be added to the "Base settings" section
          of the field in the Content-Type Builder
        */
        {
          sectionTitle: {
            // Add a "Format" settings section
            id: "color-picker.color.section.format",
            defaultMessage: "Format",
          },
          items: [
            // Add settings items to the section
            {
              /*
                Add a "Color format" dropdown
                to choose between 2 different format options
                for the color value: hexadecimal or RGBA
              */
              intlLabel: {
                id: "color-picker.color.format.label",
                defaultMessage: "Color format",
              },
              name: "options.format",
              type: "select",
              value: "hex", // option selected by default
              options: [
                // List all available "Color format" options
                {
                  key: "hex",
                  defaultValue: "hex",
                  value: "hex",
                  metadatas: {
                    intlLabel: {
                      id: "color-picker.color.format.hex",
                      defaultMessage: "Hexadecimal",
                    },
                  },
                },
                {
                  key: "rgba",
                  value: "rgba",
                  metadatas: {
                    intlLabel: {
                      id: "color-picker.color.format.rgba",
                      defaultMessage: "RGBA",
                    },
                  },
                },
              ],
            },
          ],
        },
      ],
      advanced: [
        /*
          Declare settings to be added to the "Advanced settings" section
          of the field in the Content-Type Builder
        */
      ],
      validator: (args) => ({
        format: yup.string().required({
          id: "options.color-picker.format.error",
          defaultMessage: "The color format is required",
        }),
      }),
    },
  });
},
};

TypeScript

// imports go here (ColorPickerIcon, pluginId, yup package…)

export default {
register(app) {
  // ... app.addMenuLink() goes here
  // ... app.registerPlugin() goes here
  app.customFields.register({
    // …
    options: {
      base: [
        /*
          Declare settings to be added to the "Base settings" section
          of the field in the Content-Type Builder
        */
        {
          sectionTitle: {
            // Add a "Format" settings section
            id: "color-picker.color.section.format",
            defaultMessage: "Format",
          },
          items: [
            // Add settings items to the section
            {
              /*
                Add a "Color format" dropdown
                to choose between 2 different format options
                for the color value: hexadecimal or RGBA
              */
              intlLabel: {
                id: "color-picker.color.format.label",
                defaultMessage: "Color format",
              },
              name: "options.format",
              type: "select",
              value: "hex", // option selected by default
              options: [
                // List all available "Color format" options
                {
                  key: "hex",
                  defaultValue: "hex",
                  value: "hex",
                  metadatas: {
                    intlLabel: {
                      id: "color-picker.color.format.hex",
                      defaultMessage: "Hexadecimal",
                    },
                  },
                },
                {
                  key: "rgba",
                  value: "rgba",
                  metadatas: {
                    intlLabel: {
                      id: "color-picker.color.format.rgba",
                      defaultMessage: "RGBA",
                    },
                  },
                },
              ],
            },
          ],
        },
      ],
      advanced: [
        /*
          Declare settings to be added to the "Advanced settings" section
          of the field in the Content-Type Builder
        */
      ],
      validator: (args) => ({
        format: yup.string().required({
          id: "options.color-picker.format.error",
          defaultMessage: "The color format is required",
        }),
      }),
    },
  });
},
};
TIP

Strapi 代码库给出了设置对象如何描述的示例:查看 baseForm.ts 文件了解 base 设置,查看 advancedForm.ts 文件了解 advanced 设置。基础表单内联列出了设置项,而高级表单则从 attributeOptions.js 文件中获取这些项。

使用

在管理面板中

自定义字段可以通过从 Marketplace(应用市场) 安装,或通过你自己创建来添加到 Strapi。

一旦添加到 Strapi,自定义字段就可以添加到任何内容类型。在为内容类型选择字段时,自定义字段列于 Custom(自定义) 标签页中。

每种自定义字段类型都可以有基本和高级设置。Marketplace(应用市场) 列出了可用的自定义字段,并为每种自定义字段托管了专门的文档,包括特定的设置。

在代码中

一旦创建并使用,自定义字段就像模型模式中的任何其他属性一样定义。

自定义字段在模型的 attributes 中通过 type: customField 显式定义。

与其他类型模型的属性定义相比,自定义字段的属性还显示了以下特性:

  • 自定义字段有一个 customField 属性。其值充当唯一标识符,指示应使用哪个已注册的自定义字段,并遵循以下 2 种格式之一:

    格式(Format)来源(Origin)
    plugin::plugin-name.field-name自定义字段是通过插件创建的
    global::field-name自定义字段特定于当前 Strapi 应用程序,并直接在 register 函数 中创建
  • 自定义字段可以有额外的参数,具体取决于注册自定义字段时定义的内容(请参阅 服务端注册 和 管理面板注册)。

示例:一个简单的 color 自定义字段模型定义:


{
// …
"attributes": {
  "color": { // name of the custom field defined in the Content-Type Builder
    "type": "customField",
    "customField": "plugin::color-picker.color",
    "options": {
      "format": "hex"
    }
  }
}
// …
}
用于性能优化的自定义字段

存储结构化 JSON 的自定义字段,可以作为深度嵌套关系结构的一种高性能替代方案。当数据不需要被独立查询时,将其作为单个 JSON 字段存储可避免昂贵的连接(join)操作。请参阅 Strapi 博客上的 Building High-Performance Strapi Applications。