Formulaire modal

! Nouvelle version pour Laravel 5.2 !

On m’a souvent posé la question concernant la création d’un formulaire modal avec Bootstrap et également son interaction avec Laravel. Je vais donc dans cet article développer cet aspect en partant d’une installation toute fraiche de Laravel et en la modifiant un peu pour permettre l’enregistrement des utilisateurs à partir d’un formulaire modal.

Donc commencez par installer un Laravel 5.2 tout neuf par votre méthode préférée (l’installateur, composer…). Créez aussi une base de données pour que ça fonctionne. N’oubliez pas de changer le nom du fichier d’environnement (.env.example => .env) en mentionnant correctement les références de la base.

Vous devez vous retrouver avec le contrôleur pour l’authentification :

img01

Comme Laravel 5.2 ne comporte pas les vues, routes et autres pour l’authentification il faut les générer avec la commande :

php artisan make:auth

Vous devez alors avoir ces vues :

img87

Le contrôleur

Voici le code du contrôleur Authcontroller modifié :

<?php

namespace App\Http\Controllers\Auth;

use App\User;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
use Illuminate\Http\Request;

class AuthController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Registration & Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles the registration of new users, as well as the
    | authentication of existing users. By default, this controller uses
    | a simple trait to add these behaviors. Why don't you explore it?
    |
    */

    use AuthenticatesAndRegistersUsers, ThrottlesLogins;

    /**
     * Where to redirect users after login / registration.
     *
     * @var string
     */
    protected $redirectTo = '/';

    /**
     * Create a new authentication controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware($this->guestMiddleware(), ['except' => 'logout']);
    }

    /**
     * Get a validator for an incoming registration request.
     *
     * @param  array  $data
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => 'required|max:255',
            'email' => 'required|email|max:255|unique:users',
            'password' => 'required|min:6|confirmed',
        ]);
    }

    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return User
     */
    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
        ]);
    }

    /**
     * Handle a registration request for the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function register(Request $request)
    {
        $validator = $this->validator($request->all());

        if ($validator->fails()) {
            $this->throwValidationException(
                $request, $validator
            );
        }

        $this->create($request->all());

        return response()->json();
    }
}

J’ai surchargé la méthode postRegister du trait pour changer le code en enlevant la connexion automatique et en renvoyant une réponse JSON. La validation tient compte du fait que la requête peut être en Ajax, donc en cas d’erreur de saisie on aura automatiquement un retour correct. Si tout va bien on crée l’utilisateur et on renvoie une réponse JSON vierge.

La vue app.blade.php

Voici le code complet de cette vue :

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Laravel</title>

    <!-- Fonts -->
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.4.0/css/font-awesome.min.css" rel='stylesheet' type='text/css'>
    <link href="https://fonts.googleapis.com/css?family=Lato:100,300,400,700" rel='stylesheet' type='text/css'>

    <!-- Styles -->
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
    {{-- <link href="{{ elixir('css/app.css') }}" rel="stylesheet"> --}}

    <style>
        body {
            font-family: 'Lato';
        }

        .fa-btn {
            margin-right: 6px;
        }
    </style>
</head>
<body id="app-layout">
    <nav class="navbar navbar-default navbar-static-top">
        <div class="container">
            <div class="navbar-header">

                <!-- Collapsed Hamburger -->
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#app-navbar-collapse">
                    <span class="sr-only">Toggle Navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>

                <!-- Branding Image -->
                <a class="navbar-brand" href="{{ url('/') }}">
                    Laravel
                </a>
            </div>

            <div class="collapse navbar-collapse" id="app-navbar-collapse">
                <!-- Left Side Of Navbar -->
                <ul class="nav navbar-nav">
                    <li><a href="{{ url('/home') }}">Home</a></li>
                </ul>

                <!-- Right Side Of Navbar -->
                <ul class="nav navbar-nav navbar-right">
                    <!-- Authentication Links -->
                    @if (Auth::guest())
                        <li><a href="{{ url('/login') }}">Login</a></li>
                        <li><a href="#" id="register">Register</a></li>
                    @else
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
                                {{ Auth::user()->name }} <span class="caret"></span>
                            </a>

                            <ul class="dropdown-menu" role="menu">
                                <li><a href="{{ url('/logout') }}"><i class="fa fa-btn fa-sign-out"></i>Logout</a></li>
                            </ul>
                        </li>
                    @endif
                </ul>
            </div>
        </div>
    </nav>

    <div class="container">
        <div class="alert alert-success alert-dismissible hidden">
            You are now registered, you can login.
        </div>   
    </div> 

    @yield('content')

    <!-- Modal -->
    <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
                    <h4 class="modal-title" id="myModalLabel">Register</h4>
                </div>
                <div class="modal-body">
 
                    <form id="formRegister" class="form-horizontal" role="form" method="POST" action="{{ url('register') }}">
                        <input type="hidden" name="_token" value="{{ csrf_token() }}">
 
                        <div class="form-group">
                            <label class="col-md-4 control-label">Name</label>
                            <div class="col-md-6">
                                <input type="text" class="form-control" name="name">
                                <small class="help-block"></small>
                            </div>
                        </div>
 
                        <div class="form-group">
                            <label class="col-md-4 control-label">E-Mail Address</label>
                            <div class="col-md-6">
                                <input type="email" class="form-control" name="email">
                                <small class="help-block"></small>
                            </div>
                        </div>
 
                        <div class="form-group">
                            <label class="col-md-4 control-label">Password</label>
                            <div class="col-md-6">
                                <input type="password" class="form-control" name="password">
                                <small class="help-block"></small>
                            </div>
                        </div>
 
                        <div class="form-group">
                            <label class="col-md-4 control-label">Confirm Password</label>
                            <div class="col-md-6">
                                <input type="password" class="form-control" name="password_confirmation">
                            </div>
                        </div>
 
                        <div class="form-group">
                            <div class="col-md-6 col-md-offset-4">
                                <button type="submit" class="btn btn-primary">
                                    Register
                                </button>
                            </div>
                        </div>
                    </form>                       
 
                </div>
            </div>
        </div>
    </div>

    <!-- JavaScripts -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
    <script>
 
    $(function(){
 
        $('#register').click(function() {
            $('#myModal').modal();
        });
 
        $(document).on('submit', '#formRegister', function(e) {  
            e.preventDefault();
             
            $('input+small').text('');
            $('input').parent().removeClass('has-error');
             
            $.ajax({
                method: $(this).attr('method'),
                url: $(this).attr('action'),
                data: $(this).serialize(),
                dataType: "json"
            })
            .done(function(data) {
                $('.alert-success').removeClass('hidden');
                $('#myModal').modal('hide');
            })
            .fail(function(data) {
                $.each(data.responseJSON, function (key, value) {
                    var input = '#formRegister input[name=' + key + ']';
                    $(input + '+small').text(value);
                    $(input).parent().addClass('has-error');
                });
            });
        });
 
    })
 
    </script>
    {{-- <script src="{{ elixir('js/app.js') }}"></script> --}}
</body>
</html>

Nous allons analyser un peu tout ça…

Le formulaire

La première chose à prévoir est le formulaire d’enregistrement :

<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
                <h4 class="modal-title" id="myModalLabel">Register</h4>
            </div>
            <div class="modal-body">

				<form id="formRegister" class="form-horizontal" role="form" method="POST" action="{{ url('/auth/register') }}">
					<input type="hidden" name="_token" value="{{ csrf_token() }}">

					<div class="form-group">
						<label class="col-md-4 control-label">Name</label>
						<div class="col-md-6">
							<input type="text" class="form-control" name="name">
							<small class="help-block"></small>
						</div>
					</div>

					<div class="form-group">
						<label class="col-md-4 control-label">E-Mail Address</label>
						<div class="col-md-6">
							<input type="email" class="form-control" name="email">
							<small class="help-block"></small>
						</div>
					</div>

					<div class="form-group">
						<label class="col-md-4 control-label">Password</label>
						<div class="col-md-6">
							<input type="password" class="form-control" name="password">
							<small class="help-block"></small>
						</div>
					</div>

					<div class="form-group">
						<label class="col-md-4 control-label">Confirm Password</label>
						<div class="col-md-6">
							<input type="password" class="form-control" name="password_confirmation">
						</div>
					</div>

					<div class="form-group">
						<div class="col-md-6 col-md-offset-4">
							<button type="submit" class="btn btn-primary">
								Register
							</button>
						</div>
					</div>
				</form>                       

            </div>
        </div>
    </div>
</div>

A ce niveau il n’y a rien de bien particulier, c’est le code classique d’une page modale de Bootstrap.

Le Javascript

Voici le Javascript que j’ai prévu :

@section('scripts')
	<script>

	$(function(){

		$('#register').click(function() {
			$('#myModal').modal();
		});

		$(document).on('submit', '#formRegister', function(e) {  
			e.preventDefault();
			
			$('input+small').text('');
			$('input').parent().removeClass('has-error');
			
			$.ajax({
				method: $(this).attr('method'),
				url: $(this).attr('action'),
				data: $(this).serialize(),
				dataType: "json"
			})
			.done(function(data) {
				$('.alert-success').removeClass('hidden');
				$('#myModal').modal('hide');
			})
			.fail(function(data) {
				$.each(data.responseJSON, function (key, value) {
					var input = '#formRegister input[name=' + key + ']';
					$(input + '+small').text(value);
					$(input).parent().addClass('has-error');
				});
			});
		});

	})

	</script>
@endsection

La première chose est l’ouverture de la feuille modale lorsqu’on clique l’option Register de la barre de menu :

$('#register').click(function() {
	$('#myModal').modal();
});

On a alors la présentation du formulaire modal :

img03

On doit s’intéresser ensuite à la soumission :

$(document).on('submit', '#formRegister', function(e) {  
	e.preventDefault();
	
	$('input+small').text('');
	$('input').parent().removeClass('has-error');
	
	$.ajax({
		method: $(this).attr('method'),
		url: $(this).attr('action'),
		data: $(this).serialize(),
		dataType: "json"
	})
	.done(function(data) {
		// Action en cas de réussite
	})
	.fail(function(data) {
		// Action en cas d'échec
	});
});

On commence par effacer les références éventuelles des erreurs précédentes et on lance la requête en Ajax.

En cas d’erreur dans la saisie on a :

.fail(function(data) {
	$.each(data.responseJSON, function (key, value) {
		var input = '#formRegister input[name=' + key + ']';
		$(input + '+small').text(value);
		$(input).parent().addClass('has-error');
	});
});

On passe en revue toutes les erreurs et on les rend visibles sur le formulaire. On reçoit ce genre de JSON de la part du contrôleur :

{"name":["The name field is required."],"password":["The password field is required."]}

Et donc cet aspect :

img04Si la validation est correcte c’est ce code qui est activé :

.done(function(data) {
	$('.alert-success').removeClass('hidden');
	$('#myModal').modal('hide');
})

On rend visible la barre d’information pour l’utilisateur :

img05On pourrait adopter une autre stratégie en connectant directement l’utilisateur, là j’ai préféré l’obliger à entrer ses informations.

Pour terminer on cache la feuille modale et c’est terminé !

2 réflexions sur “Formulaire modal

  1. John Smith dit :

    Bonsoir Momo,

    Je souhaiterais généraliser ta méthode pour n’importe quel formulaire modal : avec des input mais aussi des textarea, voire autres…

    Comment modifier le javascript pour qu’il tienne compte de tous les types d’éléments de formulaire ?

    Je te remercie d’avance pour ta réponse.

    Cordialement.

    • bestmomo dit :

      Salut,

      Pour la soumission pas de souci, il n’y a aucun taitement particulier et tout passe.

      Le seul problème peut se situer au niveau du traitement des erreurs de validation, si on utilise des boutons radio ou des cases à cocher il faut les sauter dans la boucle du fail en testant le type de l’input.

Laisser un commentaire