Ok, now it’s time to submit some forms.
First of all, we configure our ActiveForm slightly, to set our onsubmit listener.
Best way (the only way?..) to do it is beforeSubmit, it fires after client validation, but before actual submit.
Handler function takes one argument: "jqueryfied" form.
<?php $form = ActiveForm::begin([
'id' => 'my-form-id', // Optional. I just like to keep things under control
'validateOnType' => true, // Optional, but it helps to get rid of extra click
'beforeSubmit' => new \yii\web\JsExpression('function($form) {
$.post(
$form.attr("action"),
$form.serialize()
)
.done(function(result) {
$form.parent().html(result);
})
.fail(function() {
console.log("server error");
});
return false;
}'),
]); ?>
As you can see, I’ve set beforeSubmit callback that calls well-known jquery’s ajax post().
I can take submit url from default form’s action using $form.attr(“action”).
In case of successful ajax call, I can take the result and do whatever I want. Here I’m just replacing form’s parent element.
If you hate writing JS inside PHP (as I do), you can just pass the name of the handler function:
<?php $form = ActiveForm::begin([
'beforeSubmit' => 'postForm',
]); ?>
and then write actual processing code somewhere else:
<script>
function postForm($form) {
$.post(
$form.attr("action"),
$form.serialize()
)
.done(function(result) {
if (typeof(result) === "string") {
// form is invalid, html returned
$form.parent().html(result);
} else {
// form is valid and saved, json returned
if (result.redirect) {
window.location = result.redirect; // ok, that's weird, but who cares?..
};
}
})
.fail(function() {
console.log("server error");
});
return false;
}
</script>
In this example I have extra check for typeof(result), so that I can return HTML or JSON from controller’s action.
Now the controller part.
Here’s simple action that renders the ‘_create’ partial view (that is, our form) in case of server validation failure,
or ‘_success’ partial view in case of saving.
public function actionCreate()
{
$view = '_create';
$record = new MyModel;
if ($record->load($_POST) && $record->save()) {
$record->refresh(); // just in case of some DB logic
$view = '_success';
}
return $this->renderAjax($view, [
'record' => $record,
]);
}
Or you can return HTML in case of error and JSON in case of success. Or something else. It’s up to you.
public function actionCreate()
{
$record = new MyModel;
if ($record->load($_POST) && $record->save()) {
$record->refresh(); // just in case of some DB logic
Yii::$app->response->format = 'json';
return [
'record' => $record->toArray(),
'redirect' => Url::toRoute(['site/somewhere', 'from' => 'created']),
];
}
return $this->renderAjax('edit', [
'record' => $record,
]);
}
As you have noticed, renderAjax() is used to render the partials.
It’s a special function that not only renders the view, but also adds JS and CSS files registered from the view.
Check this and this for details.