Composants
Création et utilisation d'un composant
Les composants sont des éléments réutilisables qui permettent de structurer une application Vue en morceaux indépendants. Il s'agit d'un fichier .vue
qui a vocation à être importé ou réutilisé.
Imaginons dans notre cas que nous ayons extrait la logique de notre Counter
précédemment et que nous cherchons à l'importer.
// Counter.component.vue
<script setup lang="ts">
import { ref } from 'vue';
const counter = ref(0);
const doubleCounter = computed(() => counter.value * 2);
const reset = () => {
counter.value = 0;
};
</script>
<template>
<div>
<p>Compteur : {{ counter }}</p>
<p>Double compteur : {{ doubleCounter }}</p>
<button @click="counter++">Incrémenter</button>
<button v-bind:disabled="counter===0" @click="reset">Réinitialiser</button>
</div>
</template>
// App.vue
<script setup>
import {ref} from 'vue';
import CounterComponent from './components/counter.component.vue';
const connectedUser = ref(null);
const users = ref([
{name: 'John', age: 25},
{name: 'Jane', age: 24},
{name: 'Jack', age: 30},
]);
</script>
<template>
<div
>
<CounterComponent/>
<div>
<p v-if="connectedUser">Connected User : {{ connectedUser }}</p>
<ul>
<li v-for="user in users" :key="user.name">
{{ user.name }} - {{ user.age }} years
</li>
</ul>
<input v-model="connectedUser" type="text">
</div>
</div>
</template>
<style scoped>
div {
margin-top: 2em;
display: block
}
</style>
Props: Passer des données aux composants
Les props permettent de transmettre des données d'un composant parent à un composant enfant.
Dans notre cas nous allons modifier la logique de noter code lié aux utilisateurs. Je voudrais un composant qui prend en paramètre une liste d'utilisateurs et les affiche sous forme de liste.
Composant Enfant (UserCollection.component.vue
)
<template>
<div class="user-list">
<h2>Liste des utilisateurs</h2>
<ul>
<li v-for="user in users" :key="user.name">
{{ user.name }} - {{ user.age }} ans
</li>
</ul>
</div>
</template>
<script lang="ts" setup>
import {defineProps} from 'vue';
import type {User} from '../types/common.type.ts';
const props = defineProps({
users: {
type: Array as () => User[],
required: true,
},
});
</script>
Composant Parent (App.vue
)
<script lang="ts" setup>
import {ref} from 'vue';
import CounterComponent from './components/Counter.component.vue';
import UserCollection from './components/UserCollection.component.vue';
import type {User} from './types/common.type.ts';
const connectedUser = ref(null);
const users = ref<User[]>([
{name: 'John', age: 25},
{name: 'Jane', age: 24},
{name: 'Jack', age: 30},
]);
</script>
<template>
<div
>
<CounterComponent/>
<div>
<p v-if="connectedUser">Connected User : {{ connectedUser }}</p>
<UserCollection :users="users"/>
<input v-model="connectedUser" type="text">
</div>
</div>
</template>
<style scoped>
div {
margin-top: 2em;
display: block
}
</style>
On renseigne la props users
dans le composant parent App.vue
et on l'utilise dans le composant enfant UserCollection.component.vue
.
Émission d'événements entre composants
Lorsqu'un composant enfant doit envoyer une information au parent, on utilise $emit
.
Supposons que lors d'un click sur l'un items de la liste utilisateurs je souhaite afficher cet utilisateur dans mon composant parent.
Composant Enfant (UserCollection.component.vue
)
<template>
<button @click="envoyerMessage">Cliquez-moi</button>
</template>
<script>
export default {
methods: {
envoyerMessage() {
this.$emit("messageEnvoye", "Bonjour depuis le composant enfant !");
}
}
};
</script>
Composant Parent (App.vue
)
<script lang="ts" setup>
import {ref} from 'vue';
import CounterComponent from './components/Counter.component.vue';
import UserCollection from './components/UserCollection.component.vue';
import type {User} from './types/common.type.ts';
const selectedUser = ref<User | null>(null);
const users = ref<User[]>([
{name: 'John', age: 25},
{name: 'Jane', age: 24},
{name: 'Jack', age: 30},
]);
const userReceived = (user: User) => {
selectedUser.value = user;
};
</script>
<template>
<div
>
<CounterComponent/>
<div>
<p v-if="selectedUser">Selected User : {{ selectedUser.name }}</p>
<UserCollection :users="users" @send-user="userReceived"/>
</div>
</div>
</template>
<style scoped>
div {
margin-top: 2em;
display: block
}
</style>
Slots : insérer du contenu personnalisé
Les slots permettent de passer du contenu HTML au sein d'un composant. On peut même appeler d'autres composants à l'intérieur d'un slot.
Composant Enfant (UserCollection.component.vue
)
<template>
<div class="user-list">
<h2>Liste des utilisateurs</h2>
<ul>
<li v-for="user in users" :key="user.name" @click="sendUser(user)">
{{ user.name }} - {{ user.age }} ans
</li>
</ul>
<slot></slot>
</div>
</template>
<script lang="ts" setup>
import {defineProps} from 'vue';
import type {User} from '../types/common.type.ts';
const props = defineProps({
users: {
type: Array as () => User[],
required: true,
},
});
const emit = defineEmits(['sendUser']);
const sendUser = (user: User) => {
emit('sendUser', user);
};
</script>
Composant Parent (App.vue
)
<script lang="ts" setup>
import {ref} from 'vue';
import CounterComponent from './components/Counter.component.vue';
import UserCollection from './components/UserCollection.component.vue';
import type {User} from './types/common.type.ts';
const selectedUser = ref<User | null>(null);
const newUser = ref({name: '', age: 0});
const users = ref<User[]>([
{name: 'John', age: 25},
{name: 'Jane', age: 24},
{name: 'Jack', age: 30},
]);
const userReceived = (user: User) => {
selectedUser.value = user;
};
const addUser = () => {
if (newUser.value.name && newUser.value.age) {
users.value.push({...newUser.value});
}
};
</script>
<template>
<div
>
<CounterComponent/>
<div>
<p v-if="selectedUser">Selected User : {{ selectedUser.name }}</p>
<UserCollection :users="users" @send-user="userReceived">
<div class="form-group">
<input v-model="newUser.name" placeholder="Nom"/>
<input v-model="newUser.age" placeholder="Âge" type="number"/>
<button @click="addUser">Valider</button>
</div>
</UserCollection>
</div>
</div>
</template>