efabrica / neoforms
渲染表单的更好方式
Requires
- php: ^8.3
- latte/latte: ^2.11 || ^3.0
- nette/forms: ^3.1
Requires (Dev)
- jetbrains/phpstorm-attributes: ^1.0
- nette/application: ^3.1
- nette/database: ^3.1
- phpunit/phpunit: ^10.3
- radekdostal/nette-datetimepicker: ^3.1
- tracy/tracy: ^2.9
- dev-master
- 3.2.1
- 3.2.0
- 3.1.0
- 3.0.1
- 3.0.0
- 2.5.0
- 2.4.2
- 2.4.1
- 2.4.0
- 2.3.0
- 2.2.0
- 2.1.1
- 2.1.0
- 2.0.2
- 2.0.1
- 2.0.0
- 1.7.1
- 1.7.0
- 1.6.2
- 1.6.1
- 1.6.0
- 1.5.2
- 1.5.1
- 1.5.0
- 1.4.0
- 1.3.0
- 1.2.2
- 1.2.1
- 1.2.0
- 1.1.0
- 1.0.0
- 0.0.2
- 0.0.1
- dev-vrana/php83
- dev-vrana/tags-sortable
- dev-popelis/refactor
- dev-popelis/fix-textarea
- dev-popelis/fix-option-label
- dev-popelis/fix-render
- dev-popelis/checkboxlist
- dev-popelis/fix-hidden
- dev-popelis/fix-select
- dev-popelis/standalone
This package is auto-updated.
Last update: 2024-08-27 12:32:12 UTC
README
NeoForms 是 Nette\Forms 所急需的良药。
- 为您提供更轻松的方式编写自己的渲染模板
- 为您提供规范以更有效地与团队协作
- 为您提供使用
{formRow}
、{formLabel}
和{formInput}
标签渲染表单字段的途径,这些标签您可能已从 Symfony 使用过。 - 为您提供在 PHP 中构建表单时渲染行和列的途径
- 为您提供
readonly
模式,以渲染无法编辑的人的表单,通过渲染常规文本而不是输入来代替。(告别灰色禁用的字段!) - 为您提供 FormCollection,用于预样式 AJAX 无 Multiplier,并内置 diff 计算器
- ControlGroup 内部嵌套 ControlGroup(树形结构)
安装
composer require efabrica/neoforms
# config.neon includes: - ../../vendor/efabrica/neoforms/config.neon
文档
使用 ActiveRowForm
use Efabrica\NeoForms\Build\NeoForm; use Efabrica\NeoForms\Build\NeoFormFactory; use Efabrica\NeoForms\Build\NeoFormControl; use Efabrica\NeoForms\Build\ActiveRowForm; use Nette\Database\Table\ActiveRow; use Nette\Application\UI\Template; class CategoryForm extends ActiveRowForm { private NeoFormFactory $formFactory; private CategoryRepository $repository; // There is no parent constructor public function __construct(NeoFormFactory $formFactory, CategoryRepository $repository) { $this->formFactory = $formFactory; $this->repository = $repository; } /** * NeoFormControl is attached to presenter and used in template. * * @param ActiveRow|null $row Optional ActiveRow parameter to fill the form with data * @return NeoFormControl */ public function create(?ActiveRow $row = null): NeoFormControl { $form = $this->formFactory->create(); $form->addText('name', 'Category Name') ->setHtmlAttribute('placeholder', 'Enter category name') ->setRequired('Name is required to fill'); $form->addText('description', 'Category Description') ->setHtmlAttribute('placeholder', 'Type category description') ->setRequired('Description is required to fill'); $form->addSubmit('save', ($row === null ? 'Edit' : 'Create') . ' Category') ->setIcon('save'); return $this->control($form, $row); } }
展示者中的组件使用
class CategoryPresenter extends AdminPresenter { private CategoryForm $form; private CategoryRepository $repository; public function actionCreate(): void { $this->addComponent($this->form->create(), 'categoryForm'); } public function actionUpdate(int $id): void { $row = $this->repository->findOneById($id); if (!$row instanceof \Nette\Database\Table\ActiveRow) { throw BadRequestException(); } $this->addComponent($this->form->create($row), 'categoryForm'); } }
latte 模板中的组件使用
简单渲染
{* create.latte *} {block content} <div class="category-card"> <div class="category-form-wrapper"> {control categoryForm} </div> </div>
在 <form>
标签内自定义 HTML 结构
{* create.latte *} {block content} <div class="category-card"> <div class="category-form-wrapper"> {neoForm categoryForm} {formRow $form['name'], data-joke => 123} {* adds [data-joke="123"] to the wrapping div *} {formRow $form['description']} <img src="whatever.png" alt="whatever" /> <div class="row"> <div class="col">{formRow $form['is_pinned']}</div> <div class="col">{formRow $form['is_highlight']}</div> <div class="col">{formRow $form['is_published']}</div> </div> {formRow $form['save'], input => [class => 'reverse']} {* sets input's class to 'reverse' *} {/neoForm} </div> </div>
独立的 HTML 表单模板
{* categoryForm.latte *} {neoForm categoryForm} {formRow $form['name'], data-joke => 123} {* adds [data-joke="123"] to the wrapping div *} {formRow $form['description']} {formRow $form['save'], input => [class => 'reverse']} {* sets input's class to 'reverse' *} {/neoForm}
分组表单元素
/** @var \Efabrica\NeoForms\Build\NeoForm $form */ $names = $form->group('names'); $names->addText('id', 'ID'); $names->addText('icon', 'Icon'); $checkboxes = $form->group('checkboxes'); $checkboxes->addToggleSwitch('enabled', 'Enabled'); $checkboxes->addCheckbox('verified', 'Verified');
{neoForm categoryForm} <div class="row"> <div class="col-6"> {formGroup $form->getGroup('names')} {* renders id & icon *} </div> <div class="col-6"> {formGroup $form->getGroup('checkboxes')} {* renders enabled & verified *} </div> </div> {/neoForm}
.row .col 网格布局在 PHP 中
/** @var \Efabrica\NeoForms\Build\NeoForm $form */ $row1 = $form->row(); // returns a row instance $col1 = $row1->col('6'); // returns a new col instance, class="col-6" $col1->addText('a'); $col1->addTextArea('b'); $col2 = $row1->col('6'); // returns a new different col instance $col2->addCheckbox('c'); $a = $form->row('main'); $b = $form->row('main'); assert($a === $b); // true, it's the same instance
Latte 标签(API)文档
{neoForm}
{neoForm}
标签用于在 HTML 中渲染 <form>
元素。它还可以渲染表单末尾所有未渲染的输入。此标签的参数是控制器的名称,不带引号。
如果您想渲染整个表单而不指定任何子元素,请使用以下语法
{neoForm topicForm}{/neoForm} <!-- This is equivalent to {control topicForm} -->
如果您想排除某些表单字段从渲染中,可以使用
rest => false
如此{neoForm topicForm, rest => false} {/neoForm} <!-- This is similar to {form topicform}{/form} -->这将渲染一个空的
<form>
,类似于使用一个空的{form}
标签。
{formRow}
{formRow}
标签用于在包装组内渲染表单标签和表单输入。它接受各种选项。此标签的参数是一个 BaseControl
实例(例如,$form['title']
)。
以下是一些使用 {formRow}
的示例
{formRow $form['title'], class => 'mt-3'}
这渲染了一个具有自定义类的表单行,结果为
<div class="mt-3">...</div>
。
{formRow $form['title'], '+class' => 'mt-3'}
如果您正在使用 Bootstrap 模板,这将渲染一个具有类的表单组,结果为
<div class="form-group mt-3">...</div>
。
您还可以使用选项向输入或标签元素添加属性
{formRow $form['title'], input => [data-tooltip => 'HA!']}这渲染了一个具有
data-tooltip
属性的输入元素的表单行。
{formRow $form['title'], label => [data-toggle => 'modal']}这渲染了一个具有
data-toggle
属性的标签元素的表单行。
{formGroup}
{formGroup}
标签接受一个 ControlGroup
作为必需参数并渲染组中的所有控件。它内部使用 {formRow}
来处理渲染。
示例用法
{formGroup $form->getGroup('main')}
{formLabel}
{formLabel}
标签用于渲染一个 <label>
元素。参数是一个 BaseControl
实例。
示例用法
{formLabel $form['title'], class => 'text-large', data-yes="no"}
这将渲染一个具有自定义类和数据属性的标签元素。
如果表单元素是一个隐藏字段或复选框,则标签以空 HTML 字符串的形式渲染。
{formInput}
{formInput}
标签用于渲染 <input>
、<textarea>
、<button>
或任何表单行的基本部分。参数是一个 BaseControl
实例。
示例用法
{formInput $form['category'], data-select2 => true}
这将渲染一个带有空
data-select2
属性的输入元素。
应用属性
可以使用选项将属性应用到表单元素。以下是一些常用属性:
"icon"
当应用于按钮时,"icon"
属性会在文本之前添加一个图标。例如
$form->addSubmit('save', 'Save')->setOption('icon', 'fa fa-save');
您可以在模板中自定义图标添加的方式。
"description"
"description"
属性在输入元素下方添加灰色辅助文本。例如
$form->addPassword('password', 'Password')->setOption('description', 'At least 8 characters.');
"info"
"info"
属性在标签旁边添加一个蓝色信息圆圈提示。例如
$form->addText('title', 'Title')->setOption('info', 'This appears on homepage');
"readonly"
"readonly"
属性,当设置为 true 时,使值不可修改且不提交。它以徽章的形式渲染。例如
$form->addText('title', 'Title')->setOption('readonly', true); // or {formRow $form['title'], readonly => true} // or $form->setReadonly(true); // to make the entire form readonly // or {neoForm yourForm, readonly => true} // to make the entire form readonly
您还可以提供一个回调函数以实现动态只读行为。
"class"
"class"
属性允许您覆盖行/输入/标签的类或任何其他 HTML 属性。例如
$form->addText('title', 'Title')->setOption('class', 'form-control form-control-lg');
如果您想保留模板中的类,请使用
+class
替代$form->addText('title', 'Title')->setOption('+class', 'form-control-lg');这也同样有效
{formRow $form['title'], input => ['+class' => 'form-control-lg']}`
如果您想强制从模板中移除一个类,请使用 false 替代
$form->addText('title', 'Title')->setOption('class', false);
"input"
和 "label"
您可以将这些属性应用到 {formRow}
标签,分别将 HTML 属性传递给输入和标签元素。例如
{formRow $form['title'], 'input' => ['class' => 'special']} {formRow $form['title'], 'label' => ['class' => 'special']}
FormCollection
用法
use Efabrica\NeoForms\Build\NeoContainer; // Create a new collection called "sources" $form->addCollection('sources', 'Sources', function (NeoContainer $container) { // Add some fields to the collection $container->addText('bookTitle', 'Book Title'); $container->addInteger('Year', 'Year'); // Add another collection for authors $container->addCollection('authors', 'Authors', function (NeoContainer $container) { $container->addText('author', 'Author'); }); });
您可以将表单渲染为表单中的任何其他控件。({formRow}
或自动)
处理
protected function onUpdate(NeoForm $form, array $values, ActiveRow $row): void { // To process the form, you can get the new state of the collection like this: $sources = $values['sources']; // If you want to use the Diff API, you can do something like this: $diff = $form['sources']->getDiff(); foreach($diff->getAdded() as $newRow) { $this->sourceRepository->insert($newRow); } foreach($diff->getDeleted() as $removedRow) { $this->sourceRepository->delete($removedRow); } foreach($diff->getModified() as $updatedRow) { $row = $this->sourceRepository->findOneBy($updatedRow->getOldRow()); $row->update($updatedRow->getDiff()); } }
您不需要使用 Diff API。如果您例如只使用简单的单个文本输入集合,您可能会发现仅持久化新值数组更容易。
自定义模板
要创建自己的扩展模板以渲染表单,您可以遵循我们的示例。以下是一个使用 Bootstrap 4 作为示例的创建自定义扩展模板的逐步指南
步骤 1:创建一个新的 PHP 类
通过扩展基本模板类创建一个新的 PHP 类用于您的扩展模板。在这个示例中,我们将它命名为 Bootstrap5FormTemplate
。确保将此类放置在适当的命名空间中,就像在提供的代码中一样。
namespace Your\Namespace\Here; use Efabrica\NeoForms\Render\Template\NeoFormTemplate; // ... Import other necessary classes here ... class Bootstrap4FormTemplate extends NeoFormTemplate { // Your template implementation goes here }
步骤 2:自定义表单元素
覆盖扩展模板类中的方法,根据您首选的 Bootstrap 4 风格自定义表单元素的渲染。例如,您可以定义文本输入、按钮、复选框和其他表单元素应该如何与 Bootstrap 类一起渲染。
以下是自定义文本输入渲染的示例
protected function textInput(TextInput $control, array $attrs): Html { $el = $control->getControl(); $el->class ??= 'form-control'; // Add Bootstrap class, if no class was specified through ->setHtmlAttribute() return $this->applyAttrs($el, $attrs); }
步骤 3:自定义表单标签
您也可以自定义表单标签的渲染方式。在 Bootstrap 中,您可能想要添加 col-form-label
类以实现正确对齐。通过覆盖 formLabel
方法来实现这一点
public function formLabel(BaseControl $control, array $attrs): Html { $el = $control->getLabel(); $el->class ??= 'col-form-label'; // Add Bootstrap class $el->class('required', $control->isRequired())->class('text-danger', $errors !== []); $this->addInfo($control, $el); foreach ($errors as $error) { $el->addHtml( Html::el('span')->class('c-button -icon -error -tooltip js-form-tooltip-error') ->setAttribute('data-bs-toggle', 'tooltip') ->title($control->translate($error)) ->addHtml(Html::el('i', 'warning')->class('material-icons-round')) ); } // Customize label rendering as needed return $this->applyAttrs($el, $attrs); }
步骤 4:自定义按钮
对于按钮,您可以根据需要添加 Bootstrap 类和图标。像这样自定义按钮的渲染
protected function button(Button $control, array $attrs): Html { $el = $control->getControl(); $el->class ??= 'btn btn-primary'; // Add Bootstrap 4 classes $icon = $control->getOption('icon'); if (is_string($icon) && trim($icon) !== '') { $el->insert(0, Html::el('i')->class("fa fa-$icon")); // Add an icon if available } // Customize button rendering as needed return $this->applyAttrs($el, $attrs); }
步骤 5:自定义其他表单元素
根据您的所需样式,对复选框、单选按钮、选择框等其他表单元素重复类似的自定义操作。
步骤 6:实现额外样式
如果您的模板需要针对特定元素或表单组进行额外样式设置,您可以在扩展模板类中这样做。
步骤 7:应用您的自定义模板 要使用您的自定义模板,您需要实例化它并将其设置为渲染表单时的模板。例如
use Your\Namespace\Here\BootstrapFormTemplate; use Efabrica\NeoForms\Build\NeoForm; // Instantiate your custom template $template = new Bootstrap4FormTemplate(); // Create a NeoForm instance and set the custom template $form = new NeoForm(); $form->setTemplate($template); // Render your form echo $form;
如果您想为所有表单使用您的自定义模板,您可以通过重新连接自动连接将其设置为默认模板:)
# config.neon services: neoForms.template: Your\Namespace\Here\Bootstrap4FormTemplate()
按照以下步骤,您可以创建自己的扩展模板,以便以符合Bootstrap 4或您偏好的任何自定义样式的方式渲染表单。根据您的具体样式需求自定义模板方法。