Un tutoriel pour créer une liste d'options <select> dynamique pour choisir un pays et une ville à travers un composant Livewire dans un projet Laravel.
L'élément <select>
représente un contrôle de formulaire HTML qui fournit une liste d'options ou liste déroulante (dropdown) pour choisir une valeur.
Nous pouvons l'intégrer dans une page HTML en paramétrant ses options avec la balise <option>
de la manière suivante :
<select id="color_id" name="color_id" >
<!-- Les options -->
<option value="1" >Rouge</option>
<option value="2" >Vert</option>
<option value="3" >Bleu</option>
</select>
Nous voulons voir dans ce guide comment créer une liste d'options ou liste déroulante dynamique où l’on peut choisir un pays puis dans une autre liste déroulante, afficher les villes du pays sélectionné pour en choisir une :
Pour monter ce système de listes déroulantes dynamiques où la liste de villes présentée dépend du pays choisi, nous allons créer un composant Livewire dans un projet Laravel.
Pour présenter les pays avec leurs villes dans des listes déroulantes dynamiques, nous avons besoin de la table de pays reliée à celle de villes dans la base de données et les modèles pour accéder aux données de ces tables.
Nous devons avoir le modèle App/Models/Country.php qui représente les pays ou la table « countries ». Le schéma de cette table peut être décrit de la manière suivante dans la migration …_create_countries_table.php :
Schema::create('countries', function (Blueprint $table) {
$table->id();
$table->string("name");
$table->timestamps();
});
Nous devons aussi avoir le modèle App/Models/City.php qui représente les villes ou la table « cities ». Le schéma de cette table peut être décrit de la manière suivante dans la migration …_create_cities_table.php :
Schema::create('cities', function (Blueprint $table) {
$table->id();
$table->string("name");
$table->unsignedBigInteger("country_id");
$table->timestamps();
$table->foreign("country_id")->references("id")->on("countries");
});
La clé étrangère « country_id » relie une ville à un pays.
La relation entre le modèle App/Models/Country.php et App/Models/City.php se définit de la manière suivante au fichier Country.php :
<?php
// ...
class Country extends Model
{
use HasFactory;
public function cities () {
return $this->hasMany(City::class);
}
}
Nous pouvons traduire cette relation en disant : « Un pays a plusieurs ville ». Ce qui nous permet de récupérer toutes les villes d'un pays.
Pour installer Livewire dans un projet Laravel, nous devons exécuter la commande composer suivante :
composer require livewire/livewire
Une fois l’installation terminée, nous pouvons directement utiliser Livewire.
Pour afficher les listes d'options dynamiques de pays et villes, utilisons le composant « CountriesCitiesSelect » que nous pouvons créer en exécutant la commande artisan suivante :
php artisan make:livewire CountriesCitiesSelect
Cette commande crée deux fichiers :
Editons ces fichiers :
Avant de vous présenter le code complet du composant app/Http/Livewire/CountriesCitiesSelect.php, décortiquons sa logique qui se résume en 4 points :
1.1. Nous avons besoin de 3 propriétés :
<select>
de pays<select>
de villesCes propriétés doivent être déclarées public
pour être accessibles sur la vue :
public $country_id; // L'identifiant du pays
public $city_id; // L'identifiant de la ville
public $cities; // la collection de villes
1.2. Dans la méthode mount()
qui est appelée immédiatement après que le composant soit instancié, on affecte une collection vide à la propriété $cities :
public function mount() {
// On affecte une collection vide
$this->cities = collect();
}
Les données de $cities dépendent du pays $country_id choisi. Aucun pays n'est choisi au moment où le composant est instancié d'où on affecte une collection vide.
1.3. Quand la propriété $country_id change de valeur, c'est à dire un pays est choisi, on récupère les villes du pays $country_id qu'on affecte à la propriété $cities. On surveille le changement de $country_id à travers la méthode updatedCountryId($newValue)
:
// Quand $country_id change, on charge les $cities de $country_id
public function updatedCountryId ($newValue) {
$this->cities = City::where("country_id", $newValue)->orderBy("name")->get();
}
1.4. Dans la méthode render()
, on récupère tous les pays de la table « countries » à travers le modèle App/Models/Country.php qu'on transmet à la vue resources/views/livewire/countries-cities-select.blade.php :
public function render()
{
// On récupère les pays
$countries = Country::select("id", "name")->get();
// On retourne la vue avec les pays
return view('livewire.countries-cities-select', [
'countries' => $countries
]);
}
Le code complet
<?php
namespace App\Http\Livewire;
use Livewire\Component;
// Les modèles
use App\Models\Country;
use App\Models\City;
class CountriesCitiesSelect extends Component
{
public $country_id; // L'identifiant du pays
public $city_id; // L'identifiant de la ville
public $cities; // la collection de villes
public function mount() {
// On affecte une collection vide
$this->cities = collect();
}
// Quand $country_id change, on charge les $cities de $country_id
public function updatedCountryId ($newValue) {
$this->cities = City::where("country_id", $newValue)->orderBy("name")->get();
}
public function render()
{
// On récupère les pays
$countries = Country::select("id", "name")->get();
// On retourne la vue avec les pays
return view('livewire.countries-cities-select', [
'countries' => $countries
]);
}
}
La logique de la vue resources/views/livewire/countries-cities-select.blade.php se résument en 3 points :
2.1. Lorsque les données de pays ou de villes sont entrain d'être chargées, on affiche le texte « Chargement de données ... » en utilisant la directive wire:loading
:
<p wire:loading >Chargement de données ...</p>
2.2. On relie (Data Binding) les champs <select>
avec les propriétés $country_id et $city_id en utilisant la directive wire:model="property_name"
puis on parcourt la collection de pays et celle de villes pour afficher les listes déroulantes :
<!-- Data Binding : <select> avec la propriété $country_id -->
<select id="country_id" wire:model="country_id" >
<!-- Les pays -->
@foreach ($countries as $country)
<option value="{{ $country->id }}" >{{ $country->name }}</option>
@endforeach
</select>
<!-- Data Binding : <select> avec la propriété $city_id -->
<select id="city_id" wire:model="city_id" >
<!-- Les villes -->
@foreach ($cities as $city)
<option value="{{ $city->id }}" >{{ $city->name }}</option>
@endforeach
</select>
2.3. Pour ne pas afficher une liste déroulante de villes vide lorsqu'un pays n'est pas choisi, on vérifie avant si la collection $cities contient des éléments :
@if($cities->count())
<!-- ... -->
@endif
Le code complet
<div>
<!-- L'état de chargement de données -->
<p wire:loading >Chargement de données ...</p>
<!-- Les pays -->
<p>
<label for="country_id" >Sélectionnez un pays</label>
<!-- Data Binding : <select> avec la propriété $country_id -->
<select id="country_id" wire:model="country_id" >
<option value="" >Séléctionner un pays</option>
<!-- On parcourt la collection de pays pour afficher chaque pays -->
@foreach ($countries as $country)
<option value="{{ $country->id }}" >{{ $country->name }}</option>
@endforeach
</select>
</p>
<!-- On vérifie si la collection de villes contient des éléments -->
@if($cities->count())
<p>
<label for="city_id" >Sélectionnez une ville</label>
<!-- Data Binding : <select> avec la propriété $city_id -->
<select id="city_id" wire:model="city_id" >
<option value="" >Sélectionnez une ville</option>
<!-- On parcourt la collection de villes pour afficher chaque ville -->
@foreach ($cities as $city)
<option value="{{ $city->id }}" >{{ $city->name }}</option>
@endforeach
</select>
</p>
@endif
</div>
Sur une vue (template Blade) de l'application Laravel, nous pouvons intégrer le composant CountriesCitiesSelect, sans oublier les scripts et styles Livewire, de la manière suivante :
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Livewire : Sélection Pays et Ville</title>
<!-- Styles livewire -->
@livewireStyles()
</head>
<body>
<!-- Le composant app/Http/Livewire/CountriesCitiesSelect.php -->
@livewire("countries-cities-select")
<!-- Scripts livewire -->
@livewireScripts()
</body>
</html>
Portez-vous bien ! 😉
Cette publication vous a plu ?
Partagez-la avec vos ami(e)s sur les réseaux sociaux.
Wilo Ahadi, l'auteur
Passionné de l'informatique, je suis spécialiste en techniques des systèmes et réseaux, développeur web et mobile, infographiste et designer, ... J'aime partager mon expérience en formant sur la plateforme Akili School
Voir profil
Commentaires