So, this is what I finally got:
The main form that is very long and time-consuming
...
<?php
// renders the ajax login form at the end of the form script
echo $this->render('//site/_ajax_login', [
'mainFormId' => 'very-long-and-time-consuming-form', // id of the main form
]);
The ajax login form ‘site/_ajax_login.php’
<?php
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use app\models\LoginForm;
use yii\bootstrap\Modal;
/* @var $this yii\web\View */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $mainFormId string */
$model = new LoginForm();
if (!Yii::$app->user->isGuest) {
$model->username = Yii::$app->user->identity->username; // username for login credential
}
?>
<?php Modal::begin([
'header' => 'Re-enter your password',
'toggleButton' => false,
'id' => 'ajax-login-modal',
'size' => Modal::SIZE_DEFAULT,
]); ?>
<?php $form = ActiveForm::begin([
'id' => 'ajax-login-form',
'action' => ['/site/ajax-login'],
'options' => ['class' => 'form-horizontal'],
'fieldConfig' => [
'template' => "{label}\n<div class=\"col-sm-3 col-xs-8\">{input}</div>",
'labelOptions' => ['class' => 'col-sm-3 col-xs-4 control-label'],
],
]); ?>
<?= $form->field($model, 'username')->textInput(['readonly' => true]) ?>
<?= $form->field($model, 'password')->passwordInput() ?>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-3 col-xs-offset-4 col-xs-8">
<?= Html::submitButton('Submit', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?>
</div>
</div>
<?php ActiveForm::end(); ?>
<?php Modal::end();
$checkUrl = Yii::$app->urlManager->createUrl(['/site/login-check']);
$loginUrl = Yii::$app->urlManager->createUrl(['/site/ajax-login']);
$this->registerJs("
var loginchecked = false;
$('#$mainFormId').on('beforeValidate', function(event, messages, deferreds){
if (!loginchecked) {
$.post('$checkUrl','', function(data){
if (data === 'user') {
doSubmit();
} else {
loginModal = true;
$('#ajax-login-modal').modal('show');
}
}, 'json');
return false;
} else {
return;
}
});
function doSubmit() {
loginchecked = true;
setTimeout(function(){
loginchecked = false;
}, 1000);
$('#$mainFormId').submit();
}
$('#ajax-login-form').on('beforeValidate', function(event, messages, deferreds) {
$.post('$loginUrl',$(this).serialize(), function(data){
if (data === 'user') {
$('#ajax-login-modal').modal('hide');
doSubmit();
} else {
alert('Wrong password.');
}
}, 'json');
return false;
});
");
SiteController
...
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'only' => ['logout'],
'rules' => [
[
'actions' => ['logout'],
'allow' => true,
'roles' => ['@'],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'logout' => ['post'],
'login-check' => ['post'],
'ajax-login' => ['post'],
],
],
];
}
...
...
public function actionLoginCheck()
{
$response = Yii::$app->response;
$response->format = \yii\web\Response::FORMAT_JSON;
$response->data = Yii::$app->user->isGuest ? 'guest' : 'user';
}
public function actionAjaxLogin()
{
$response = Yii::$app->response;
$response->format = \yii\web\Response::FORMAT_JSON;
$model = new LoginForm();
if ($model->load(Yii::$app->request->post()) && $model->login()) {
$response->data = 'user';
} else {
$response->data = 'guest';
}
}
...
It seems working fine, at least for the moment.
At first I was trying to use on.(‘submit’, function(){}) and got confused by a strange behavior of the ActiveForms.
By replacing it with on.(‘beforeValidate’, function(){}) I got the expected result.
Please let me know if you have noticed something wrong.