3/18/2024
Vue emits with parameters
Passing events in Vue from a component back to the one that calls it is done with emits. The emits can also be done with Pinia or another library for state management, but this will be for another article.
What is a page and what is a component?
I am often asked: 'Should I extract this code into components or leave it on the page?'. I have built myself a basic rule - if I have the question, whether this code should become a component, it should become a component. I also group the components by certain characteristics. I often give the @layer directive of TailwindCSS as an example when someone wonders how to group their components.
I divide the components into 2 types:
- Speculators: Those that do not perform any business logic, but only draw and/or transfer data;
- Workers: Those that perform secondary processing of the incoming parameters and add business logic, which they return to the calling component.
Workers can often call other workers or speculators, while Speculators usually work independently.
How are parameters passed?
Let's make an example. We have a component (Speculator) that shows on the screen how many more product pages are left before the last page is reached. We call it LoadMore.vue
. The input parameters of the component are 2: 'which number is the current page' and 'how many are the total number of pages'. The component does not perform any business logic. It calculates how many more pages are left and draws them.
// components/utilities/LoadMore.vue
<script lang="ts" setup>
defineProps<{
page: number; // current page
totalPages: number; // total number of pages
}>();
</script>
<template>
<p>{{ `There are ${(totalPages - page)} more page${(totalPages - page) > 1 ? 's' : ''}` }}</p>
</template>
Direct event passing
Let's rework our component a bit so that when it is clicked, it passes the event to the calling component, which fires a function.
// components/utilities/LoadMore.vue
<script lang="ts" setup>
defineProps<{
page: number;
totalPages: number;
}>();
</script>
<template>
<div class="col-12 text-center q-mt-md q-mb-xl">
<button @click="$emit('loadMore', page)"
:label="`There are ${(totalPages - page)} more page${(totalPages - page) > 1 ? 's' : ''}`"
class="btn btn-action btn-action--micro" />
</div>
</template>
This is a direct way to pass the event. When the button is clicked, the loadMore
event and the page
parameter are passed to the calling component.
// pages/Products.vue
<script lang="ts" setup>
import LoadMore from '@/components/utilities/LoadMore.vue';
import { ref } from 'vue';
const page = ref(1);
const totalPages = ref(10);
const loadMore = (page: number) => {
console.log(`Load more products from page ${page}`);
};
</script>
<template>
<div>
<LoadMore :page="page" :totalPages="totalPages" @load-more="loadMore" />
</div>
</template>
I always use kebab-case for emitted events.
Define emit
When you want to make some changes to the parameters you are passing before emitting the event, you will need to define the emit.
// components/utilities/LoadMore.vue
<script lang="ts" setup>
defineProps<{
page: number;
totalPages: number;
}>();
const emit = defineEmits(['loadMore']);
/**
* Change the page number and emit "loadMore" with the updated number.
* @param {number} page - The current page number
*/
const changePage = (page: number) => {
const nextPage = page + 1;
emit('loadMore', nextPage);
};
</script>
<template>
<div class="col-12 text-center q-mt-md q-mb-xl">
<button @click="changePage(page)"
:label="`There are ${(totalPages - page)} more page${(totalPages - page) > 1 ? 's' : ''}`"
class="btn btn-action btn-action--micro" />
</div>
</template>
This way we now have a lot of control over the output parameters.
// pages/Products.vue
<script lang="ts" setup>
import LoadMore from '@/components/utilities/LoadMore.vue';
import { ref } from 'vue';
const page = ref(1);
const totalPages = ref(10);
const loadMore = (page: number) => {
console.log(`Load more products from page ${page}`);
};
</script>
<template>
<div>
<LoadMore :page="page" :totalPages="totalPages" @load-more="loadMore" />
</div>
</template>
This example is just illustrative. If a similar change to the page number is being made, it should be logic in the calling component. But for our example, it's a great way to see how you can take control of the returned data.
If you have an opinion or questions about the article, don't hesitate to share them.
Parcel & Robots
There are different ways to tell ParcelJS which files are static and should not go through the build transformation, but if there are only a few, you can activate the transformers plugin and then include them directly in the build script in the package.json
file.
Nitro, i18n and Dev Proxy
When you use Nuxt, it is normal to use Nitro as well, but sometimes this does not fit into the scenario you have been prepared for. The API requests are handled by another back-end server and in order to develop the application locally, a proxy must be set up.