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