yii2-extensions/dynamicform

Yii2 动态表单小部件,以嵌套方式克隆表单元素,同时保持可访问性。

安装: 270

依赖项: 0

建议者: 0

安全: 0

星标: 3

关注者: 2

分支: 1

公开问题: 0

语言:JavaScript

类型:yii2-extension

1.0.1 2024-08-19 17:49 UTC

This package is auto-updated.

Last update: 2024-09-19 18:03:42 UTC


README

Latest Version Software License Total Downloads

这是一个用于yii2框架的Widget,可以以嵌套方式克隆表单元素,同时保持可访问性。 yii2-dynamicform

安装

安装此扩展的首选方式是通过 composer

运行

composer require --prefer-dist yii2-extensions/dynamicform:"^1.0.0"

或添加

"yii2-extensions/dynamicform": "^1.0.0"

到您的 composer.json 文件的 require 部分。

扩展使用

数据库

为了解释此扩展的使用,我们将有一个示例场景,其中我们正在为客户构建通讯录。每位客户可以有多个地址。下图中提供了更多详细信息。

Database

模型

根据该数据库,我们假设您有两个模型 CustomerAddress 类。

<?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:小部件的示例模型。如果您不确定,请传递模型行的第一个元素。这要求您的控制器始终向视图发送至少一个模型。
  • formIdActiveForm 的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 新闻和更新