Dynamic Tabular Input Form

I did it without any extension to get better understanding of the problem.

Next step will be to test out yii2-dynamicform and or other solutions.

Its ugly like hell - but for my purpose it is working.

So here is my solution so far…

Scenario:

Lets say we have the tables / models "Master" and "Slave".

Every "Master" should have multiple "Slaves".

And you want to create a Master including a "unkown" amount of Slaves at the same time.

So here is everything important I did:

app/views/masterslave/create.php

[spoiler]




### FORM VIEW 

$form = ActiveForm::begin([

	'id' => 'masterslave-form',

	'enableAjaxValidation' =>false, // important to disable

	'enableClientValidation'=>false, // important to disable

]);


# ERROR SUMMARY

echo $form->errorSummary(array_merge([$event], $slaves));


# MASTER ELEMENTS (like in any normal active form)

echo $form->field($master, 'name')->textInput();


# SLAVE ELEMENTS (dynamic tabular - for simplicity only ONE text field)

echo '<div id="slaves_adder_div">'; // div to append / remove dynamic 

	foreach ($slaves as $nr => $slave) {	// loop through slave-elements

		echo $form->field($single_slave, "[$nr]name")->textInput(); // generate batch input field

	}

echo '</div>';


# BUTTONS: ADD / REMOVE SLAVE

echo '<input type="button" class="btn btn-xs btn-primary" id="add-slave" value="add" /> ';

echo '<input type="button" class="btn btn-xs btn-primary" id="rem-slave" value="remove" />';


# RENDER PARTIAL TO APPEND AS JSON 

$new_slave = 

Json::encode(

	$this->render('_slaveRow', [

		'form'=>$form, 

	])

);


# BUTTON FUNCTIONS TO ADD OR REMOVE 

$this->registerJs('

	// keep track of added elements for correct ID

	var slaveID='.count($slaves).';


	// add a new row to div

	$("#add-slave").on("click",function(){

		var newRow = '.$new_slave.';


		// replace string "ROW_ID" with current id

		newRow = newRow.replace(/ROW_ID/g, slaveID);

		

		// appent to div

		$("#copyreceiver_adder_div").append(newRow);


		slaveID = slaveID+1;

	})


	// remove row from div

	$("#rem-slave").click(

	   function(){

		   var oldDiv = $("#copyreceiver_adder_div").children("div").eq(slaveID-1);

		   oldDiv.remove();

		   

		   if(slaveID > 0){

			   slaveID=slaveID-1;

		   }else{

			   slaveID=0;

		   }

	   }

   );

');



[/spoiler]

Then create the partial you want to "repeat" in your form.

app/views/masterslave/_slaveRow.php:

[spoiler]




	use app\models\Slave;


	$slave = new Slave;

	// $form variable is passed with render in "main-form"

	echo $form->field($slave, "[ROW_ID]user_id")->textInput(); // ROW_ID will be replaced while rendering in "main-form"



[/spoiler]

app/controllers/MasterSlaveController => actionCreate I did:

[spoiler]




public function actionCreate()

{

	// shortcut to request

	$request  = Yii::$app->request; 


	// models for initial form 

	$master= new Master; 

	$slaves= [new Slave]; // create first model in array  

	

	// if form was submitted

	if($request->post()){

		$valid=true; // ensure later that validation of all models was OK


		$master->load($request->post()); 

		$valid=$master->validate() && $valid; 


		// get and validate slaves

		$POST_slaves = $request->post('Slave', []);

		foreach ($POST_slaves as $i => $POST_slave) {

			$slave = new Slave;

			$slave->setAttributes($POST_slave);

			$slaves[$i] = $slave;

			$valid=$slaves[$i]->validate() && $valid;

		}

		

		// when $valid is still true all models are validated and OK to save

		if($valid){

			// CODE / TRANSACTION TO SAVE ALL MODELS 

		}

	}

	

	// Default Output of the form 

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

		'master' =>$master,

		'slaves' =>$slaves

	]);

}	



[/spoiler]

Problems with this solution:

  • ClientSide Validation will not work.

  • Form will be reloaded on every press on "create"

Like all others here I’m not very happy with this solution.

So every improvement to this problem is welcome…

In next steps I will try out yii-dynamicform like suggested above.

Best Regards

Same problem here. Trying to find an elegant solution, but still haven’t found one. :frowning: Hopefully there will be a elegant solution available soon.

Hi guys, have you found a solution to this problem?

The best way I’ve found to do this is to add a javascript function, something like this:




   this.addRow = function (el) {

        $.ajax({

            url: this.createUrl,

            type: 'GET'

            })

            .done(function(data) {

                gv = $(el).parents('.gridview').data('name');

                window[gv].gridview.find('tbody').append(data);

                window[gv].gridview.find('.add-new').prop('disabled', true);

        });

     }



the url points to a form formatted as a table row, so perhaps like this:-




    <tr>

        <td class="editable-grid-newrow" colspan="4">

            <?php $form = ActiveForm::begin(['id' => 'form-new-status', 'layout'=>'inline']); ?>

                <div class='form-group'>

                <?= $form->field($options, 'statuses[code]',['inputOptions'=>['placeholder'=>'Code']]) ?>

                <?= $form->field($options, 'statuses[descrip]',['inputOptions'=>['placeholder'=>'Descrip']]) ?>

                <?= $form->field($options, 'statuses[colour]',['inputOptions'=>['placeholder'=>'Colour']])->hiddenInput(); ?>


                <?= Html::submitButton(Yii::t('app', 'Create') , ['class' => 'btn btn-primary']) ?>

                </div>

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

        </td>

    </tr>



I’m in the process of updating my editable gridview plugin to contain this which you can find on github

NOTE: This is a proof of concept NOT production ready code …

Hi to everyone.

Is there a decision o this problem? Have the same task for now.

Basicaly, this is problem master/child form. Bat in Yii community nobody will help You!!! Master/child form is top secrete.

Very strange but true.

Asim

So… regarding clientside validation. Did anyone look at the cookbook?


Adding and removing fields dynamically


To add a field to validation list:


$('#contact-form').yiiActiveForm('add', {

    'id': 'address',

    'name': 'address',

    'container': '.field-address',

    'input': '#address',

    'error': '.help-block'

});

https://yii2-cookbook.readthedocs.io/forms-activeform-js/

Hi Guys,

The weekend was reading about this problem. Finally I found it solution:

For me this widget work perfectly.

Can somebody explain me how to got it guy do work the client side validation in the new rows?