typo3/formbuilder

此包已被废弃,不再维护。作者建议使用neos/formbuilder包。

Flow Form Framework集成到Neos CMS

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

安装次数: 5 122

依赖者: 1

建议者: 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 Form Framework集成到Neos CMS

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

相关包

请务必查看其他Flow Form Framework 相关包

使用方法

使用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'
}

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

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

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

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

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

示例:自定义“标题”选择器

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

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

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'
        }
    }
}

动态选项

我们可以在Fusion原型中不硬编码选项,而是使用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'
        }
    }