Need help with yii2-dynamicform?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

wbraganca
408 Stars 386 Forks Other 49 Commits 227 Opened issues

Description

It is widget to yii2 framework to clone form elements in a nested manner, maintaining accessibility.

Services available

!
?

Need anything else?

Contributors list

# 88,631
CSS
Bootstr...
PHP
yii2-ex...
36 commits
# 284,672
JavaScr...
PHP
yii2-ex...
2 commits

yii2-dynamicform

Latest Version Software License Total Downloads

It is widget to yii2 framework to clone form elements in a nested manner, maintaining accessibility. yii2-dynamicform

Installation

The preferred way to install this extension is through composer.

Either run

php composer.phar require --prefer-dist wbraganca/yii2-dynamicform "*"

or add

"wbraganca/yii2-dynamicform": "*"

to the require section of your

composer.json
file.

Demos

Usage

Hypothetical Scenario

Database

The View

<?php $form = ActiveForm::begin(['id' => 'dynamic-form']); ?&gt;
<div class="row">
    <div class="col-sm-6">
        = $form-&gt;field($modelCustomer, 'first_name')-&gt;textInput(['maxlength' =&gt; true]) ?&gt;
    </div>
    <div class="col-sm-6">
        = $form-&gt;field($modelCustomer, 'last_name')-&gt;textInput(['maxlength' =&gt; true]) ?&gt;
    </div>
</div>

<div class="panel panel-default">
    <div class="panel-heading"><h4>

Addresses

<?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">
        <?php foreach ($modelsAddress as $i => $modelAddress): ?&gt;
            <div class="item panel panel-default">
                <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");
                        }
                    ?&gt;
                    = $form-&gt;field($modelAddress, "[{$i}]full_name")-&gt;textInput(['maxlength' =&gt; true]) ?&gt;
                    <div class="row">
                        <div class="col-sm-6">
                            = $form-&gt;field($modelAddress, "[{$i}]address_line1")-&gt;textInput(['maxlength' =&gt; true]) ?&gt;
                        </div>
                        <div class="col-sm-6">
                            = $form-&gt;field($modelAddress, "[{$i}]address_line2")-&gt;textInput(['maxlength' =&gt; true]) ?&gt;
                        </div>
                    </div>
                    <div class="row">
                        <div class="col-sm-4">
                            = $form-&gt;field($modelAddress, "[{$i}]city")-&gt;textInput(['maxlength' =&gt; true]) ?&gt;
                        </div>
                        <div class="col-sm-4">
                            = $form-&gt;field($modelAddress, "[{$i}]state")-&gt;textInput(['maxlength' =&gt; true]) ?&gt;
                        </div>
                        <div class="col-sm-4">
                            = $form-&gt;field($modelAddress, "[{$i}]postal_code")-&gt;textInput(['maxlength' =&gt; true]) ?&gt;
                        </div>
                    </div>
                </div>
            </div>
        <?php endforeach; ?>
        </div>
        <?php DynamicFormWidget::end(); ?>
    </div>
</div>

<div class="form-group">
    = Html::submitButton($modelAddress-&gt;isNewRecord ? 'Create' : 'Update', ['class' =&gt; 'btn btn-primary']) ?&gt;
</div>

<?php ActiveForm::end(); ?>

Javascript Events

$(".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"); });

The Controller (sample code)

use Yii;
use app\models\Customer;
use app\models\CustomerSearch;
use app\models\Address;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use app\base\Model;
use yii\web\Response;
use yii\widgets\ActiveForm;
use yii\helpers\ArrayHelper;

/**

  • CustomerController implements the CRUD actions for Customer model.

  • / class CustomerController extends Controller { ...

    /**

    • Creates a new Customer model.

    • If creation is successful, the browser will be redirected to the 'view' page.

    • @return mixed

    • / 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-&gt;request-&gt;post());
      
        // ajax validation
        if (Yii::$app-&gt;request-&gt;isAjax) {
            Yii::$app-&gt;response-&gt;format = Response::FORMAT_JSON;
            return ArrayHelper::merge(
                ActiveForm::validateMultiple($modelsAddress),
                ActiveForm::validate($modelCustomer)
            );
        }
      
        // validate all models
        $valid = $modelCustomer-&gt;validate();
        $valid = Model::validateMultiple($modelsAddress) &amp;&amp; $valid;
      
        if ($valid) {
            $transaction = \Yii::$app-&gt;db-&gt;beginTransaction();
            try {
                if ($flag = $modelCustomer-&gt;save(false)) {
                    foreach ($modelsAddress as $modelAddress) {
                        $modelAddress-&gt;customer_id = $modelCustomer-&gt;id;
                        if (! ($flag = $modelAddress-&gt;save(false))) {
                            $transaction-&gt;rollBack();
                            break;
                        }
                    }
                }
                if ($flag) {
                    $transaction-&gt;commit();
                    return $this-&gt;redirect(['view', 'id' =&gt; $modelCustomer-&gt;id]);
                }
            } catch (Exception $e) {
                $transaction-&gt;rollBack();
            }
        }

      }

      return $this->render('create', [

        'modelCustomer' =&gt; $modelCustomer,
        'modelsAddress' =&gt; (empty($modelsAddress)) ? [new Address] : $modelsAddress

      ]); }

      /**

    • Updates an existing Customer model.

    • If update is successful, the browser will be redirected to the 'view' page.

    • @param integer $id

    • @return mixed

    • / public function actionUpdate($id) { $modelCustomer = $this->findModel($id); $modelsAddress = $modelCustomer->addresses;

      if ($modelCustomer->load(Yii::$app->request->post())) {

        $oldIDs = ArrayHelper::map($modelsAddress, 'id', 'id');
        $modelsAddress = Model::createMultiple(Address::classname(), $modelsAddress);
        Model::loadMultiple($modelsAddress, Yii::$app-&gt;request-&gt;post());
        $deletedIDs = array_diff($oldIDs, array_filter(ArrayHelper::map($modelsAddress, 'id', 'id')));
      
        // ajax validation
        if (Yii::$app-&gt;request-&gt;isAjax) {
            Yii::$app-&gt;response-&gt;format = Response::FORMAT_JSON;
            return ArrayHelper::merge(
                ActiveForm::validateMultiple($modelsAddress),
                ActiveForm::validate($modelCustomer)
            );
        }
      
        // validate all models
        $valid = $modelCustomer-&gt;validate();
        $valid = Model::validateMultiple($modelsAddress) &amp;&amp; $valid;
      
        if ($valid) {
            $transaction = \Yii::$app-&gt;db-&gt;beginTransaction();
            try {
                if ($flag = $modelCustomer-&gt;save(false)) {
                    if (! empty($deletedIDs)) {
                        Address::deleteAll(['id' =&gt; $deletedIDs]);
                    }
                    foreach ($modelsAddress as $modelAddress) {
                        $modelAddress-&gt;customer_id = $modelCustomer-&gt;id;
                        if (! ($flag = $modelAddress-&gt;save(false))) {
                            $transaction-&gt;rollBack();
                            break;
                        }
                    }
                }
                if ($flag) {
                    $transaction-&gt;commit();
                    return $this-&gt;redirect(['view', 'id' =&gt; $modelCustomer-&gt;id]);
                }
            } catch (Exception $e) {
                $transaction-&gt;rollBack();
            }
        }

      }

      return $this->render('update', [

        'modelCustomer' =&gt; $modelCustomer,
        'modelsAddress' =&gt; (empty($modelsAddress)) ? [new Address] : $modelsAddress

      ]); }

      ... }

Model Class


use Yii; use yii\helpers\ArrayHelper;

class Model extends \yii\base\Model { /** * Creates and populates a set of models. * * @param string $modelClass * @param array $multipleModels * @return array */ public static function createMultiple($modelClass, $multipleModels = []) { $model = new $modelClass; $formName = $model->formName(); $post = Yii::$app->request->post($formName); $models = [];

    if (! empty($multipleModels)) {
        $keys = array_keys(ArrayHelper::map($multipleModels, 'id', 'id'));
        $multipleModels = array_combine($keys, $multipleModels);
    }

    if ($post &amp;&amp; is_array($post)) {
        foreach ($post as $i =&gt; $item) {
            if (isset($item['id']) &amp;&amp; !empty($item['id']) &amp;&amp; isset($multipleModels[$item['id']])) {
                $models[] = $multipleModels[$item['id']];
            } else {
                $models[] = new $modelClass;
            }
        }
    }

    unset($model, $formName, $post);

    return $models;
}

}

To zero or more elements (use the following code in your view file)

<?php $form = ActiveForm::begin(['id' => 'dynamic-form']); ?&gt;
<div class="row">
    <div class="col-sm-6">
        = $form-&gt;field($modelCustomer, 'first_name')-&gt;textInput(['maxlength' =&gt; true]) ?&gt;
    </div>
    <div class="col-sm-6">
        = $form-&gt;field($modelCustomer, 'last_name')-&gt;textInput(['maxlength' =&gt; true]) ?&gt;
    </div>
</div>

<?php DynamicFormWidget::begin([
    'widgetContainer' => 'dynamicform_wrapper', // required: only alphanumeric characters plus "_" [A-Za-z0-9_]
    'widgetBody' =&gt; '.container-items', // required: css class selector
    'widgetItem' =&gt; '.item', // required: css class
    'limit' =&gt; 4, // the maximum times, an element can be added (default 999)
    'min' =&gt; 0, // 0 or 1 (default 1)
    'insertButton' =&gt; '.add-item', // css class
    'deleteButton' =&gt; '.remove-item', // css class
    'model' =&gt; $modelsAddress[0],
    'formId' =&gt; 'dynamic-form',
    'formFields' =&gt; [
        'full_name',
        'address_line1',
        'address_line2',
        'city',
        'state',
        'postal_code',
    ],
]); ?&gt;

<div class="panel panel-default">
    <div class="panel-heading">
        <h4>
            <i class="glyphicon glyphicon-envelope"></i> Addresses
            <button type="button" class="add-item btn btn-success btn-sm pull-right"><i class="glyphicon glyphicon-plus"></i> Add</button>
        </h4>
    </div>
    <div class="panel-body">
        <div class="container-items">
        <?php foreach ($modelsAddress as $i => $modelAddress): ?&gt;
            <div class="item panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title pull-left">Address</h3>
                    <div class="pull-right">
                        <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");
                        }
                    ?&gt;
                    = $form-&gt;field($modelAddress, "[{$i}]full_name")-&gt;textInput(['maxlength' =&gt; true]) ?&gt;
                    <div class="row">
                        <div class="col-sm-6">
                            = $form-&gt;field($modelAddress, "[{$i}]address_line1")-&gt;textInput(['maxlength' =&gt; true]) ?&gt;
                        </div>
                        <div class="col-sm-6">
                            = $form-&gt;field($modelAddress, "[{$i}]address_line2")-&gt;textInput(['maxlength' =&gt; true]) ?&gt;
                        </div>
                    </div>
                    <div class="row">
                        <div class="col-sm-4">
                            = $form-&gt;field($modelAddress, "[{$i}]city")-&gt;textInput(['maxlength' =&gt; true]) ?&gt;
                        </div>
                        <div class="col-sm-4">
                            = $form-&gt;field($modelAddress, "[{$i}]state")-&gt;textInput(['maxlength' =&gt; true]) ?&gt;
                        </div>
                        <div class="col-sm-4">
                            = $form-&gt;field($modelAddress, "[{$i}]postal_code")-&gt;textInput(['maxlength' =&gt; true]) ?&gt;
                        </div>
                    </div>
                </div>
            </div>
        <?php endforeach; ?>
        </div>
    </div>
</div>
<?php DynamicFormWidget::end(); ?>

<div class="form-group">
    = Html::submitButton($modelAddress-&gt;isNewRecord ? 'Create' : 'Update', ['class' =&gt; 'btn btn-primary']) ?&gt;
</div>

<?php ActiveForm::end(); ?>

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.