Question

We have to develop a multi-model form that should use ajax calls to dynamically add or remove, sub-model fieldsets.

For example, let's imagine this team form scenario:

Team:
Team Name: 
Team Country:

Members:
Member A name: 
Member A age: 
      [add another member]

[REGISTER TEAM AND MEMBERS]

We are using Yii at the moment. As as far as we can tell Yii doesn't support this out of the box (nor there is any extension that does this). Please don't tell me that there is, if you have not done it. As far as we can tell, that's not possible at the moment without A LOT of extending work.

So, we may try to integrate Zend Form Collections but it seems a pain.

So, Zend do have this.

My question is quite simple:

From all developers and programmers here present, that use php frameworks, what framework do you use, in order to accomplish the scenario described above? Other than Zend, that, we know, do have this already.

Was it helpful?

Solution

Here is the final solution, form id is #quoteForm. Here you "merge" the settings of the two active forms:

//Add car button
        $("#addCar").on('click', function(){
            $.get('<?php echo Yii::app()->createAbsoluteUrl('quote/addCar'); ?>' + '?index=' + (carCount+1), function(data) {
                var settings = $("#quoteForm").data('settings');
                $("#quoteForm").data('settings', null);
                $("#carlist").append(data);
                var settings_new = $("#quoteForm").data('settings');
                carCount++;

                $.each(settings_new.attributes, function(k, v) {
                    settings.attributes.push(v);
                });
               //set the new settings
               $("#quoteForm").data('settings', settings);
        });

I actually use ActiveForm class which extends from CActiveForm and has this method which adds JS to the client script without running the widget(extracted form CActiveForm->run()):

public function registerJs()
{
    $options=$this->clientOptions;
    if(isset($this->clientOptions['validationUrl']) && is_array($this->clientOptions['validationUrl']))
        $options['validationUrl']=CHtml::normalizeUrl($this->clientOptions['validationUrl']);

    $options['attributes']=array_values($this->attributes);

    if($this->summaryID!==null)
        $options['summaryID']=$this->summaryID;

    if($this->focus!==null)
        $options['focus']=$this->focus;

    if(!empty(CHtml::$errorCss))
        $options['errorCss']=CHtml::$errorCss;

    $options=CJavaScript::encode($options);
    $cs=Yii::app()->clientScript;
    $cs->registerCoreScript('yiiactiveform');
    $id=$this->id;
    $cs->registerScript(__CLASS__.'#'.$id,"jQuery('#$id').yiiactiveform($options);");
}

And here is the controller part. You create a form but only render fields, not using it as a widget:

 $form = new ActiveForm();
 $form->enableAjaxValidation = false;
 $form->enableClientValidation = true;
 $form->id = 'quoteForm';


 $car = new \QuotesHistory();
 $this->renderPartial('car_row', compact('car', 'carMakes', 'index', 'form'), false, true);

Here is a small part of the partial view($index is actually the carCount that is passed in the get request), also this partial has a lot more fields and they all validate and submit correctly:

<div class="field-box">
            <?php
                echo $form->hiddenField($car, "[$index]id");
                echo $form->label($car, "[$index]automake_id", array('class'=>'form-label'));

                echo $form->dropDownRow($car, "[$index]automake_id", CHtml::listData($carMakes, 'id', 'name'), array(
                    'data-target' => CHtml::activeId($car, "[$index]automodel_id"),
                    'empty'=>'Select Make',
                ));

                echo $form->error($car, "[$index]automake_id", array(
                    'class'=>'alert-msg',
                    'style'=>'margin: 0;'
                ));
        ?>
    </div>

And on the last partial you need to add this in the end of the view so that the JS is registered.:

<?php
if(Yii::app()->request->isAjaxRequest)  //we dont need this in normal render  
    $form->registerJs();
?>

One final step: In order all this to works you should not double include any JS libs in the ajax response. You can do this in jQuery - http://www.eirikhoem.net/blog/2011/08/29/yii-framework-preventing-duplicate-jscss-includes-for-ajax-requests/

I've tested this code now and it works. If I remove elements I have no problem submitting the form.

OTHER TIPS

Unless I'm misunderstanding your question, I would guess this could be done with any framework:

// Make an ajax (using jQuery or whatever you want to use) call using your new member info (and team id so the member knows what team they belong to
// (JavaScript)
var data = {
  'name' : 'John Doe',
  'age' : 7,
  'team_id' : 24
}
$.post(data, "/member/add", function(data)
{
   if ( data != true ) {
     // Parse error messages here and display them in your page.
   }
});


// member controller
// (PHP)
public function postAdd()
{
  $member = new Member(Input::all());

  $validator = Validator::make(
    Input::all(),
    $rules, // Validation rules defined wherever you want
  );

  // Validation fails
  if ( $validator->fails() ) {
      $messages = $validator->messages(); // I think this returns an array. Can't remember exactly. If not, conver it to an array of error messages
      return json_encode($messages);
  }

  // Validation passes
  $member->save();

  return true;
}

The above PHP is simple syntax I would use with Laravel, which makes stuff like this stupidly easy. I don't know much about Yii but I can't imagine why it wouldn't let you do such a simple operation. Laravel is really great and super simple to learn. I suggest it to any PHP developer these days.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top