Liker
Discuter
Partager
Offrir
Youtube

Laravel Livewire : liste déroulante pour choix de pays et ville

Mis à jour il y a 1 semaine

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.

Wilo Ahadi

Auteur

Wilo A.

Technologies

Laravel, PHP, Livewire

Introduction

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 choisi une ville :

liste déroulante de choix de pays et ville

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.

Prérequis

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.

Installer Livewire

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.

Créer le composant « CountriesCitiesSelect »

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 :

  • app/Http/Livewire/CountriesCitiesSelect.php : le composant Livewire
  • resources/views/livewire/countries-cities-select.blade.php : la vue (template Blade) du composant

Editons ces fichiers :

1. Le composant « CountriesCitiesSelect.php »

Avant de vous présenter le code complet du composant app/Http/Livewire/CountriesCitiesSelect.php, décortiquons sa logique qui se résume en ces 4 points :

1.1. Nous avons besoin de 3 propriétés :

  • $country_id pour enregistrer l'identifiant d'un pays choisi. Nous allons le relier (Data Binding) à la liste d'options <select> de pays
  • $city_id pour enregistrer l'identifiant de la ville choisie. Nous allons le relier à liste d'options <select> de villes
  • $cities pour enregistrer la collection de villes d'un pays $country_id

Ces 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
        ]);
    }
}

2. La vue « countries-cities-select.blade.php »

Les grandes lignes de la vue resources/views/livewire/countries-cities-select.blade.php se résument en ces 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>

Utiliser le composant « CountriesCitiesSelect »

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 ?
Encouragez-nous en la partageant sur les réseaux sociaux

Wilo Ahadi
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 Suivre

Commentaires