yii2-extensions / dynamicform
Yii2 动态表单小部件,以嵌套方式克隆表单元素,同时保持可访问性。
1.0.1
2024-08-19 17:49 UTC
Requires
- symfony/css-selector: ^6.4||^7.0
- symfony/dom-crawler: ^6.4||^7.0
- yiisoft/yii2: ^2.0.49
This package is auto-updated.
Last update: 2024-09-19 18:03:42 UTC
README
这是一个用于yii2框架的Widget,可以以嵌套方式克隆表单元素,同时保持可访问性。
安装
安装此扩展的首选方式是通过 composer。
运行
composer require --prefer-dist yii2-extensions/dynamicform:"^1.0.0"
或添加
"yii2-extensions/dynamicform": "^1.0.0"
到您的 composer.json
文件的 require 部分。
扩展使用
数据库
为了解释此扩展的使用,我们将有一个示例场景,其中我们正在为客户构建通讯录。每位客户可以有多个地址。下图中提供了更多详细信息。
模型
根据该数据库,我们假设您有两个模型 Customer
和 Address
类。
<?php namespace app\models; class Customer extends ActiveRecord { public static function tableName() { return 'customer'; } //.... //normal Yii AR stuffs }
<?php namespace app\models; class Address extends ActiveRecord { public static function tableName() { return 'address'; } //.... //normal Yii AR stuffs }
控制器
1. 创建操作
<?php namespace app\controllers; use Yii2\Extensions\DynamicForm\Models\Model; //Very important. Do not mix this with yii\base\Model class CustomerController extends Controller { public function actionCreate() { $modelCustomer = new Customer; $modelsAddress = [new Address]; if ($modelCustomer->load(Yii::$app->request->post())) { $modelsAddress = Model::createMultiple(Address::classname()); Model::loadMultiple($modelsAddress, Yii::$app->request->post()); // ajax validation if (Yii::$app->request->isAjax) { Yii::$app->response->format = Response::FORMAT_JSON; return ArrayHelper::merge( ActiveForm::validateMultiple($modelsAddress), ActiveForm::validate($modelCustomer) ); } // validate all models $valid = $modelCustomer->validate(); $valid = Model::validateMultiple($modelsAddress) && $valid; if ($valid) { $transaction = \Yii::$app->db->beginTransaction(); try { if ($flag = $modelCustomer->save(false)) { foreach ($modelsAddress as $modelAddress) { $modelAddress->customer_id = $modelCustomer->id; if (! ($flag = $modelAddress->save(false))) { $transaction->rollBack(); break; } } } if ($flag) { $transaction->commit(); return $this->redirect(['view', 'id' => $modelCustomer->id]); } } catch (Exception $e) { $transaction->rollBack(); } } } return $this->render('create', [ 'modelCustomer' => $modelCustomer, 'modelsAddress' => (empty($modelsAddress)) ? [new Address] : $modelsAddress ]); } }
2. 更新操作
<?php namespace app\controllers; use Yii2\Extensions\DynamicForm\Models\Model; //Very important. Do not mix this with yii\base\Model class CustomerController extends Controller { public function actionUpdate($id) { $modelCustomer = $this->findModel($id); $modelsAddress = $modelCustomer->addresses; // primary key of $modelsAddress $pkey = 'address_id'; if ($modelCustomer->load(Yii::$app->request->post())) { $oldIDs = ArrayHelper::map($modelsAddress, $pkey, $pkey); $modelsAddress = Model::createMultiple(Address::classname(), $modelsAddress, $pkey); Model::loadMultiple($modelsAddress, Yii::$app->request->post()); $deletedIDs = array_diff($oldIDs, array_filter(ArrayHelper::map($modelsAddress, $pkey, $pkey))); // ajax validation if (Yii::$app->request->isAjax) { Yii::$app->response->format = Response::FORMAT_JSON; return ArrayHelper::merge( ActiveForm::validateMultiple($modelsAddress), ActiveForm::validate($modelCustomer) ); } // validate all models $valid = $modelCustomer->validate(); $valid = Model::validateMultiple($modelsAddress) && $valid; if ($valid) { $transaction = \Yii::$app->db->beginTransaction(); try { if ($flag = $modelCustomer->save(false)) { if (! empty($deletedIDs)) { Address::deleteAll([$pkey => $deletedIDs]); } foreach ($modelsAddress as $modelAddress) { $modelAddress->customer_id = $modelCustomer->id; if (! ($flag = $modelAddress->save(false))) { $transaction->rollBack(); break; } } } if ($flag) { $transaction->commit(); return $this->redirect(['view', 'id' => $modelCustomer->id]); } } catch (Exception $e) { $transaction->rollBack(); } } } return $this->render('update', [ 'modelCustomer' => $modelCustomer, 'modelsAddress' => (empty($modelsAddress)) ? [new Address] : $modelsAddress ]); } }
视图
视图展示了一个复杂的表单,可以动态添加或删除项。其核心是 DynamicFormWidget
以下是一些关于小部件属性的详细信息
widgetContainer
:小部件的顶级容器。只能包含字母数字以及一个_
字符。这是必需的widgetBody
:承载表单元素行的容器。其值必须符合CSS类。这是必需的widgetItem
:表示单行表单行。如果您习惯于Bootstrap网格,则widgetBody
类似于容器,而widgetItem
类似于行。这是一个必需元素,并且必须以CSS类的格式表示limit
:最大克隆次数。它是一个整数。限制元素可以被克隆的次数。默认为999。min
:默认元素的最小数量。如果要为子表单元素留空,则将其设置为0;如果要从一个单行开始,则将其设置为1。默认为1。insertButton
:点击将添加表单行的元素的CSS类名称。deleteButton
:点击将删除表单行的元素的CSS类名称。model
:小部件的示例模型。如果您不确定,请传递模型行的第一个元素。这要求您的控制器始终向视图发送至少一个模型。formId
:ActiveForm
的ID。不匹配这两个ID将是一场灾难。请小心!
示例视图
<?php use yii\helpers\Html; use yii\widgets\ActiveForm; use Yii2\Extensions\DynamicForm\DynamicFormWidget; ?> <div class="customer-form"> <?php $form = ActiveForm::begin(['id' => 'dynamic-form']); ?> <div class="row"> <div class="col-sm-6"> <?= $form->field($modelCustomer, 'first_name')->textInput(['maxlength' => true]) ?> </div> <div class="col-sm-6"> <?= $form->field($modelCustomer, 'last_name')->textInput(['maxlength' => true]) ?> </div> </div> <div class="panel panel-default"> <div class="panel-heading"><h4><i class="glyphicon glyphicon-envelope"></i> Addresses</h4></div> <div class="panel-body"> <?php DynamicFormWidget::begin([ 'widgetContainer' => 'dynamicform_wrapper', // required: only alphanumeric characters plus "_" [A-Za-z0-9_] 'widgetBody' => '.container-items', // required: css class selector 'widgetItem' => '.item', // required: css class. 'limit' => 4, // the maximum times, an element can be cloned (default 999) 'min' => 1, // 0 or 1 (default 1) 'insertButton' => '.add-item', // css class 'deleteButton' => '.remove-item', // css class 'model' => $modelsAddress[0], 'formId' => 'dynamic-form', 'formFields' => [ 'full_name', 'address_line1', 'address_line2', 'city', 'state', 'postal_code', ], ]); ?> <div class="container-items"><!-- widgetContainer --> <?php foreach ($modelsAddress as $i => $modelAddress): ?> <div class="item panel panel-default"><!-- widgetBody --> <div class="panel-heading"> <h3 class="panel-title pull-left">Address</h3> <div class="pull-right"> <button type="button" class="add-item btn btn-success btn-xs"><i class="glyphicon glyphicon-plus"></i></button> <button type="button" class="remove-item btn btn-danger btn-xs"><i class="glyphicon glyphicon-minus"></i></button> </div> <div class="clearfix"></div> </div> <div class="panel-body"> <?php // necessary for update action. if (! $modelAddress->isNewRecord) { echo Html::activeHiddenInput($modelAddress, "[{$i}]id"); } ?> <?= $form->field($modelAddress, "[{$i}]full_name")->textInput(['maxlength' => true]) ?> <div class="row"> <div class="col-sm-6"> <?= $form->field($modelAddress, "[{$i}]address_line1")->textInput(['maxlength' => true]) ?> </div> <div class="col-sm-6"> <?= $form->field($modelAddress, "[{$i}]address_line2")->textInput(['maxlength' => true]) ?> </div> </div><!-- .row --> <div class="row"> <div class="col-sm-4"> <?= $form->field($modelAddress, "[{$i}]city")->textInput(['maxlength' => true]) ?> </div> <div class="col-sm-4"> <?= $form->field($modelAddress, "[{$i}]state")->textInput(['maxlength' => true]) ?> </div> <div class="col-sm-4"> <?= $form->field($modelAddress, "[{$i}]postal_code")->textInput(['maxlength' => true]) ?> </div> </div><!-- .row --> </div> </div> <?php endforeach; ?> </div> <?php DynamicFormWidget::end(); ?> </div> </div> <div class="form-group"> <?= Html::submitButton($modelCustomer->isNewRecord ? 'Create' : 'Update', ['class' => 'btn btn-primary']) ?> </div> <?php ActiveForm::end(); ?> </div>
JavaScript事件
$(".dynamicform_wrapper").on("beforeInsert", function(e, item) { console.log("beforeInsert"); }); $(".dynamicform_wrapper").on("afterInsert", function(e, item) { console.log("afterInsert"); }); $(".dynamicform_wrapper").on("beforeDelete", function(e, item) { if (! confirm("Are you sure you want to delete this item?")) { return false; } return true; }); $(".dynamicform_wrapper").on("afterDelete", function(e) { console.log("Deleted item!"); }); $(".dynamicform_wrapper").on("limitReached", function(e, item) { alert("Limit reached"); });
特别感谢
特别感谢 Wanderson Bragança 创建了这个出色的扩展。我们在这里确保他的优秀工作不会消失。我们正在尽我们最大的努力在整个代码中保留他的身份。
谢谢 Wanderson!
在 https://github.com/wbraganca/yii2-dynamicform 查看原始工作
问题和贡献
欢迎以PR的形式贡献。请向我们的github仓库提交pull request。您可以通过github issues提出问题、建议功能或报告错误。
如果您发现它们有用,请为我们评分。
如果您使用的是 X,请别忘了在我们这里联系我们:通过 @yiiframework 获取 Yii 框架信息,通过 @yiiupdates 获取 Yii 新闻和更新