ajax/pjax user login panel

Hello,

I’m trying to create a sidebar panel which should show a login form if user is not logged and a menu/logout if user is logged.

I want the panel to update it’s content using ajax/pjax:

  • user visits site and a login form is presented

  • user logs in and the menu replaces the login form

  • user logs out and the login form replaces the menu

all without reloading the entire page but only the panel.

So far I tried using ajax/pjax but I’m having a hard time managing login form submission/validation and changing the view (yes, I accomplished almost nothing…). I tried using [font=“Lucida Console”]renderPartial[/font] to return through ajax the correct view based on situation, but it didn’t work.

What do you think should be my approach?

When we sign in via ajax and update our panel based on return html, the problem is that the session cookie remains the same.

Usually without ajax, when sign in happens, the session cookie is changed. You may have to do something about this session cookie to get the result you want.

No, I don’t have session problems. I’m having problems replacing one view with the other through ajax.

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]