Students predict a ticket total after two clicks, trace the total and a v-if message, find a plain calculation that goes stale, add a people count and a group deal, and make a recipe scaler.
Load these into Waypoints Prepare: choose Load URL and paste the copied address, or download the file and import it. In Prepare, the worksheet's Teacher edition shows the answers; the student copy hides them.
Predict and Run 10 min · Investigate 15 min · Modify 10 min · Make 15 min
Read the unit primer first. computed(() => adults.value * 12 + children.value * 5) gives a value Vue works out again whenever the refs it reads change. On the page it is used like a ref ({{ total }}, v-if="total >= 50"). The arrow () => is a short function that gives back what follows it; students do not need more about arrow functions than that.
The planted bug is the idea the course names as hard: a value derived from data must be computed, or it is worked out once when setup runs and then goes stale. The page shows no error. A useful contrast: a ref is data you set; a computed is data you never set, only work out.
Prices are invented for the example.
Students make the total a ref and update it in every button. Ask "what is the total worked out from?" and "what if someone adds a button and forgets?"
| What you will see | A question to ask | The fix |
|---|---|---|
| The total never changes. | Is it wrapped in computed? | const total = computed(() => …); |
| A red error: computed is not defined. | Is computed in the line that takes things from Vue? | const { createApp, ref, computed } = Vue; |
| The total shows NaN or [object Object]. | Inside computed, did every ref get its .value? | adults.value * 12, not adults * 12. |
| A computed with an if shows nothing. | Does the function return the value at the end? | Finish with return sum; |
One possible solution for each task. Other correct programs are fine.
<div id="app">
<h1>Concert tickets</h1>
<p>Adults ($12): {{ adults }}
<button @click="adults++">+</button> <button @click="adults--">−</button></p>
<p>Children ($5): {{ children }}
<button @click="children++">+</button> <button @click="children--">−</button></p>
<p>People: {{ people }}</p>
<p><b>Total: ${{ total }}</b></p>
<p v-if="total >= 50">A free programme comes with your tickets!</p>
</div>
<script src="vue.js"></script>
<script>
const { createApp, ref, computed } = Vue;
createApp({
setup() {
const adults = ref(2);
const children = ref(1);
const people = computed(() => adults.value + children.value);
const total = computed(() => adults.value * 12 + children.value * 5);
return { adults, children, people, total };
}
}).mount("#app");
</script>
<div id="app">
<h1>Concert tickets</h1>
<p>Adults ($12): {{ adults }}
<button @click="adults++">+</button> <button @click="adults--">−</button></p>
<p>Children ($5): {{ children }}
<button @click="children++">+</button> <button @click="children--">−</button></p>
<p><b>Total: ${{ total }}</b></p>
<p v-if="total >= 50">A free programme comes with your tickets!</p>
</div>
<script src="vue.js"></script>
<script>
const { createApp, ref, computed } = Vue;
createApp({
setup() {
const adults = ref(2);
const children = ref(1);
const total = computed(() => {
let sum = adults.value * 12 + children.value * 5;
if (adults.value + children.value >= 6) {
sum = sum - 10;
}
return sum;
});
return { adults, children, total };
}
}).mount("#app");
</script>
<div id="app">
<h1>Pancakes</h1>
<p>Pancakes: {{ pancakes }}
<button @click="pancakes++">+</button> <button @click="pancakes--">−</button></p>
<ul>
<li>Flour: {{ flour }} g</li>
<li>Milk: {{ milk }} ml</li>
<li>Eggs: {{ eggs }}</li>
</ul>
</div>
<script src="vue.js"></script>
<script>
const { createApp, ref, computed } = Vue;
createApp({
setup() {
const pancakes = ref(4);
const flour = computed(() => pancakes.value * 25);
const milk = computed(() => pancakes.value * 40);
const eggs = computed(() => Math.ceil(pancakes.value / 4));
return { pancakes, flour, milk, eggs };
}
}).mount("#app");
</script>
Stretch: Stop the numbers going below 0 (a computed can't do that: change the buttons, or use Math.max). Add a computed message that says how far from $50 the order is.
Support: Give the people computed written out; students only return and show it.
Why does a total made with computed stay right, when a plain calculation in setup does not?
Vue works a computed out again whenever the data it uses changes; a plain calculation runs once, when the page starts.
Next lesson: components with props. Class idea: name three values on a shopping site that are computed (total, item count, delivery cost).