Aller au contenu
Accueil » Créer un tableau filtrable dynamique en VueJS

Créer un tableau filtrable dynamique en VueJS

Je vous propose aujourd’hui une manière simple de réaliser un tableau filtrable et tout cela dynamiquement, à l’aide de VueJS. Sachez que pour utiliser VueJS, nul besoin d’avoir un environnement complet, ni même un serveur d’actif. Nous allons tout bêtement utiliser le CDN disponible, qui nous permettra d’importer le cadre de travail Vue, tout comme n’importe quelle librairie Javascript.

En premier lieu, créons notre instance vierge :

<!DOCTYPE html>
<html lang="en">
<head>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
          type="text/css">
    <title>Vue search sample</title>
</head>
<body>
<div id="app">
    {{ test }}
</div>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            test: 'Hello',          
        },
       
    })
</script>
</body>
</html>

Ici, on importe en premier lieu le CDN de Vue, ainsi que celui de Bootstrap, histoire de mettre un peu de design dans notre résultat. On crée tout simplement une div que l’on associe dans notre partie script à un element Vue. Et ensuite, on est capable à l’intérieur de ce contexte de faire tourner notre application. Vous pouvez donc ouvrir ce fichier HTML dans votre navigateur, et vous verrez le résultat suivant apparaître :

Maintenant que Vue est fonctionnel, passons à notre structure et nos données. Nous prendrons pour cet exemple la liste des pays du monde. Une API gratuite nous permet de récupérer toutes ces informations, à l’URL suivante : https://countriesnow.space/api/v0.1/countries/flag/images

On les récupèrera grâce à la méthode fetch :

fetch('https://countriesnow.space/api/v0.1/countries/flag/images')
.then(response => response.json())
.then(countries => {
    this.elements = countries.data
    this.sortElements()
})

Ce tableau d’éléments rempli, il nous faut un coup de HTML pour afficher tout ça. Ajoutons également nos inputs de recherche, une pagination, ainsi qu’une possibilité de contrôler combien d’éléments sont affichés par page :

<div id="app">
    <div>
        <form>
            Query: <input type="search" placeholder="Search..." v-model="searchQuery" @keyup="sortElements">
            Elements per page: <input type="number" min="1" placeholder="Number of elements per page..."
                                      v-model="elementsPerPage" @keyup="sortElements">
            Ignore case: <input type="checkbox" v-model="ignoreCase" @change="sortElements">
            <input type="reset">
        </form>
        <hr>
        <table class="table table-striped w-100" v-if="elements.length">
            <tr>
                <th>Country</th>
            </tr>
            <tr v-for="element in currentElements">
                <td>
                    <img :src=element.flag height="25px">
                    <span>{{ element.name }}</span>
                </td>
            </tr>
        </table>
        <div v-else>List is empty.</div>
        <br>
        <button :disabled="this.currentPage === 0" @click="changePage(false)">&larr;</button>
        {{ currentPage + 1 }} / {{ numberOfPages }}
        <button :disabled="this.currentPage +1 >= numberOfPages" @click="changePage">&rarr;</button>
    </div>
</div>

On se retrouve enfin avec notre tableau de pays bien remplis, avec notre pagination et notre possibilité de filtre :

Une fois ce visuel créé, il nous faut les méthodes Javascript qui nous permettront de pouvoir faire nos actions. Parmi elles :

  • Une méthode pour filtrer nos éléments selon le texte que l’on a rentré dans notre input query
  • Une méthode permettant de changer de page, qui sera un classique slice d’un tableau, prenant un offset et une limit
  • Les éléments courants à afficher dans notre tableau, qui revient à utiliser notre méthode de filtre
  • Associer tous les éléments utiliser dans notre front, à des variables définies dans la partie data, et ainsi leur associer des v-model pour assurer le dynamisme de la page

Nous aurons donc la partie script suivante pour assurer le tout :

<script>
    var app = new Vue({
        el: '#app',
        data: {
            elements: [],
            elementsSorted: [],
            searchQuery: "",
            ignoreCase: true,
            currentPage: 0,
            elementsPerPage: 10,
        },
        mounted() {
            fetch('https://countriesnow.space/api/v0.1/countries/flag/images')
                .then(response => response.json())
                .then(countries => {
                    this.elements = countries.data
                    this.sortElements()
                })
        },
        computed: {
            numberOfPages() {
                return Math.ceil(this.elementsSorted.length / this.elementsPerPage)
            },
            currentElements() {
                return this.elementsSorted.slice(
                    this.currentPage * this.elementsPerPage,
                    this.currentPage * this.elementsPerPage + this.elementsPerPage
                )
            }
        },
        methods: {
            sortElements() {
                this.elementsSorted = this.elements.filter((element) => {
                    let elementTransformed = this.ignoreCase ? element.name.toUpperCase() : element.name
                    let searchQueryTransformed = this.ignoreCase ? this.searchQuery.toUpperCase() : this.searchQuery
                    return elementTransformed.includes(searchQueryTransformed)
                })
            },
            changePage(next = true) {
                this.currentPage += next ? 1 : -1
            },
        }
    })
</script>

Et voilà le résultat en live :

Si on agrège les parties de codes explicitées ci-avant, on peut donc en sortir le fichier complet suivant :

<!DOCTYPE html>
<html lang="en">
<head>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
          type="text/css">
    <title>Vue search sample</title>
</head>
<body>
<div id="app">
    <div>
        <form>
            Query: <input type="search" placeholder="Search..." v-model="searchQuery" @keyup="sortElements">
            Elements per page: <input type="number" min="1" placeholder="Number of elements per page..."
                                      v-model="elementsPerPage" @keyup="sortElements">
            Ignore case: <input type="checkbox" v-model="ignoreCase" @change="sortElements">
            <input type="reset">
        </form>
        <hr>
        <table class="table table-striped w-100" v-if="elements.length">
            <tr>
                <th>Country</th>
            </tr>
            <tr v-for="element in currentElements">
                <td>
                    <img :src=element.flag height="25px">
                    <span>{{ element.name }}</span>
                </td>
            </tr>
        </table>
        <div v-else>List is empty.</div>
        <br>
        <button :disabled="this.currentPage === 0" @click="changePage(false)">&larr;</button>
        {{ currentPage + 1 }} / {{ numberOfPages }}
        <button :disabled="this.currentPage +1 >= numberOfPages" @click="changePage">&rarr;</button>
    </div>
</div>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            elements: [],
            elementsSorted: [],
            searchQuery: "",
            ignoreCase: true,
            currentPage: 0,
            elementsPerPage: 10,
        },
        mounted() {
            fetch('https://countriesnow.space/api/v0.1/countries/flag/images')
                .then(response => response.json())
                .then(countries => {
                    this.elements = countries.data
                    this.sortElements()
                })
        },
        computed: {
            numberOfPages() {
                return Math.ceil(this.elementsSorted.length / this.elementsPerPage)
            },
            currentElements() {
                return this.elementsSorted.slice(
                    this.currentPage * this.elementsPerPage,
                    this.currentPage * this.elementsPerPage + this.elementsPerPage
                )
            }
        },
        methods: {
            sortElements() {
                this.elementsSorted = this.elements.filter((element) => {
                    let elementTransformed = this.ignoreCase ? element.name.toUpperCase() : element.name
                    let searchQueryTransformed = this.ignoreCase ? this.searchQuery.toUpperCase() : this.searchQuery
                    return elementTransformed.includes(searchQueryTransformed)
                })
            },
            changePage(next = true) {
                this.currentPage += next ? 1 : -1
            },
        }
    })
</script>
</body>
</html>

Et voilà, pas si complexe non ?

N’hésitez pas à utiliser ce datatable à votre convenance, et à le pimper pour ajouter encore plus de fonctionnalités. On pourrait penser par exemple à de la suppression directe dans le tableau, ajouter des actions de masse… Bref, la seule limite limite est votre imagination !

2 commentaires sur “Créer un tableau filtrable dynamique en VueJS”

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *