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)">←</button>
{{ currentPage + 1 }} / {{ numberOfPages }}
<button :disabled="this.currentPage +1 >= numberOfPages" @click="changePage">→</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)">←</button>
{{ currentPage + 1 }} / {{ numberOfPages }}
<button :disabled="this.currentPage +1 >= numberOfPages" @click="changePage">→</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 !
woow merci c’est assez clair et compréhensible
😊😊