I solved it.
I made a view which renders another view based on guest or not, and a javascript to act on submits on those child views. The controller returns the child views, popolated with error data if needed, with renderAjax.
This is a basic example with login form and modal for signup in one view, and user panel in the other. It assumes you’re using bootstrap and jquery in some asset.
user-panel.php
[spoiler]
<?php
use app\models\LoginForm;
use app\models\SignupForm;
/* @var $this yii\web\View */
$js = <<<JS
$(function() {
/**
* Using $(document).on() is not the best in performance,
* but it's needed because of the html replacement done by ajax,
* which removes and rebuilds DOM elements, fucking up something like $('#login-form').on()
**/
// login
$(document).on('submit', '#login-form', function(event) {
event.preventDefault();
console.log('submit login form');
$.ajax({
type: 'post',
url: $('#login-form').attr('action'),
data: $("#login-form").serialize(),
dataType: 'json'
})
.done(function(data) {
console.log('data obtained:');
console.log(JSON.stringify(data, null, 4));
if ( data.success ) {
$('#user-panel-container').html(data.view);
}
else
console.log('error: ' + data.error_msg);
});
});
// logout
$(document).on('click', '#logout-link', function(event) {
event.preventDefault();
console.log('click logout link');
$.ajax({
type: 'post',
url: $('#logout-link').attr('href'),
data: '', // maybe not needed, i didn't try without
dataType: 'json'
})
.done(function(data) {
console.log('data obtained:');
console.log(JSON.stringify(data, null, 4));
if ( data.success ) {
$('#user-panel-container').html(data.view);
}
else
console.log('error: ' + data.error_msg);
});
});
// signup
$(document).on('submit', '#signup-form', function(event) {
event.preventDefault();
console.log('submit signup form');
$.ajax({
type: 'post',
url: $('#signup-form').attr('action'),
data: $('#signup-form').serialize(),
dataType: 'json'
})
.done(function(data) {
console.log('data obtained:');
console.log(JSON.stringify(data, null, 4));
if ( data.error ) {
console.log('error: ' + data.error_msg);
}
// controller redirects to home if succesfull, so i just write errors in log
});
});
});
JS;
$this->registerJs($js);
?>
<div class="panel panel-default" id="user-panel-container">
<?php
// renders initial view, based on guest or not
if ( Yii::$app->user->isGuest )
echo $this->render('_login', [
'form_login' => new LoginForm(),
'form_signup' => new SignupForm()
]);
else
echo $this->render('_logged', [
'utente' => Yii::$app->user->identity
]);
?>
</div>
[/spoiler]
_login.php: (child view, login/signup form)
[spoiler]
<?php
use yii\helpers\Html;
use yii\helpers\Url;
use yii\bootstrap\ActiveForm;
use yii\bootstrap\Modal;
use app\models\LoginForm;
use app\models\SignupForm;
/* @var $this yii\web\View */
/* @var $form_login app\models\LoginForm */
/* @var $form_signup app\models\SignupForm */
$this->title = 'Login';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="panel-body">
<h2>Login</h2>
<?php
$form = ActiveForm::begin([
'id' => 'login-form',
'options' => [
'class' => 'form-horizontal'
],
'action' => Url::toRoute(['//user/login'])
]);
?>
<?= $form->field($form_login, 'email') ?>
<?= $form->field($form_login, 'password')->passwordInput() ?>
<?= $form->field($form_login, 'rememberMe')->checkbox() ?>
<div class="btn-group">
<button type="submit" class="btn" name="login-button">
<span>Login</span>
</button>
</div>
<?php ActiveForm::end(); ?>
<div class="site-signup">
<?php
Modal::begin([
'options' => [
'id' => modal-signup'
],
'toggleButton' => [
'tag' => 'a',
'label' => 'Crea un account',
'class' => 'sw-single-link',
],
]);
$form = ActiveForm::begin([
'id' => 'signup-form',
'options' => ['class' => 'form-horizontal'],
'action' => Url::toRoute(['//user/signup']),
]);
echo $form->field($form_signup, 'username');
echo $form->field($form_signup, 'password')->passwordInput();
echo $form->field($form_signup, 'password_repeat')->passwordInput();
echo $form->field($form_signup, 'email')->input('email');
?>
<div class="row">
<div class="col-md-6">
<a type="button" class="btn" data-dismiss="modal" aria-label="Close">
<span>Close</span>
</a>
</div>
<div class="col-md-6">
<button type="submit" class="btn" name="signup-button">
<span>Signup</span>
</button>
</div>
</div>
<?php
ActiveForm::end();
Modal::end();
?>
</div>
</div>
[/spoiler]
_logged.php: (user panel, logout link)
[spoiler]
<?php
use yii\helpers\Url;
/* @var $this yii\web\View */
/* @var $user app\models\Utente */
$this->title = 'User Panel';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="panel-body">
<h2>Hello <?= $user->username ?>!</h2>
</div>
<div class="panel-footer">
<ul>
<li><a href="<?= Url::toRoute(['user/profile']) ?>">Edit profile</a></li>
<li><a href="#">Whishlist</a></li>
<li><a href="#">More Ice Cream</a></li>
</ul>
</div>
<div class="panel-footer">
<ul>
<li>
<a id="logout-link" href="<?= Url::toRoute(['user/logout']) ?>">Logout</a>
</li>
</ul>
</div>
[/spoiler]
UserController: (actions only)
[spoiler]
/**
* This are just the needed actions.
* You can change them, but remember to change in the form inside views.
**/
use app\models\LoginForm;
use app\models\SignupForm;
public function actionLogin()
{
$post = Yii::$app->request->post();
$form_login = new LoginForm();
Yii::$app->response->format = Response::FORMAT_JSON;
if ( $form_login->load($post) ) {
if ( $form_login->login() ) {
// login success
return [
'success' => 1,
'view' => $this->renderAjax('//site/_logged', [
'user' => Yii::$app->user->identity
])
];
}
// login errors, $form_login->load($post) above should set some sort of errors with $this->addError() inside model
return [
'success' => 1,
'view' => $this->renderAjax('//site/_login', [
'form_login' => $form_login,
'form_signup' => new SignupForm()
])
];
}
return [
'error' => 1,
'error_msg' => 'parameters error'
];
}
public function actionLogout()
{
$post = Yii::$app->request->post();
Yii::$app->response->format = Response::FORMAT_JSON;
if ( ! Yii::$app->user->isGuest ) {
Yii::$app->user->logout();
return [
'success' => 1,
'view' => $this->renderAjax('//site/_login', [
'form_login' => new LoginForm(),
'form_signup' => new SignupForm()
])
];
}
return [
'error' => 1,
'error_msg' => "user not logged, can't logout"
];
}
public function actionSignup()
{
$post = Yii::$app->request->post();
$form_signup = new SignupForm();
Yii::$app->response->format = Response::FORMAT_JSON;
// signup
if ( $form_signup->load($post) && $form_signup->signup() ) {
return $this->goHome();
}
// signup errors
return [
'success' => 1,
'view' => $this->renderAjax('//site/_login', [
'form_login' => new LoginForm(),
'form_signup' => $form_signup
])
];
}
[/spoiler]