title: Collecting Tabular Input with improper indexes
body: <p>Hope you all be doing fine.</p>
  
  <p><strong>Flow</strong>
  Every time I hit the Add New button, an AJAX request is sent to server with the updated counter of indexes to controller. Now controller creates a new model at given index, develops a container row using a partial view and then converts it to Json and returns it back to show off the new record extending the current main container with the sub-container or row.</p>
  
  <p><a href="https://i.stack.imgur.com/2fGUf.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/2fGUf.png" alt="enter image description here"></a>
  <strong>Problem</strong>
  Everything works fine as long as we follow the normal flow of indexes. But when I make changes to the normal flow by adding/removing records (indexes) and then submit it (as shown in above image - indexes are not in normal flow, 2nd index is deleted), I get validation errors pointing the model values (the required ones) can not be left blank.</p>
  
  <p><strong>Target</strong><br>
  I want to add model-instances to be added in my database if they are valid regardless of their index number. No matter what index they belong to, I want them to be submitted and treated like a model array that is in a sequence.</p>
  
  <p>Here is the code I have in my main partial file:
  <strong>_partials/_edit_form</strong></p>
  
  <pre><code>&lt;div class="row"&gt;
      &lt;!-- container that has form and its operations in it --&gt;
      &lt;div class="col-xs-12"&gt;
  
  &lt;?php
  
      $form = ActiveForm::begin([
          'id' =&gt; 'form_edit_resume_lang_multiple',
          'options' =&gt; [
              'enctype' =&gt; 'multipart/form-data',
              'class' =&gt; 'section_form',
          ],
      ]);
  
  ?&gt;
      &lt;div id="add_new_record_container"&gt;
      &lt;!--
          Add new Item related item_container will be placed in here dynamically in parallel
          This loaded content is based on _add_new_form partial view
          Content is added via AJAX request caused by btn_add_new_record
      --&gt;
      &lt;/div&gt;
  
      &lt;div class="row form_controls"&gt;
          &lt;div class="col-xs-12 text-right"&gt;
              &lt;input type="hidden" name="hdnCounter" id="hdnCounter" value="&lt;?php echo count($languages); ?&gt;" /&gt;
              &lt;?php echo Html::a(T::t('main', 'Add New'), ['/oresume/resume-language/add-new-record', 'idresume'=&gt;$model_resume-&gt;idresume], ['id'=&gt;'btn_add_new_record', 'class'=&gt;'btn btn-primary' ]); ?&gt;
          &lt;/div&gt;
          &lt;div class="col-xs-12 text-right"&gt;
              &lt;?php echo Html::a(T::t('main', 'Cancel'), ['/oresume/attachment/discard-all-drafts'], ['class'=&gt;'btn btn-default', 'id'=&gt;'btnDiscardChanges', 'data'=&gt;['confirm_message'=&gt;T::t('main', 'Discard Changes?'), ] ]);?&gt;
              &lt;?php echo Html::submitButton(T::t('main', 'Save Changes'), ['class' =&gt; 'btn btn-success']); ?&gt;
          &lt;/div&gt;
      &lt;/div&gt;
  
      &lt;?php $attributes = Json::htmlEncode($form-&gt;attributes);?&gt;
      &lt;script type="text/javascript"&gt;
          jQuery(document).ready(function($){
              &lt;?php 
                  $options = Json::htmlEncode($form-&gt;options);
                  $attributes = Json::htmlEncode($form-&gt;attributes);          
              ?&gt;
  
              $("#&lt;?php echo $form-&gt;options['id'];?&gt;").yiiActiveForm(&lt;?php echo $attributes;?&gt;, &lt;?php echo $options;?&gt;);
  
              $(".resume_languages").select2({
                  tags: true,
                  multiple: true,
                  maximumSelectionLength: 1,
                  language: "&lt;?php echo \Yii::$app-&gt;language; ?&gt;",
                  allowClear: true,
                  placeholder: {
                      idskill: "",
                      placeholder: "&lt;?php echo T::t('main', 'Please select'); ?&gt;"
                  },
              });
  
              var attributes = &lt;?php echo $attributes;?&gt;; 
              for(var i = 0; i &lt; attributes.length; i++) {
                  $("#form_edit_resume_lang_multiple").yiiActiveForm('add', attributes[i]);
              }
          });
      &lt;/script&gt;
  
  
  &lt;?php ActiveForm::end(); ?&gt;
      &lt;/div&gt;
      &lt;!-- //container that has form and its operations in it --&gt;
  &lt;/div&gt;
  </code></pre>
  
  <p><strong>Note:</strong>
  The hdnCounter inout field has the counter value. the initial counter is 0. When I hit "Add New" button, the AJAX request brings in a new model instance with proper look and feel as response and it is appended to main form container. After that counter gets incremented by 1.</p>
  
  <p>Here is the code for my partial_view that is used as a base template to create a new 
  <strong>_partials/_add_new_form.php</strong></p>
  
  <pre><code>&lt;div class="col-xs-12 item_container"&gt;
  
      &lt;div class="row single_value_row"&gt;
          &lt;div class="col-xs-6"&gt;
              &lt;?php echo $form-&gt;field($model, "[$i]idlanguage")-&gt;dropDownList($languages, ['style'=&gt;['width'=&gt;'100%'], 'class'=&gt;'resume_languages', 'placeholder'=&gt;T::t('main', 'Please select')]); ?&gt;
          &lt;/div&gt;
          &lt;div class="col-xs-5"&gt;
              &lt;?php echo $form-&gt;field($model, "[$i]level")-&gt;dropDownList($proficiencyLevels, ['prompt' =&gt; T::t('main', 'Please Select')]); ?&gt;
          &lt;/div&gt;
          &lt;div class="col-xs-1 text-left"&gt;
              &lt;button class="btn btn-danger pull-right btn_remove_record" id="btnRemoveResumeLanguage-&lt;?php echo $i;?&gt;"&gt;&lt;?php echo T::t('main', '&lt;i class="fa fa-times"&gt;&lt;/i&gt;'); ?&gt;&lt;/button&gt;
              &lt;?php echo $form-&gt;field($model, "[$i]isDeleted")-&gt;hiddenInput(['class'=&gt;'isDeleted_input'])-&gt;label(false); ?&gt;
              &lt;?php echo $form-&gt;field($model, "[$i]isFromDb")-&gt;hiddenInput(['class'=&gt;'isFromDb_input'])-&gt;label(false); ?&gt;
          &lt;/div&gt;
      &lt;/div&gt;
  
      &lt;script type="text/javascript"&gt;
          jQuery(document).ready(function($){
              $("#form_edit_resume_lang_multiple").yiiActiveForm('add', 'OresumeResumeLang[&lt;?php echo $i; ?&gt;]');
  
              $(".resume_languages").select2({
                  tags: true,
                  multiple: true,
                  maximumSelectionLength: 1,
                  language: "&lt;?php echo \Yii::$app-&gt;language; ?&gt;",
                  allowClear: true,
                  placeholder: {
                      idskill: "",
                      placeholder: "&lt;?php echo T::t('main', 'Please select'); ?&gt;"
                  },
              });
  
              &lt;?php $attributes = Json::htmlEncode($form-&gt;attributes);?&gt;
              var attributes = &lt;?php echo $attributes; ?&gt;;
              for(var i = 0; i &lt; attributes.length; i++) {
                  $("#form_edit_resume_lang_multiple").yiiActiveForm('add', attributes[i]);
              }
          });
      &lt;/script&gt;
  &lt;/div&gt;
  </code></pre>
  
  <p>Now Here is the code my my controller method that creates a new model on given index
  <strong>actionAddNewRecord</strong></p>
  
  <pre><code>public function actionAddNewRecord($idresume, $index)
  {
      $model = new OresumeResumeLang();
      $model-&gt;idresume = $idresume;
      $model-&gt;isDeleted = 0;
      $model-&gt;isFromDb = 0;
  
      $response = [];
  
      $response['content'] = $this-&gt;renderPartial('_partials/_add_new_form', [
          'model' =&gt; $model, 'i'=&gt;$index
      ]);
  
      return Json::encode($response);
  }
  </code></pre>
  
  <p>Here is the code for Adding/Removing multiple instances - (managing tabular input)
  <strong>actionGetResumeLanguages</strong></p>
  
  <pre><code>    public function actionGetResumeLanguages($idresume)
      {
          $model_resume = OresumeResume::findOne($idresume);
          $models = OresumeResumeLang::getResumeLanguages($model_resume-&gt;idresume);
  
          $response = [];
  
          $postedArray = \Yii::$app-&gt;request-&gt;post('OresumeResumeLang');
          //         print_r($postedArray);
  
          if( count($postedArray) )  //case: Its a postback and we have some models in it
          {
              if(count($models) &lt; count($postedArray) )//case: postback has more number of models as compared to in db
              {
                  //  Generate empty models array to be filled by loadMultiple() method of model class
                  //  create emoty models and add in models array counter so that
                  //  we've equal number of sent / recieved models for processing
                  for ($i=count($models); $i&lt; count($postedArray); $i++ )
                  {
                      $model = new OresumeResumeLang();
                      $model-&gt;idresume = $idresume;
                      $models[] = $model;
                  }
              }
          }
  
          if( count($models) == 0) //  we need to check if this array has something to process
          {
              $response['status'] = false;
              $response['message'] = T::t('main', 'No records found to process');
          }
  
          //  if we have postback and if we have more models in it than
          //  in our database, and since we have created empty models
          //  to be filled-up now we can load posted models in our array
          if(OresumeResumeLang::loadMultiple($models, \Yii::$app-&gt;request-&gt;post())) //  load multiple models of models array
          {
              $status = true;
  
              foreach ($models as $model)
              {
                  //  Delete models that are flaged to do so
                  //  execute continue; statement after deletion
  
                  //  Validate and save models that are to be saved/updated
                  $model-&gt;idlanguage = OresumeResumeLang::getLanguageId($model-&gt;idlanguage);
                  $model-&gt;level = ($model-&gt;level != null)? $model-&gt;level : OresumeResumeLang::LEVEL_BEGINNER;
                  if( $model-&gt;validate() )    //  Case: Model data is valid
                  {
                      //  Save Model in database
                      $status &amp;= $model-&gt;save();
                  }
                  else
                  {
                      $status = false;
  //                     print_r($model-&gt;errors['idlanguage'][0]);
                      $response['message'] = T::t("main", "Storing of record \"{$model-&gt;idlanguage0-&gt;name}\" got some validation issues\n");
                  }
              }
  
              if($status)
              {
                  $model_resume = OresumeResume::findOne($model-&gt;idresume);
                  $models = OresumeResumeLang::getResumeLanguages($model-&gt;idresume);
  
                  $response['status'] = true;
                  $response['content'] = $this-&gt;renderPartial('_partials/_edit_form', ['model_resume' =&gt; $model_resume, 'languages' =&gt; $models]);
                  $response['counter'] = count($models);
                  $response['message'] = T::t('main', 'Record(s) updated Successfully');
              }
              else
              {
                  $response['status'] = false;
                  //  $response['message'] = T::t('main', 'Records could not be updated.\n Something went wrong');
              }
          }
          else // case: page loads for the first time
          {
              $response['content'] = $this-&gt;renderPartial('_partials/_edit_form', ['model_resume'=&gt;$model_resume, 'languages' =&gt; $models]);
          }
  
          return Json::encode($response);
      }
  </code></pre>
  
  <p>Edit:
  I created index-2 and then deleted it. (caused the form to be created against index-2). Now Once again, I hit Add New (causing a new form to be created against index-3) I fill it with values and then submit the form to the server, I get the validation errors against form with index-2 pointing the values cannot be left blank. I am not able to fix this.</p>