Sorry, I guess I should have supplied all my code, as I’m already using loadMultiple.
My controller is
public function actionLegIndexNew($id)
{
$modelLeg = ProjectsLegs::findOne(['LegId' => $id]);
$modelInvoicesItems = $modelLeg->projectsLegsInvoicesItems;
if (Yii::$app->request->post('ProjectsLegsInvoicesItems')) {
$oldItemIds = ArrayHelper::map($modelInvoicesItems, 'InvoiceItemId', 'InvoiceItemId');
$modelInvoicesItems = Model::createMultipleProjectsLegsInvoicesItems(ProjectsLegsInvoicesItems::class, $modelInvoicesItems);
Model::loadMultiple($modelInvoicesItems, Yii::$app->request->post('ProjectsLegsInvoicesItems'));
$deletedItemsIds = array_diff($oldItemIds, array_filter(ArrayHelper::map($modelInvoicesItems, 'InvoiceItemId', 'InvoiceItemId')));
$valid = Model::validateMultiple($modelInvoicesItems);
if (!$valid) {
$validationErrors = '';
$iCounter = 0;
foreach ($modelInvoicesItems as $modelsItem) {
$iCounter++;
$valid = $modelsItem->validate();
if (!$valid) {
if ($validationErrors != '') {
$validationErrors .= '<br>';
}
$validationErrors .= '<b>Work Item Entry No. ' . $iCounter . '</b><br>';
foreach ($modelsItem->getErrors() as $attribute => $errors) {
$validationErrors .= '<div class="col-md-offset-1">' . $attribute . ' - ' . implode('<br>', $errors) . '</div>';
}
}
}
return json_encode([
'status' => 'Error',
'message' => '<i class="glyphicon glyphicon-remove"></i> There are problems with the following Invoice Items entries!',
'errors' => $validationErrors
]);
}
if ($valid) {
$transaction = \Yii::$app->db->beginTransaction();
try {
$flag = true;
// if ($flag = $modelLeg->save(false)) {
if (!empty($deletedItemsIds)) {
ProjectsLegsInvoicesItems::deleteAll(['InvoiceItemId' => $deletedItemsIds]);
}
foreach ($modelInvoicesItems as $modelsItem) {
$modelsItem->LegId = $modelLeg->LegId;
if (!($flag = $modelsItem->save(false))) {
$transaction->rollBack();
return json_encode(['status' => 'Error', 'message' => 'Item not created!']);
break;
}
}
// }
if ($flag) {
$transaction->commit();
return json_encode(['status' => 'Success', 'message' => '<i class="glyphicon glyphicon-ok"></i> Entry updated successfully.']);
}
} catch (ErrorException $e) {
$transaction->rollBack();
return json_encode(['status' => 'Error', 'message' => 'Something went wrong!']);
}
}
} else {
return $this->renderAjax('indexn', [
'modelLeg' => $modelLeg,
'modelInvoicesItems' => (empty($modelInvoicesItems)) ? [new ProjectsLegsInvoicesItems] : $modelInvoicesItems,
]);
}
}
Model::createMultipleProjectsLegsInvoicesItems is
public static function createMultipleProjectsLegsInvoicesItems($modelClass, $multipleModels = [])
{
$model = new $modelClass;
$formName = 'ProjectLegsInvoiceItems';
$post = Yii::$app->request->post($formName);
$models = [];
if (! empty($multipleModels)) {
$keys = array_keys(ArrayHelper::map($multipleModels, 'InvoiceItemId', 'InvoiceItemId'));
$multipleModels = array_combine($keys, $multipleModels);
}
if ($post && is_array($post)) {
foreach ($post as $i => $item) {
if (isset($item['InvoiceItemId']) && !empty($item['InvoiceItemId']) && isset($multipleModels[$item['InvoiceItemId']])) {
$models[] = $multipleModels[$item['InvoiceItemId']];
} else {
$models[] = new $modelClass;
}
}
}
unset($model, $formName, $post);
return $models;
}
The View is
<?php
use yii\bootstrap\ActiveForm;
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use yii\helpers\Url; //used to make create a popup window
use wbraganca\dynamicform\DynamicFormWidget;
use backend\models\LstPilotCarItems;
use backend\models\LstTaxRates;
use backend\models\ProjectsLegsInvoicesItems;
/* @var $this yii\web\View */
/* @var $searchModel backend\models\ProjectsLegsInvoicesItemsSearch */
/* @var $dataProvider yii\data\ActiveDataProvider */
$this->title = Yii::t('app', 'Projects Legs Invoices Items');
// $this->params['breadcrumbs'][] = $this->title;
?>
<div class="projects-legs-invoices-items-index">
<?php
$form = ActiveForm::begin([
// 'id' => $modelLeg->formName(),
'id' => 'ProjectLegsInvoiceItems',
'fieldConfig' => [
'enableError' => true,
'options' => ['class' => ''],
'template' => '<div class="col-md-2 no-padding">{label}</div><div class="col-md-10">{input}{error}</div>',
'labelOptions' => [
'class' => 'control-label'
],
],
'layout' => 'horizontal', //'default', 'horizontal' or 'inline'
]);
?>
<div class="modal-body">
<table>
<tbody>
<tr>
<th>Leg No: </th>
<td><?= $modelLeg->LegNo ?></td>
</tr>
<tr>
<th>From: </td>
<td><?= $modelLeg->LocFrom ?></th>
</tr>
<tr>
<th>To: </td>
<td><?= $modelLeg->LocDest ?></th>
</tr>
<tr>
<th>Type of Service: </th>
<td><?= $modelLeg->lstProjectsLegsServtypes->ServType ?></td>
</tr>
<tr>
<th>Rate: </th>
<td><?= $modelLeg->EmpRate ?></td>
</tr>
</tbody>
</table>
<br>
<?php
DynamicFormWidget::begin([
'widgetContainer' => 'projectsLegsInvoicesItems',
'widgetBody' => '.items-items',
'widgetItem' => '.item-item',
'limit' => 25,
'min' => 0,
'insertButton' => '.add-item',
'deleteButton' => '.remove-item',
'model' => $modelInvoicesItems[0],
// 'formId' => 'dynamic-form',
'formId'=>$modelLeg->formName(),
'formFields' => [
// 'InvoiceItemId',
'PilotCarItemId',
'Qty',
'UnitPrice',
'TaxRateId',
],
]);
?>
<div class="panel panel-body">
<div class="scrollyitems">
<table id="table-items" class="table table-bordered table-condensed">
<thead>
<tr class="active">
<td style="min-width: 25px;"> </td>
<td style="min-width: 25px;"><label>No.</label></td>
<td style="min-width: 5px;"><?= Html::Label('Item'); ?></td>
<td style="min-width: 125px;"><?= Html::Label('Qty'); ?></td>
<td style="min-width: 125px;"><?= Html::Label('Unit Price'); ?></td>
<td style="min-width: 125px;"><?= Html::Label('Sub-Total'); ?></td>
<td style="display: none;"></td>
</tr>
</thead>
<tbody class="table-striped items-items"><!-- widgetContainer -->
<?php foreach ($modelInvoicesItems as $i => $modelInvoicesItem):
//$rowClass = ($modelInvoicesItem->Active != 1 ? ' danger' : '');
$rowClass =''; ?>
<tr class="item-item<?= $rowClass ?>"><!-- widgetBody -->
<td class="align-middle">
<button type="button" class="remove-item btn btn-danger btn-xs table-control" tabindex="-1"><i class="glyphicon glyphicon-minus"></i></button>
<?php
if (! $modelInvoicesItem->isNewRecord) {
echo Html::activeHiddenInput($modelInvoicesItem, "[{$i}]InvoiceItemId",['class' => 'table-control']);
}
?>
</td>
<td class="align-middle text-right index"><?= $i+1 ?></td>
<td>
<?php
$query = LstPilotCarItems::find()
->distinct()
->innerJoin('clients_price_list', 'lst_pilot_car_items.PilotCarItemId = clients_price_list.PilotCarItemId')
->innerJoin('projects', 'projects.ClientId = clients_price_list.ClientId')
->rightJoin('clients', 'clients.ClientId = projects.ClientId')
->innerJoin('projects_legs', 'projects_legs.ProjId = projects.ProjId')
->where(["=", "projects_legs.LegId", $modelLeg->LegId])
->andWhere('lst_pilot_car_items.CurrencyCode = clients.CurrencyCode')
->andWhere('lst_pilot_car_items.CreditCardCompany = clients.CreditCardCompany')
->orderBy([
'lst_pilot_car_items.SortOrder'=>SORT_ASC,
'lst_pilot_car_items.PilotCarItem'=>SORT_ASC,
]);
echo $form->field($modelInvoicesItem, "[{$i}]PilotCarItemId",['options' => ['class' => 'table-control']])->begin();
// echo Html::activeTextInput($modelInvoicesItem, "[{$i}]PilotCarItemId", ['class' => 'form-control']); //Field
echo Html::activeDropDownList($modelInvoicesItem,
"[{$i}]PilotCarItemId",
ArrayHelper::map($query->all(),
'PilotCarItemId',
'PilotCarItem'
),
[
'class' => 'form-control',
'prompt' => 'Select an Item ...',
]
);
echo Html::error($modelInvoicesItem,"[{$i}]PilotCarItemId", ['class' => 'help-block']); //error
echo $form->field($modelInvoicesItem, "[{$i}]PilotCarItemId")->end();
?>
</td>
<td>
<?php
echo $form->field($modelInvoicesItem, "[{$i}]Qty",['options' => ['class' => 'table-control']])->begin();
echo Html::activeTextInput($modelInvoicesItem, "[{$i}]Qty", ['class' => 'form-control text-right', 'onchange' => 'recalcSubTotal(this);calculateSum();']); //Field
echo Html::error($modelInvoicesItem,"[{$i}]Qty", ['class' => 'help-block']); //error
echo $form->field($modelInvoicesItem, "[{$i}]Qty")->end();
?>
</td>
<td>
<?php
echo $form->field($modelInvoicesItem, "[{$i}]UnitPrice",['options' => ['class' => 'table-control']])->begin();
echo Html::activeTextInput($modelInvoicesItem, "[{$i}]UnitPrice", ['class' => 'form-control text-right','onchange' => 'recalcSubTotal(this);calculateSum();','tabIndex' => -1,]); //Field
echo Html::error($modelInvoicesItem,"[{$i}]UnitPrice", ['class' => 'help-block']); //error
echo $form->field($modelInvoicesItem, "[{$i}]UnitPrice")->end();
?>
</td>
<td>
<?php
echo Html::input('text',"ProjectsLegsInvoicesItems[$i][SubTotal]",'',
[
'class' => 'form-control text-right',
'id' => "projectslegsinvoicesitems-$i-subtotal",
'readonly' => true,
'tabIndex' => -1,
]);
?>
</td>
<td style="display: none;">
<?php
echo $form->field($modelInvoicesItem, "[{$i}]TaxRateId",['options' => ['class' => 'table-control']])->begin();
echo Html::activeDropDownList($modelInvoicesItem,
"[{$i}]TaxRateId",
ArrayHelper::map(
LstTaxRates::find()
->FilterWhere(['like', 'Name', 'Sales'])
->orWhere(['TaxType'=>'NONE'])
->orderBy(['Name'=>SORT_ASC,])
->all(),
'TaxRateId',
function($model) {
return $model['Name'].' ('.$model['EffectiveRate'].'%)';
}
),
[
'class' => 'form-control',
'prompt'=>'Select a Tax Rate ...',
'tabIndex' => -1,
]
);
echo Html::error($modelInvoicesItem,"[{$i}]TaxRateId", ['class' => 'help-block']); //error
echo $form->field($modelInvoicesItem, "[{$i}]TaxRateId")->end();
?>
</td>
</tr>
<?php endforeach; // end of loads loop ?>
</tbody>
<tfoot>
<tr>
<td colspan="5"></td>
<td>
<?php
echo Html::input('text',"SubTotal",'',
[
'class' => 'form-control text-right',
'id' => "subtotal",
'readonly' => true,
'tabIndex' => -1,
]);
?>
</td>
</tr>
<tr>
<td colspan="7" class="active">
<button type="button" class="btn btn-xs btn-success add-item"><i class="glyphicon glyphicon-plus"></i> Add New Item</button>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
<?php
DynamicFormWidget::end();
?>
</div>
<button type="submit" id="btn-close" class="btn btn-block btn-success form-cmd" title="Save and close">
<span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span> Create</button>
<?php
ActiveForm::end();
?>
</div>
<?php
$script = <<< JS
$('form#ProjectLegsInvoiceItems').on('beforeSubmit', function(e){
// $('form#{$modelLeg->formName()}').on('beforeSubmit', function(e){
'use strict';
$("#loading").show();
var \$form = $(this);
$.post(
\$form.attr("action"), //serialize Yii2 form
\$form.serialize()
)
.done(function(result){
result = JSON.parse(result);
var divClass = (result.status == 'Error')? 'danger' : result.status.toLowerCase();
var sysMsg = '<div id="w0-' + result.class + '-0" class="alert-' + divClass + ' alert fade in">'
+ '<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>'
+ result.status + '<br>' + result.message + '<br>' + (result.errors === undefined?'':'<br><br>' + result.errors) + '</div>';
if(result.status == 'Success'){
$(\$form).trigger("reset");
$('#modal-system-messages').html("");
$('#modalPopup2 #modalHeaderTitle').html("");
$('#modalPopup2 #modal-system-messages').html("");
$('#modalPopup2 #modalContent').html("");
$('#modalPopup2').modal('hide');
$('#system-messages').html(sysMsg).stop().fadeIn().animate({opacity: 1.0}, 4000).fadeOut(4000);
}else{
$('#modal-system-messages').html(sysMsg).stop().fadeIn().animate({opacity: 1.0}, 4000);
}
$("#loading").fadeOut("slow");
})
.fail(function(xhr, status, error){
var msg = "<span class=\"glyphicon glyphicon-remove\"></span><b>Javascript Error::Form/beforeSubmit()</b><br>"+xhr.responseText;
var sysMsg = '<div id="w0-error-0" class="alert-danger alert fade in">'
+ '<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>'
+ '<span class="glyphicon glyphicon-warning-sign"></span> '+msg+'</div>';
$('#modal-system-messages').html(sysMsg).stop().fadeIn().animate({opacity: 1.0}, 4000);
$("#loading").fadeOut("slow");
});
return false;
});
$(function(){
calcSubTotals();
calculateSum();
$(".projectsLegsInvoicesItems").on("afterInsert", function(e, item) {
$('#table-items > tbody > tr:last > td:nth-child(2)').html(item.rowIndex);
$('#projectslegsinvoicesitems-' + (item.rowIndex - 1) + '-pilotcaritemid').focus();
})
if($('#projectslegsinvoicesitems-0-pilotcaritemid').length > 0){ $('#projectslegsinvoicesitems-0-pilotcaritemid').focus(); }
})
function recalcSubTotal(elem){
var callingElem = $(elem);
var Qty = callingElem.closest("tr").find("[id$='-qty']").val();
var UnitPrice = callingElem.closest("tr").find("[id$='-unitprice']").val();
var SubTotal = Qty * UnitPrice;
callingElem.closest("tr").find("[id$='-subtotal']").val(SubTotal.toFixed(2));
return false;
};
function calcSubTotals(){
var noRows = $("#table-items > tbody > tr").length;
for (var i = 0; i < noRows; i++) {
recalcSubTotal($('#projectslegsinvoicesitems-' + i + '-unitprice'));
}
}
function calculateSum(){
var noRows = $("#table-items > tbody > tr").length; //Zero based!
var subtotal = 0;
for (var i = 0; i < noRows; i++) {
value = $('#projectslegsinvoicesitems-' + i + '-subtotal').val();
if (typeof (value) === 'string') {
value = value.replace(/[$,]+/g,"");
}
value = parseFloat(value);
subtotal = subtotal + value;
}
$('#subtotal').val(subtotal.toFixed(2));
}
JS;
$this->registerJS($script);
?>
For some reason I suspect my issue lies with the $post assignment in
Model::createMultipleProjectsLegsInvoicesItems, but could easily be wrong.
In this instance, $modelLeg is just for information purposes and does not need to be saved in any way, I don’t have a form with it, just use it to populate some reference details in a table.