Computed Properties
What is a computed property?
The previous lesson used a JavaScript expression to calculate the text of "day" or "days":
<small v-else-if="daysLeft > 0">
{{ daysLeft === 1 ? "day" : "days" }} left
</small>
Once we go beyond simple expressions like this, we need a way to handle tasks outside of our HTML to keep things more maintainable. Computed properties are ideal for this.
Computed properties are used for logic containing reactive data (such as a ref()
). If any of the reactive data it contains changes, it will be re-calculated and update the template.
A method can also be used for this purpose, however methods are called manually and computed values are automatically re-evaulated when a reactive dependency is changed. Computed properties are also cached so will not run unless a change has occured.
Using computed
We will use a computed property to calculate if events are in the past, and show/hide these with a checkbox. In the App.vue
we begin by importing computed
from the Vue.js library:
<script setup>
import { ref, computed } from "vue";
Then create a const
called showPastEvents
to store the checkbox value (initially false):
<script setup>
import { ref, computed } from "vue";
import Event from "./components/Event.vue";
// create new const:
const showPastEvents = ref(false);
const eventData = ref([...
Then below our calculateDaysLeft
function, set up a computed
property:
const filteredEvents = computed(() => {
return eventData.value;
});
This will return
a new value when any of the reactive data inside is changed, and store this value into filteredEvents
. Set this to simply return eventData
for now. In the <template>
, update v-for
to loop over this new value:
<Event
v-for="event in filteredEvents"
...
>
This should display the events as before in the browser as we are still using
eventData
in the computed property.
Filtering events
We have access to the full power of JavaScript in the Vue.js <script>
section, and we can make use of methods such as filter()
.
Update the computed property to return a filtered version of our eventData
depending on the value of our showPastEvents
checkbox:
const filteredEvents = computed(() => {
return eventData.value.filter((event) => {
const daysLeft = calculateDaysLeft(event.date);
return showPastEvents.value ? true : daysLeft >= 0;
});
});
This computed property:
- Uses the
filter()
method to create a new array with events that match a condition. - For each event:
- Calculates the days left until the event using the
calculateDaysLeft
function. - Uses the ternary operator to determine if the event should be included:
- If
showPastEvents
istrue
: includes all events (returnstrue
). - If
showPastEvents
isfalse
: only includes events withdaysLeft >= 0
(future or today's events).
- If
- Calculates the days left until the event using the
Test this in the browser by toggling
showPastEvents
to betrue
/false
.
Toggling past events
We need to allow the user to be able to toggle the past events using a checkbox. Add the <div class="header">
section to our <template>
:
<template>
<div class="events-container">
<!-- add new section -->
<div class="header">
<h1 class="title">Vue Countdown</h1>
<div class="controls">
<label class="checkbox-label">
<input type="checkbox" v-model="showPastEvents" />
Show past events
</label>
</div>
</div>
<!-- new section end -->
<Event
v-for="event in filteredEvents"
Then add the following styles in the <style>
section:
.title {
font-size: 2rem;
font-weight: 600;
color: #2c3e50;
margin: 0;
}
.controls {
background: rgba(255, 255, 255, 0.1);
padding: 0.75rem 1rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.checkbox-label {
display: flex;
align-items: center;
gap: 0.75rem;
cursor: pointer;
font-size: 1rem;
color: #2c3e50;
user-select: none;
}
input[type="checkbox"] {
cursor: pointer;
width: 1.2rem;
height: 1.2rem;
border: 2px solid #4e65ff;
border-radius: 4px;
appearance: none;
transition: all 0.2s ease;
position: relative;
}
input[type="checkbox"]:checked {
background: #4e65ff;
border-color: #4e65ff;
}
input[type="checkbox"]:checked::before {
content: "✓";
position: absolute;
color: white;
font-size: 0.8rem;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
Sorting events
You may notice the order of events is not as we would expect from a countdown app. The events are not in the correct order by date. This can be updated using another JavaScript method called sort()
:
const filteredEvents = computed(() => {
return eventData.value
.filter((event) => {
const daysLeft = calculateDaysLeft(event.date);
return showPastEvents.value ? true : daysLeft >= 0;
})
.sort((a, b) => {
const daysLeftA = calculateDaysLeft(a.date);
const daysLeftB = calculateDaysLeft(b.date);
return daysLeftA - daysLeftB;
});
});
- This uses JavaScript's
sort()
method to arrange events in chronological order. - For each pair of values (
a
andb
):- Calculates days left for both events
- Returns the difference (
daysLeftA - daysLeftB
) to determine order.
Test this checkbox in the browser to toggle past events!
The result is a dynamic list that automatically updates when:
- New events are added to
eventData
. - The
showPastEvents
checkbox is switched. - Any event dates change.