caio-brendo/yii2-dynamicgridform

Dynamic Grid Form is a widget for the yii2 structure to add values to a grid. It is very useful for working with one-to-many data.

Installs: 658

Dependents: 0

Suggesters: 0

Security: 0

Stars: 2

Watchers: 1

Forks: 2

Open Issues: 4

Language:JavaScript

Type:yii2-extension

v1.6.6 2023-12-05 18:58 UTC

README

Dynamic Grid Form


Dynamic Grid Form is a widget for the yii2 structure to add values to a grid. It is very useful for working with one-to-many data.

Latest Stable Version

Total Downloads

DIRECTORY STRUCTURE

  src/             contains source code of widget
  src/assets       contains assets definition
  src/views        contains view files

REQUIREMENTS

The minimum requirement by this project template that your Web server supports PHP 5.6.0.

INSTALLATION

Install via Composer

If you do not have Composer, you may install it by following the instructions at getcomposer.org.

You can then install this widget using the following command:

composer require caio-brendo/yii2-dynamicgridform

Release Changes

NOTE: Refer the CHANGE LOG for details on changes to various releases.

Bugs fixed with release v1.6.2:

  • Inputs aren't reordering the index

PREVIEW

Alt Text

USAGE

The view

You must have inputs to your values will be appended in the grid and references in the DynamicGridForm through the input id. Replace the "multipleModels" by your array of objects that contains the values and "modelClass" by a string that refence your model. Each column corresponds to an entry and must contain the attribute in the model that this column references. The following code renders the DynamicGridForm used in preview.

<?php 
use app\models\Telephone;
use caiobrendo\dynamicgridform\ActionColumn;
use caiobrendo\dynamicgridform\DynamicGridForm;
use caiobrendo\dynamicgridform\NormalColumn;
use yii\helpers\Html;
use yii\web\JsExpression;
use yii\widgets\ActiveForm;

/* @var $this yii\web\View */
/* @var $form yii\widgets\ActiveForm */
/* @var $telephones Telephone[] */
?>
<div class="row">
         <!-- Necessary for update -->
         <?= Html::hiddenInput('id', '', ['id' => 'telephone-id']) ?>
        <div class="col-md-2">
            <div class="form-group">
                <label class="control-label" for="telephone-ddi">DDI</label>
                <?= Html::dropDownList('ddi', null, [55 => 'Brasil', 213 => 'Argentina'], [
                    'id' => 'telephone-ddi',
                    'class' => 'form-control',
                    'prompt' => 'Selecione...'
                ]) ?>
            </div>

        </div>
        <div class="col-md-2">
            <div class="form-group">
                <label class="control-label" for="telephone-ddi">DDI</label>
                <?= Html::dropDownList('ddi', null, [61 => 'Brasilia', 62 => 'Goias'], [
                    'id' => 'telephone-ddd',
                    'class' => 'form-control',
                    'prompt' => 'Selecione...'
                ]) ?>
            </div>
        </div>
        <div class="col-md-2">
            <div class="form-group">
                <label class="control-label" for="telephone-number">Number</label>
                <?= Html::textInput('number', null, [
                    'id' => 'telephone-number',
                    'class' => 'form-control'
                ]) ?>
            </div>
        </div>
        <div class="col-md-2">
            <div class="form-group">
                <label class="control-label" for="telephone-type">Type</label>
                <?= Html::textInput('type', null, [
                    'id' => 'telephone-type',
                    'class' => 'form-control'
                ]) ?>
            </div>
        </div>
    </div>
    <div class="row">
        <div class="col-md-12">
            <?= Html::button('<i class="glyphicon glyphicon-plus"></i> Adicionar', [
                'id' => 'click',
                'class' => 'pull-leftbtn btn-primary btn-sm'
            ]) ?>
        </div>
    </div>
    <div class="row">
        <?= DynamicGridForm::widget([
            'widgetContainer' => 'dgf-container',
            'columns' => [
                [
                    'class' => NormalColumn::class,
                    'id' => 'telephone-id',
                    'attribute' => 'id',
                    'options' => [
                        'class' => 'hide'
                    ],
                    'headerOptions' => [
                        'class' => 'hide' #Hide column
                    ]
                ],
                [
                    'id' => 'telephone-ddi',
                    'attribute' => 'ddi',
                    'headerOptions' => [
                            'width' => 20
                    ]
                ],
                [
                    'id' => 'telephone-ddd',
                    'attribute' => 'ddd',
                    'headerOptions' => [
                        'width' => 20
                    ]
                ],
                [
                    'id' => 'telephone-number',
                    'attribute' => 'number',
                    'headerOptions' => [
                        'width' => 20
                    ]
                ],
                [
                    'id' => 'telephone-type',
                    'attribute' => 'type',
                    'headerOptions' => [
                        'width' => 20
                    ]
                ],
                [
                    'class' => ActionColumn::class,
                    'template' => '{delete}',
                    'buttonsClient' => [
                        'delete' => '<button type="button" class="pull-left btn btn-danger btn-xs delete"><i class="glyphicon glyphicon-minus"></i></button>'
                    ],
                    'buttons' => [
                        'delete' => '<button type="button" class="pull-left btn btn-danger btn-xs delete"><i class="glyphicon glyphicon-minus"></i></button>'
                    ],
                    'headerOptions' => [
                        'width' => 20
                    ]
                ]
            ],
            'insertButton' => 'click',
            'multipleModels' => $telephones,
            'modelClass' => Telephone::class,
            'deleteRowClass' => 'delete'
        ]);
        ?>
    </div>

The controller

At the controller, you will receive the values attached to the grid when submitting the form. The values will be in the following format:

<?php
 [
            'Telephone' => [
                0 => [
                    'ddi' => '55',
                    'ddd' => '61',
                    'number' => '999999999',
                    'type' => 'Cel'
                ],
                1 => [
                    0 => [
                        'ddi' => '55',
                        'ddd' => '62',
                        'number' => '88888888',
                        'type' => 'Tel'
                    ],
                ]
            ]
        ];
?>

Then with the following code you can save the data.

<?php

namespace app\controllers;

use app\models\Model;
use app\models\Telephone;
use app\models\User;
use caiobrendo\dynamicgridform\Helper;
use Yii;
use yii\filters\VerbFilter;
use yii\helpers\ArrayHelper;
use yii\web\Controller;
use yii\web\NotFoundHttpException;

/**
 * UserController implements the CRUD actions for User model.
 */
class UserController extends Controller
{
    /**
     * {@inheritdoc}
     */
    public function behaviors()
    {
        return [
            'verbs' => [
                'class' => VerbFilter::className(),
                'actions' => [
                    'delete' => ['POST'],
                ],
            ],
        ];
    }

    /**
     * Creates a new User model.
     * If creation is successful, the browser will be redirected to the 'view' page.
     * @return mixed
     */
    public function actionCreate()
    {
        $model = new User();
        $post = Yii::$app->request->post();
        $telephones = Helper::createMultiple(Telephone::class);
        if ($model->load($post) && Model::loadMultiple($telephones, $post) && $model->save()) {
            foreach ($telephones as $telephone) {
                $telephone->user_id = $model->id;
                if (!$telephone->save()) {
                    return false;
                }
            }
            return $this->redirect(['update', 'id' => $model->id]);
        }

        return $this->render('create', [
            'model' => $model,
            'telephones' => $telephones
        ]);
    }

    /**
     * Updates an existing User model.
     * If update is successful, the browser will be redirected to the 'view' page.
     * @param integer $id
     * @return mixed
     * @throws NotFoundHttpException if the model cannot be found
     */
    public function actionUpdate($id)
    {
        $model = $this->findModel($id);
        $telephones = $model->telephones;
        $post = Yii::$app->request->post();
        if ($model->load($post) && $model->save()) {
            $telephones = Helper::createMultiple(Telephone::class, $telephones);
            Model::loadMultiple($telephones, $post);
            $olds = ArrayHelper::map($model->telephones, 'id', 'id');
            $news = ArrayHelper::map($telephones, 'id', 'id');
            $delete = array_diff($olds, $news);
            Telephone::deleteAll(['id' => $delete]);

            foreach ($telephones as $telephone) {
                $telephone->user_id = $model->id;
                if (!$telephone->save()) {
                    return false;
                }
            }
            return $this->redirect(['update', 'id' => $model->id]);
        }

        return $this->render('update', [
            'model' => $model,
            'telephones' => $telephones
        ]);
    }

    /**
     * Finds the User model based on its primary key value.
     * If the model is not found, a 404 HTTP exception will be thrown.
     * @param integer $id
     * @return User the loaded model
     * @throws NotFoundHttpException if the model cannot be found
     */
    protected function findModel($id)
    {
        if (($model = User::findOne($id)) !== null) {
            return $model;
        }

        throw new NotFoundHttpException('The requested page does not exist.');
    }
}
?>

SETTINGS

The widget supports all parameters that one would pass for any Yii Input Widget. The additional parameter settings specially available for the DynamicGridForm widget configuration are:

  • columns: An array with column settings. Currently, we have two settings for columns: NormalColumn and ActionColumn. For NormalColumn the parameters accept are:

    • headerOptions: An array with the options of header. You can see the options allowed here.
    • options: An array with the options of content. You can see the options allowed here.
    • id: A string that reference an input id. Since of v1.2.0 this param is optional and you can have a column without referenced input
    • attribute: A string that referece an attribute of the model informed. This param is required
    • value: An string or clousure that will return the value of input hidden when the widget is rendered. When clousure is informed then the signature must be:
    <?php 
    //...
    DynamicGridForm::widget([
      'columns' => [
          [
              'id' => 'telephone-ddi',
              'attribute' => 'ddi',
              'headerOptions' => [
                  'width' => 20
              ],
              'value' => static function($model, $index){
                  return $model->ddi;
              }                   
          ],
        //...
      ]
    ]);
    //...
    ?>
    • valueOnInsert: An string or an object of the class JsExpression that will return the value of input hidden when the new row is added. Since of v1.2.0 this param is required when the param 'id' is not informed. When object of the class JsExpression is informed then the signature must be:
    <?php 
    use yii\web\JsExpression;
    //...
    DynamicGridForm::widget([
      'columns' => [
          [
              'id' => 'telephone-ddi',
              'attribute' => 'ddi',
              'headerOptions' => [
                  'width' => 20
              ],
              'valueOnInsert' => new JsExpression('(input) => {console.log(input);return $(input).val()}')
          ],
        //...
      ]
    ]);
    //...
    ?>
    • text: An string or clousure that will return the text that will shown in the grid when the widget is rendered. When clousure is informed then the signature must be:
    <?php 
    //...
    DynamicGridForm::widget([
      'columns' => [
          [
              'id' => 'telephone-ddi',
              'attribute' => 'ddi',
              'headerOptions' => [
                  'width' => 20
              ],
              'text' => static function($model, $key, $index, $name){
                  return $model->ddi;
              }                   
          ],
        //...
      ]
    ]);
    //...
    ?>
    • textOnInsert: An string or clousure that will return the text that will shown in the grid when the new row is added. Since of v1.2.0 this param is required when the param 'id' is not informed. When object of the class JsExpression is informed then the signature must be:
    <?php 
    use yii\web\JsExpression;
    //...
    DynamicGridForm::widget([
      'columns' => [
          [
              'id' => 'telephone-ddi',
              'attribute' => 'ddi',
              'headerOptions' => [
                  'width' => 20
              ],
              'textOnInsert' => new JsExpression('(input, index, name) => {console.log(input);return $(input).val()}')
          ],
        //...
      ]
    ]);
    //...
    ?>
    • cleanAfterInsert: A boolean param. If true after insert a line in the grid the input will be cleared.
    • header: A string to inform the column header in the grid.
    • showHiddenInput: A boolean param, when not informed is default true. If true the hidden input is shown
  • columns: For ActionColumn the parameters accept are:

    • headerOptions: An array with the options of header. You can see the options allowed here.
    • options: An array with the options of content. You can see the options allowed here.
    • header: A string to inform the column header in the grid.
    • template: A string with the template of buttons. Ex.: {delete} {dowload} {view}.
    • buttons: An array with the buttons that will be rendered when the widget is rendered. The value of this array can be a string or a clousure The follow code render a button to delete a line:
    <?php 
    use yii\web\JsExpression;
    use caiobrendo\dynamicgridform\ActionColumn;
    //...
    DynamicGridForm::widget([
      'columns' => [
          [
              'class' => ActionColumn::class,
              'template' => '{delete}',
    //...           
              'buttons' => [
                  'delete' => static function($model,$index){
                      return '<button type="button" class="pull-left btn btn-danger btn-xs delete"><i class="glyphicon glyphicon-minus"></i></button>';
                  }
              ],
    //...
          ]
        //...
      ]
    ]);
    //...
    ?>
    • buttonsClient: An array with the buttons that will be rendered when a new line is inserted. The value of this array can be a string or an object of the class JsExpressoin. The follow code render a button to delete a line:
      <?php 
      use yii\web\JsExpression;
      use caiobrendo\dynamicgridform\ActionColumn;
      //...
      DynamicGridForm::widget([
          'columns' => [
              [
                  'class' => ActionColumn::class,
                  'template' => '{delete}',
      //...           
                  'buttonsClient' => [
                      'delete' => new JsExpression('(values, index) => {console.log(values); return \'<button type="button" class="pull-left btn btn-danger btn-xs delete"><i class="glyphicon glyphicon-minus"></i></button>\'}')
                  ],
      //...
              ]
            //...
          ]
      ]);
      //...
      ?>
    • visibleButtons: An array that inform if the button is visible when the widget is rendered. The value of this array can be a boolean or a clousure. The follow code show of a button:
      <?php 
        use yii\web\JsExpression;
        use caiobrendo\dynamicgridform\ActionColumn;
        //...
        DynamicGridForm::widget([
            'columns' => [
                [
                    'class' => ActionColumn::class,
                    'template' => '{delete}',
        //...           
                    'visibleButtons' => [
                        'delete' => static function($model,$index){
                            return 1 === 1; 
                        }
                    ],
        //...
                ]
              //...
            ]
        ]);
        //...
        ?> 
    • visibleButtonsClient: An array that inform if the button is visible when a new row is inserted. The value of this array can be a boolean or an object of the class JsExpression. The follow code show of a button:
    <?php 
      use yii\web\JsExpression;
      use caiobrendo\dynamicgridform\ActionColumn;
      //...
      DynamicGridForm::widget([
          'columns' => [
              [
                  'class' => ActionColumn::class,
                  'template' => '{delete}',
      //...           
                  'visibleButtonsClient' => [
                      'delete' => new JsExpression('(values, key) => {console.log(values); return true;}')
                  ],
      //...
              ]
            //...
          ]
      ]);
      //...
      ?> 
  • insertButton: A string to inform the id of the button that will add the data in the grid. This param is required.

  • multipleModels: An array that contains your multiples models. This parma is required.

  • widgetContainer: A string to enter a widget container id.

  • max: A number that limits the number of lines inserted. When 0 is informed then will be unlimited. Default 0.

  • insertPosition: A string (bottom or top) to inform where the new line will be inserted. Default "bottom"

  • allowEdit: A boolean param. When true you can edit the content of the line clicked.

  • rowOptions: An array with the options of the row. You can see the options allowed here.

  • $headerRowOptions: An array with the options of the header row. You can see the options allowed here.

  • deleteRowClass: An string to inform a class that will be used to drop a line.

  • modelClass: An string to inform the class that is used in DynamicGridForm

  • variableJsName: Since of v1.5.0 you can specify the name of global variable that represents the Dynamic Grid From object javascript, so the instance of class Dynamic GridForm (js) will set with the string informed

JAVASCRIPT MANIPULATIONS

  • Since of v1.5.0 you can access the instance of class that represents the DynamicGridForm (js) through the following code.
window[$("#tableId").attr('data-object')]
  • If you informed the property "variableJsName" you can access directly with the name specified in the variable.
window['{stringInformedInThevariableJsName}']
  • You can get the all values inserted in the grid with the follow code:
await window[$("#tableId").attr('data-object')].getAllDataTable()

JAVASCRIPT EVENTS

/* 
* This event is dispatched before insert a new row in the grid. 
* If this event return false then a new row not will be inserted.
* With this event you can valid if your fields is ok then insert a new row.
* Since of v1.0.3 you can use the param valuesInserted to recovery all values 
* already inserted and get the current instance of dynamicgridform by variable
* dynamicGridForm
*/
$('#dgf-container').on('beforeInsert', (event, values, valuesInserted, dynamicGridForm) => {
    console.log('beforeInsert', event, values, valuesInserted, dynamicGridForm);
});

/* This event is dispatched after insert a new row in the grid.*/
$('#dgf-container').on('afterInsert', (event, item) => {
    console.log('afterInsert', event, item);
});

/* This event is dispatched when limit is reached.*/
$('#dgf-container').on('limitReached', (event) => {
    console.log('limitReached', event);
});

/* This event is dispatched before update a new row in the grid. */
$('#dgf-container').on('beforeUpdate', (event, values) => {
    console.log('beforeUpdate', values);
});

/* This event is dispatched after update a new row in the grid.*/
$('#dgf-container').on('afterUpdate', (event, item) => {
    console.log('afterUpdate', item);
});

/* This event is dispatched before delete a row in the grid. */
$('#dgf-container').on('beforeDelete', (event, item, dynamicGridForm) => {
  console.log('beforeDelete', event, item, dynamicGridForm);
});

/* This event is dispatched after update a new row in the grid.*/
$('#dgf-container').on('afterDelete', (event, item, dynamicGridForm) => {
  console.log('afterDelete', event, item, dynamicGridForm);
});