Reputation: 4097
For example we have this ActiveForm
implementation in a sample view:
<?php $form = ActiveForm::begin(); ?>
<?=$form->field($model, 'first_name')->textInput(['maxlength' => true]); ?>
<?=$form->field($model, 'last_name')->textInput(['maxlength' => true]); ?>
<div id="additional-form-fields"></div>
<a href="#" id="load-additional-form-fields">
Load more fields
</a>
<?php ActiveForm::end(); ?>
Now, I want to add more ActiveField
/ ActiveForm
fields inside this form and place them in the #additional-form-fields
element with Ajax, I'd do a simple jQuery
callback:
$('#load-additional-form-fields').click(function() {
$.get('/site/additional-fields', {}, function(data) {
$('#additional-form-fields').html( data );
});
});
And the action additional-fields
inside SiteController
would be something as:
public function actionAdditionalFields() {
$model = new User;
return $this->renderAjax('additional-fields', [
'model' => $model,
// I could pass a 'form' => new ActiveForm, here, but it's a big NO-NO!
]);
}
And this works perfectly, only if I don't use any other ActiveField
fields inside this action's view:
<?=$form->field($model, 'biography')->textInput(['maxlength' => true]); ?>
<?=$form->field($model, 'country')->textInput(['maxlength' => true]); ?>
<?=$form->field($model, 'occupation')->textInput(['maxlength' => true]); ?>
Of course, I have to pass or instatiate $form
somehow in this view, but it's NOT an option to use another ActiveForm::begin()
/ ActiveForm::end()
anywhere inside this view since it will create another <form>
tag and thus when I inject the Ajax response, I'll end up with with a <form>
inside a <form>
...
Now, my question is as follows: Since I want to use ActiveForm
, how can I share an instance of the ActiveForm
through out multiple requests?
Is it doable / possible, if so, please help me realize how?
So far I have tried to put $form
inside a session, but that's definitelly not working and not an option. Different than that, I've tried when passing parameters to renderAjax
:
[
'model' => $model,
'form' => new ActiveForm,
]
In this case I get the following:
<script src="...">
... you get the idea)Is there anyway to share an instance of $form
?
Upvotes: 2
Views: 796
Reputation: 4097
Okay, I have manage to do this, so I'll post the solution here and I'll open an issue on Github - might be useful in future versions.
yii2\widgets\ActiveForm.php
I've added a following property to the ActiveForm
class:
/**
* @var boolean whether to echo the form tag or not
*/
public $withFormTag = true;
And I've changed run()
method into this (check for // <-- added
):
public function run()
{
if (!empty($this->_fields)) {
throw new InvalidCallException('Each beginField() should have a matching endField() call.');
}
$content = ob_get_clean();
if($this->withFormTag) { // <-- added
echo Html::beginForm($this->action, $this->method, $this->options);
} // <-- added
echo $content;
if ($this->enableClientScript) {
$id = $this->options['id'];
$options = Json::htmlEncode($this->getClientOptions());
$attributes = Json::htmlEncode($this->attributes);
$view = $this->getView();
ActiveFormAsset::register($view);
$view->registerJs("jQuery('#$id').yiiActiveForm($attributes, $options);");
}
if($this->withFormTag) { // <-- added
echo Html::endForm();
} // <-- added
}
Thus if we instantiate a form like this:
$form = ActiveForm::begin([
'withFormTag' => false,
]);
It will not echo
a <form>
tag, but will render all ActiveField
items and it will create their respective JavaScript/jQuery validators if $this->enableClientScript = true;
.
After applying the previous fix in the base class, I needed to do the following in my view:
<?php $form = ActiveForm::begin([
'withFormTag' => false,
'id' => 'w0',
]); ?>
I had to pass the id
parameter since every next instance of the ActiveForm class is incremented by 1
, and I want my JavaScript/jQuery validators to be applied to the parent form, which by default starts from 0
-> w0
.
And this is what did the trick!
Here's the Github issue as well: https://github.com/yiisoft/yii2/issues/12973
Upvotes: 1