neos/formbuilder

此包已被弃用且不再维护。作者建议使用neos/form-yamlbuilder包。

Flow 表单框架集成到 Neos CMS

资助包维护!
shop.neos.io/neosfunding

安装: 1,893

依赖项: 0

建议者: 0

安全性: 0

星标: 18

关注者: 5

分支: 26

开放问题: 41

类型:neos-package

2.3.5 2024-07-03 07:20 UTC

This package is auto-updated.

Last update: 2024-07-03 07:29:18 UTC


README

Flow 表单框架集成到 Neos CMS

此包为 Neos CMS 后端添加了 Flow 表单框架 的构建器。它还附带 Fusion 原型,允许基于 Fusion 的动态表单定义。

相关包

请务必查看其他 Flow 表单框架的相关包

使用方法

使用composer安装此包

composer require neos/form-builder

注意:此包需要版本 3.1 或更高版本的 neos/neos

在 Neos 后端中现在有一个新的内容元素类型可以使用

Create Wizard

注意:如果您已安装 Neos.NodeTypes 包,则可以插入两种类型的表单。以下代码片段可以添加到站点的 NodeTypes.yaml 中,以禁用 Neos.NodeTypes 表单

'Neos.NodeTypes:Form': ~

现在,可以在表单中添加 表单元素

Add Form Element

可以为每个表单元素添加 验证器,并且一些元素允许创建子表单元素或 选择选项。此外,每个表单都可以创建包含元素的 更多表单页面。当然,还可以向表单添加 表单完成器

因此,有相当多的内容集合,它们很容易混淆。一个解决方案是在处理复杂表单时使用 结构树

Structure Tree

此外,此包还附带了一些自定义 StyleSheet,可以使表单构建器更容易访问

调整表单构建器的外观

此包提供了一些 CSS,可以包含在 Neos 后端中,以调整表单构建器的样式。以下 Fusion 代码片段可以添加,以在 Neos 后端中包含自定义 CSS(只要页面 Fusion 原型扩展自 Neos.Neos:Page

prototype(Neos.Neos:Page) {
    head.formBuilderStyles = Neos.Fusion:Tag {
        tagName = 'link'
        attributes {
            rel = 'stylesheet'
            href = Neos.Fusion:ResourceUri {
                path = 'resource://Neos.Form.Builder/Public/Styles/Backend.css'
            }
        }
        @position = 'end'
        @if.isInBackend = ${documentNode.context.inBackend}
    }
}

因此,表单在后端将看起来像这样

Custom Styles

使用 Fusion 构建表单

此包的主要目的是将其集成到 Neos 后端,使用内容存储节点来表示表单的定义。但在某些情况下,在纯 Fusion 中定义表单非常有用

prototype(Some.Package:ContactForm) < prototype(Neos.Form.Builder:Form) {
    presetName = 'default'
    firstPage {
        elements {
            name = Neos.Form.Builder:SingleLineText.Definition {
                label = 'Name'
                validators {
                    stringLength = Neos.Form.Builder:StringLengthValidator.Definition {
                        options.minimum = 5
                    }
                }
                properties.placeholder = 'Your name'
            }
            email = Neos.Form.Builder:SingleLineText.Definition {
                label = 'Email'
                validators {
                    emailAddress = Neos.Form.Builder:EmailAddressValidator.Definition
                }
                properties.placeholder = 'Your email address'
            }
            interests = Neos.Form.Builder:MultipleSelectCheckboxes.Definition {
                label = 'Interests'
                required = ${false}
                properties.options {
                    neos = 'Neos CMS'
                    flow = 'Neos Flow'
                    chicken = 'Chickens'
                }
            }
            comment = Neos.Form.Builder:MultiLineText.Definition {
                label = 'Message'
                properties.placeholder = 'Your Comment'
            }
        }
    }
    finishers {
        confirmationFinisher = Neos.Form.Builder:ConfirmationFinisher.Definition {
            options {
                message = 'Thank you for your comment, {name}!'
            }
        }
    }
}

要创建多页表单,可以使用 furtherPages 字段

prototype(Some.Package:ContactForm) < prototype(Neos.Form.Builder:Form) {
    // ...
    furtherPages {
        page2 = Neos.Form.Builder:FormPage.Definition {
            elements {
                elementOnPage2 = Neos.Form.Builder:SingleLineText.Definition {
                    label = 'Element on page 2'
                }
            }
        }
        preview = Neos.Form.Builder:PreviewPage.Definition
    }
}

现在,可以使用 Some.Package:ContactForm 原型,就像使用任何其他内容元素(甚至文档)一样。

在这种情况下,结果是静态的联系表单,因此与基于 YAML 的表单定义没有太大区别。但显然可以使用所有 Fusion 和 Eel 的功能来创建动态表单。例如,表单字段可以预先填充已认证用户的数据。

// ...
    someFormField = Neos.Form.Builder:SingleLineText.Definition {
        defaultValue = ${Security.account.accountIdentifier}
        // ...

为了根据当前Fusion上下文设置选项,必须显式地将值添加到表单上下文中,以便在元素/完成器配置中使用它们。

prototype(Some.ContactForm:Contact) < prototype(Neos.Form.Builder:Form) {

    // Redirect to the first child node of type "Some.Target:NodeType" upon form submission
    @context.redirectUri = Neos.Neos:NodeUri {
        node = ${q(documentNode).children('[instanceof Some.Target:NodeType]').get(0)}
    }

    // ...

    finishers {
        redirectFinisher = Neos.Form.Builder:RedirectFinisher.Definition {
            options {
                uri = ${redirectUri}
            }
        }
    }
}

缓存

默认情况下,所有 Neos.Form.Builder:Form 实现都没有被缓存。这样做是为了避免假设相反时出现讨厌的错误。

为了优化性能,可以改变单个表单的行为,使其(部分)缓存。例如,上面的静态表单可以按照以下方式更改:

prototype(Some.Package:ContactForm) < prototype(Neos.Form.Builder:Form) {
    @cache {
        mode = 'dynamic'
        entryIdentifier {
            node = ${node}
        }
        entryTags {
            1 = ${Neos.Caching.nodeTag(node)}
        }
        entryDiscriminator = ${request.httpRequest.methodSafe ? 'static' : false}
        context {
            1 = 'node'
            2 = 'documentNode'
        }
    }
    // ...

有了这些设置,表单的初始渲染被缓存,当表单提交时(=不安全的请求)模式会改为“未缓存”。

注意:动态缓存模式仅在 Neos 版本 2.3.15+ 和 3.1.5+ 中可靠地工作。

自定义表单元素

default 预设中定义的表单元素(以及在此包中可用)旨在作为简单表单的快速入门。Flow 表单框架的主要优势在于其易于创建自定义表单元素、验证器和完成器(请参阅文档)。

为了在表单构建器中使用自定义表单元素,必须定义相应的 NodeType

'Some.Package:SomeFormElementNodeType':
  superTypes:
    'Neos.Form.Builder:FormElement': TRUE
  ui:
    label: 'Some label'
    # add the new item in the "Custom Form Elements" section. Other options are form.elements, form.select and form.container
    group: 'form.custom'

表单元素映射

对于表单元素节点,假设存在一个名为 <NodeType>.Definition 的 Fusion 原型来定义表单元素。 (.Definition 后缀用于防止与 渲染 表单元素的原型命名冲突)。

上述指定节点类型的对应 Fusion 原型可能如下所示

prototype(Some.Package:SomeFormElementNodeType.Definition) < prototype(Neos.Form.Builder:FormElement.Definition) {
    formElementType = 'Some.Package:SomeFormElement'
}

或者,可以通过在节点类型配置中的 options.form.formElementType 设置中指定映射到表单元素类型,如果不需要自定义 Fusion 原型,则可以指定映射。

'Some.Package:SomeFormElementNodeType':
  // ...

  options:
    form:
      formElementType: 'Some.Package:SomeFormElement'

如果设置了该选项,将使用常规的 Neos.Form.Builder:FormElement.Definition Fusion 原型来评估该表单元素的定义。

无论如何,该表单元素必须在配置的表单预设中存在,才能正确渲染。

示例:自定义 "title" 选择器

title 选择器是联系表单的常见要求。而不是添加通用的选择元素并手动为每个实例添加选项,我们可以轻松创建一个自定义元素。

首先,需要一个新节点类型

NodeTypes.yaml:

'Some.Package:Title':
  superTypes:
    'Neos.Form.Builder:FormElement': TRUE
  ui:
    label: 'Title'
    group: 'form.custom'

相应的 Fusion 将表单元素映射并指定可选择的选项

Title.fusion:

prototype(Some.Package:Title.Definition) < prototype(Neos.Form.Builder:FormElement.Definition) {
    # we map this to the existing SingleSelectDropdown Form Element
    formElementType = 'Neos.Form:SingleSelectDropdown'
    properties {
        options = Neos.Form.Builder:SelectOptionCollection {
            mrs = 'Mrs.'
            mr = 'Mr.'
            miss = 'Miss'
            ms = 'Ms.'
            dr = 'Dr.'
            prof = 'Prof.'
        }
    }
}

注意:在这种情况下,我们将新元素映射到 Neos.Form 包中的 SingleSelectDropdown 表单元素。我们也可以使用 SingleSelectRadioButtons,或者到一个自定义元素。或者像以下示例那样有动态映射

示例:具有动态表单元素类型映射的自定义选择器

在这个例子中,我们创建了一个用于新闻通讯类别的选择器。它与上一个例子非常相似。但在这个例子中,我们想要给编辑器更多的控制,并允许他们指定是否可以选择 多个 类别。因此,我们创建了一个具有属性 multiple 的节点类型

NodeTypes.yaml:

'Some.Package:NewsletterCategories':
  superTypes:
    'Neos.Form.Builder:FormElement': TRUE
    'Neos.Form.Builder:DefaultValueMixin': FALSE
  ui:
    label: 'Newsletter Category Selector'
    group: 'form.select'
  properties:
    'multiple':
      type: boolean
      ui:
        label: 'Allow multiple'
        inspector:
          group: 'formElement'

..并在 Fusion 原型中根据该属性映射表单元素

NewsletterCategories.fusion:

prototype(Some.Package:NewsletterCategories.Definition) < prototype(Neos.Form.Builder:FormElement.Definition) {
    # depending on the "multiple" property this will render checkboxes or radio buttons
    formElementType = ${this.properties.multiple ? 'Neos.Form:MultipleSelectCheckboxes' : 'Neos.Form:SingleSelectRadiobuttons'}
    properties {
        options = Neos.Form.Builder:SelectOptionCollection {
            events = 'Events'
            corporate = 'Corporate'
            marketing = 'Marketing'
        }
    }
}

动态选项

而不是在融合原型中硬编码选项,我们可以使用 FlowQuery 从内容存储库中检索它们。以下示例将使任何 NewsletterCategory 节点可选择

NewsletterCategories.fusion:

    // ...
    properties {
        options = Neos.Form.Builder:SelectOptionCollection {
            collection = ${q(site).children('[instanceof Some.Package:NewsletterCategory]')}
            # we use the node identifier as value, we could use "name" or "label" instead for example
            valuePropertyPath = 'identifier'
        }
    }