Loading the Menu

The café's menu changes with the seasons, so it doesn't belong in the code. It's in a file, menu.json, and the app loads it, as you loaded files with fetch() in the JavaScript course. In a component, the place for that is onMounted:

<script setup>
import { onMounted, ref } from "vue";

const menu = ref([]);

onMounted(async () => {
    const response = await fetch("menu.json");
    menu.value = await response.json();
});
</script>

Here's what happens:

  1. Vue creates the component: menu is an empty array.
  2. Vue mounts it: the page shows the component, with an empty list, and Vue calls the function given to onMounted. It asks for menu.json and waits.
  3. The answer comes a moment later. menu.value = … puts the menu into the ref, and Vue updates the page, as after any other change of state.

The function is async, so it can use await, as the functions that loaded data in the JavaScript course did. async goes before the parentheses of the arrow function.

Why not write the two lines with await right at the top of <script setup>, with the rest of the code? This exercise starts that way, and the page shows nothing, not even the heading. The console says:

[Vue warn]: Component <App>: setup function returned a promise, but no <Suspense> boundary was found in the parent component tree. A component with async setup() must be nested in a <Suspense> in order to be rendered.
  at <App>

An await at the top makes the whole component wait: Vue can't finish creating it until the data is there, and it shows such a component only inside <Suspense>, a part of Vue that's still experimental and that this course doesn't use. So never put await at the top of <script setup>. Load data in onMounted, and let the page show something in the meantime.

Such as Loading…: on a slow connection, the data can take a few seconds to arrive. A ref that's true until the data is there tells the template what to show.

Instructions

  1. Load the menu in onMounted: import it next to ref, and move the two lines with await into onMounted(async () => { … });. The page shows the heading, and a moment later the menu.

  2. While the menu is loading, show <p>Loading…</p> instead of the list. Add a ref loading that starts as true, set it to false in onMounted once the menu is there, and show the paragraph with v-if="loading" and the list with v-else.