dynamictabularform

Allows Dynamic Tabular Input using partial views
17 followers

Introduction

this extension allows us to create dynamic tabular inputs using partial views and adding more of it via Ajax/JS this also allows each row to have its own widgets like CJui. i will also teach how to save 1 header model + multiple related model as details in this article

Installation

  • extract the folder in protected/extensions/dynamictabularform

  • add this in your import array in the main.php

'ext.dynamictabularform.*',
  • done!

Screenshot

tabular

Requirements

Yii 1.1 or above

Usage

This extension uses a CAction to load each row of the tabular input via ajax. basic usage with controller

we need to add my custom action in the actions() method don't forget to allow the action in the accessRules()

DynamicTabularForm widget parameters:

  • rowUrl: the url of the ajax renderer

  • defaultRowView: the default view file for the row

DynamicTabularForm widget methods:

public function rowForm($models = array(), $rowView=null, $htmlOptions = array())

Parameters

  • $models: is the array of CModels to be used

  • $rowView: the view file to be used for initial rendering if $models contains data already

public function deleteRowButton($row_id, $key, $label='X', $htmlOptions = array())

Parameters

  • $row_id: the id of the div of the current row(see usage)

  • $key: the key of the current row

  • $label: label of the delete button

public function updateTypeField($model, $key, $attribute, $htmlOptions = array())

for example i have created 3 rows and saved it in my database, then i wanted to update it and i want to delete 1 row of entry. this is important to determine what fields are to be deleted. this is usually useful to determine what rows are for new entries, for deletion and for updating

Parameters

  • $model: model of the row

  • $key: the key of the current row

  • $attribute: to what attribute we will bind on what kind of update we will do if creation, deletion of updating

GetRowForm action parameters

  • view: the view file that it will renderPartial

  • modelClass: the class of the model that will be initialized and be passed to the partial view defined in view. this is passed as the variabe $model

Variables available for the partial view

  • $model: the initialized modelClass

  • $form: just an initialized DynamicTabularForm

  • $key: the counter that you can use in naming the inputs and the row

Example

Controller - SlaController.php

class SlaController extends Controller {
 
    public function actions() {
        return array(
            'getRowForm' => array(
                'class' => 'ext.dynamictabularform.actions.GetRowForm',
                'view' => '_rowForm',
                'modelClass' => 'SlaDetail'
            ),
        );
    }
 
    /**
     * without relation extension
     */
    public function actionCreate() {
        /**
         * a typical setup... SLA is my header and its details is the SlaDetail model
         * this i like a regular receipt
         */
        $sla = new Sla();
        $sladetails = array(new SlaDetail);
 
        if (isset($_POST['Sla'])) {
            $sla->attributes = $_POST['Sla'];
 
            /**
             * creating an array of sladetail objects
             */
            if (isset($_POST['SlaDetail'])) {
                $sladetails = array();
                foreach ($_POST['SlaDetail'] as $key => $value) {
                    /*
                     * sladetail needs a scenario wherein the fk sla_id
                     * is not required because the ID can only be
                     * linked after the sla has been saved
                     */
                    $sladetail = new SlaDetail('batchSave');
                    $sladetail->attributes = $value;
                    $sladetails[] = $sladetail;
                }
            }
            /**
             * validating the sla and array of sladetail
             */
            $valid = $sla->validate();
            foreach ($sladetails as $sladetail) {
                $valid = $sladetail->validate() & $valid;
            }
 
            if ($valid) {
                $transaction = $sla->getDbConnection()->beginTransaction();
                try {
                    $sla->save();
                    $sla->refresh();
 
                    foreach ($sladetails as $sladetail) {
                        $sladetail->sla_id = $sla->id;
                        $sladetail->save();
                    }
                    $transaction->commit();
                } catch (Exception $e) {
                    $transaction->rollback();
                }
 
 
 
                $this->redirect(array('view', 'id' => $sla->id));
            }
        }
        $this->render('create', array(
            'sla' => $sla,
            'sladetails' => $sladetails
        ));
    }
 
    public function actionUpdate($id) {
        $sla = $this->loadModel($id);
        $sladetails = $sla->slaDetails;
 
        if (isset($_POST['Sla'])) {
            $sla->attributes = $_POST['Sla'];
 
            if (isset($_POST['SlaDetail'])) {
                $sladetails = array();
                foreach ($_POST['SlaDetail'] as $key => $value) {
                    /**
                     * here we will take advantage of the updateType attribute so
                     * that we will be able to determine what we want to do 
                     * to a specific row
                     */
 
                    if ($value['updateType'] == DynamicTabularForm::UPDATE_TYPE_CREATE)
                        $sladetails[$key] = new SlaDetail();
 
                    else if ($value['updateType'] == DynamicTabularForm::UPDATE_TYPE_UPDATE)
                        $sladetails[$key] = SlaDetail::model()->findByPk($value['id']);
 
                    else if ($value['updateType'] == DynamicTabularForm::UPDATE_TYPE_DELETE) {
                        $delete = SlaDetail::model()->findByPk($value['id']);
                        if ($delete->delete()) {
                            unset($sladetails[$key]);
                            continue;
                        }
                    }
                    $sladetails[$key]->attributes = $value;
                }
            }
 
            $valid = $sla->validate();
            foreach ($sladetails as $sladetail) {
                $valid = $sladetail->validate() & $valid;
            }
 
            if ($valid) {
                $transaction = $sla->getDbConnection()->beginTransaction();
                try {
                    $sla->save();
                    $sla->refresh();
 
                    foreach ($sladetails as $sladetail) {
                        $sladetail->sla_id = $sla->id;
                        $sladetail->save();
                    }
                    $transaction->commit();
                } catch (Exception $e) {
                    $transaction->rollback();
                }
 
 
 
                $this->redirect(array('view', 'id' => $sla->id));
            }
        }
 
        $this->render('create', array(
            'sla' => $sla,
            'sladetails' => $sladetails
        ));
    }
 
}

create.php

<div class="content">
    <?php
    /* @var $this SlaController */
    ?>
 
    <p>
        Example form of a one to many models with dynamic inputs!
    </p>
    <?php
    $form = $this->beginWidget('DynamicTabularForm', array(
        'defaultRowView'=>'_rowForm',
    ));
    echo "<h3>Header</h3>";
    echo $form->errorSummary($sla);
    ?>
    <div class="row-fluid">
        <div class="span4">
            <?php
            echo $form->labelEx($sla, 'name');
            echo $form->textField($sla, 'name');
            echo $form->error($sla, 'name');
            ?>
        </div>
 
        <div class="span4">
            <?php
            echo $form->labelEx($sla, 'customer_id');
            echo $form->dropDownList($sla, 'customer_id', Customer::getList());
            echo $form->error($sla, 'customer_id');
            ?>
        </div>
        <div class="span4">
            <?php
            echo $form->labelEx($sla, 'owner_id');
            echo $form->dropDownList($sla, 'owner_id', User::getList());
            echo $form->error($sla, 'owner_id');
            ?>
        </div>
 
    </div>
    <h3>Details</h3>
<?php
/**
 * this is the main feature!!
 */
echo $form->rowForm($sladetails);
 
echo CHtml::submitButton('create');
 
$this->endWidget();
?>
</div>

_rowForm.php

<?php $row_id = "sladetail-" . $key ?>
<div class='row-fluid' id="<?php echo $row_id ?>">
    <?php
    echo $form->hiddenField($model, "[$key]id");
    echo $form->updateTypeField($model, $key, "updateType", array('key' => $key));
    ?>
    <div class="span3">
        <?php
        echo $form->labelEx($model, "[$key]location");
        echo $form->textField($model, "[$key]location");
        echo $form->error($model, "[$key]location");
        ?>
 
    </div>
 
    <div class="span3">
        <?php
        echo $form->labelEx($model, "[$key]remarks");
        echo $form->textField($model, "[$key]remarks");
        echo $form->error($model, "[$key]remarks");
        ?>
    </div>
 
    <div class="span3">
        <?php
        echo $form->labelEx($model, "[$key]schedule");
 
        $this->widget('zii.widgets.jui.CJuiDatePicker', array(
            'model' => $model,
            'attribute' => "[$key]schedule",
            'options' => array(
                'showAnim' => 'fold',
            ),
            'htmlOptions' => array(
                'style' => 'height:20px;'
            ),
        ));
        echo $form->error($model, "[$key]schedule");
        ?>
 
 
    </div>
    <div class="span2">
 
            <?php echo $form->deleteRowButton($row_id, $key); ?>
        </div>
</div>

Add the following to the model that is used in the rows

public $updateType;

To Do

  • Add actionUpdate tutorial
  • Improve Documentation

Resources

Github Page

Total 15 comments

#17306 report it
heal at 2014/05/21 08:21am
Thanks

Thank you for this extension! It saves me a lot of time.

#16627 report it
ezekielnoob at 2014/03/12 10:41am
Improvements

sorry about it guys, i have been very busy for a couple of months already and haven't had a change to revisit the code...

#16625 report it
Totto Roncone at 2014/03/12 09:06am
Fixing the '+' button trouble and other documentation troubles.

First of all,

Many thanks @ezekielnoob by save long time although I had to understanding the code (thank goodness that is short xD).

Here talk about problems I encountered and resolved to make it work.

@Claudio Kerekes the same thing happened to me and to fixing it I did the following :

  1. In the controller "actions" should properly have the name of the extension as-is:
ext. ** ** dynamictabularform . actions.GetRowForm

instead it:

ext. ** ** DynamicTabularForm . actions.GetRowForm

Following complete code:

public function actions ( ) {
                return array (
                        ' getRowForm ' = > array (
                            ' class' = > ' ext.DynamicTabularForm.actions.GetRowForm '
                            'view' = > ' _row_form '
                            ' modelClass ' = > ' VisitLanguage '
                        )
                ) ;
        }

And the same with the definition in row main.php "'import' = > array ( ..." from, where instead of:

' ext.dynamictabularform . * '

we write :

' ext.DynamicTabularForm . * '

2 . At the view to rendering "_rowForm", in the row "updateTypeField" we must change "updateType " by the id ( key) of our model on which we work.

Example: if my $model is Visit and this has "id_visit" key, we would type the following:

<? php echo $ form-> updateTypeField ( $ model , $ key , " id_visit " , array (' key' = > $ key) ) >

How did I notice this? When checking the GET params that come from the javascript code by DynamicTabularForm.php class:

$ cs- > registerScript ( " DynamicForm " "
            var counter = " sizeof ($ models) . . " ;
            addRow function () {
                counter = counter + 1;
                $ . ajax ( {
                    url : ' " . $ this -> rowUrl . " '
                    data: {
                        key : counter,
                    } ,
                    success : function ( data) { appendRow (data) } ,
                } ) ;
            }

, this returned the following exception 500:

<h1> CException < / h1 >
<p> Alias ​​" ext.DynamicTabularForm.GetRowForm " is invalid . Make sure it points to an existing PHP file and the file is readable . ( / var / www / yii / framework / YiiBase.php : 322) < / p> <pre> # 0 / var / www / yii / framework /

Thanks, hope it helps

Totto.

#15806 report it
Claudio Kerekes at 2013/12/19 12:10am
click does not work to add new details

Hello people. I have a problem. I run the example but when I press + does not add a new row. Will have any idea?

thanks

#14759 report it
ezekielnoob at 2013/09/07 02:48pm
Big Changes for v3

Hi guys, i need your opinion about the usage. it seems that making the form to extend to CActiveForm makes the implementation of rows cumbersome

in my next release i would make it as a CWidget

this is the example for the next release:

$this->widget('dynamictabularform',array(
    /**
     * ajaxUpdate and rowUrl are for the ajax updating
     */
    'ajaxUpdate'=>true,
    'rowUrl'=>array('row'),
     /**
      * these next variables are for the initial data
      * that will not be populated via ajax 
      */
    'form'=>$form,
    // the form that the widget is nested into
 
    'rowView'=>'_rowForm',
    // the view file of the initial view
 
 
    'keyedViewVariables'=>array('models'=>$models),
    'viewVariables'=>array('user'=>Yii::app()->user)
 
));

also, in the first 2 releases the Dynamictabularform::rowForm() is a model specific method. i want to get away with that. what i wanted is to make the users be able to use different multiple models for each widgets.

any more comments would be appreciated

#14519 report it
ezekielnoob at 2013/08/20 05:07am
RE:Cannot create and zero detail

Hi Daniel, the bathSave scenario is there to EXEMPT the required sla_id since we are validating the slaDetail without sla_id.

array('sla_id','required','except'=>'batchSave')

on the other hand your question about zero details, i will have to add that functionality. initially DynamicTabularForm requires atleast 1 $slaDetail to register its scripts... thanks for the comments... i will have to add some features to support that properly in my third release

#14516 report it
Daniel at 2013/08/19 10:22pm
Cannot create and zero detail

Hi,

I need more info on how to create the rules for sla_detail

public function rules() {
        // NOTE: you should only define rules for those attributes that
        // will receive user inputs.
        return array(
            array('sla_id, name, date', 'required'),
            array('name, date', 'required', 'on' => 'batchSave'),
            array('sla_id', 'numerical', 'integerOnly' => true),
            array('name', 'length', 'max' => 100),
            // The following rule is used by search().
            // @todo Please remove those attributes that should not be searched.
            array('id, sla_id, name, date', 'safe', 'on' => 'search'),
        );
    }

Using above rules will not create the sla + sla_detail and no error messages shown. Somehow, it worked when I removed the sla_id numerical rule. I did not understand why.

In addition, my client just want to reserve the invoice number and do not want to add the detail yet. using this setup, it will not be able to save the sla since it requires minimum of one detail.

Thank you in advance.

#14426 report it
ezekielnoob at 2013/08/10 09:28pm
RE:image of the error message

try going to the URL directly so that you will be able to see the yii debug message... error 500 is more likely your syntax error

#14424 report it
Peter JK at 2013/08/10 12:39pm
image of the error message

Internal Server Error.. can you see that error?

Open this Link to see the error

#14423 report it
ezekielnoob at 2013/08/10 11:17am
Re: Still Get Error when duplicate Row

maybe you can show us what exactly is the error? look at yii's response message

#14422 report it
Peter JK at 2013/08/10 11:09am
Still Get Error when duplicate Row

preview image on this link

error when click create button..

what have i been missing?

#14386 report it
ezekielnoob at 2013/08/07 07:54pm
@Sepiroth

what troubles are you having exactly?

#14382 report it
Sephiroth at 2013/08/07 10:19am
Troubles...

Interesting ...

You can place an instance, database and app to see the actual performance?

I tried to run but I can add columns.

regarding adding "public UpdateType," I miss you $ ...

regards

#14347 report it
ezekielnoob at 2013/08/05 05:03pm
Update v2!

i have released a bug fix on the DynamicTabularForm::updateTypeField(), now it uses the key unlike the first release thanks!

#14321 report it
ezekielnoob at 2013/08/04 03:02pm
Imrpovements!

I hope you guys can evaluate and comment for bugs or what still can be done to improve it! thanks

Leave a comment

Please to leave your comment.

Create extension