Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue <script setup> Top level await causing template not to render

I'm using the new syntax in Vue 3 and I really like the idea of it, but once I tried to use a top level await I started to run in some problems.

This is my code:

<template>
  <div class="inventory">
    <a class="btn btn-primary">Test button</a>
      <table class="table">
        <thead>
          <tr>Name</tr>
          <tr>Description</tr>
        </thead>
        <tbody>
          <tr v-for="(item, key) in inventory" :key="key">
            <td>{{ item.name }}</td>
            <td>{{ item.description }}</td>
          </tr>
        </tbody>
      </table>
  </div>
</template>

<script setup lang="ts">
import { apiGetInventory, GetInventoryResponse } from '@/module/inventory/common/api/getInventory'

const inventory: GetInventoryResponse = await apiGetInventory()
</script>

As you can see it's not that complicated, the apiGetInventory is just an axios call so I won't bother going into that. The problem is, that if I have this top level await, my template doesn't render anymore, it's just a blank page in my browser. If I remove the two lines of code, it works fine. Also the promise seems to revolve just fine, if I place a console.log(inventory) underneath it I get an array with objects all fine and dandy.

Anyone have a clue what's going wrong here?

like image 425
olvenmage Avatar asked Sep 14 '21 19:09

olvenmage


People also ask

What is setup () in Vue?

setup can also return a render function which can directly make use of the reactive state declared in the same scope: js import { h, ref } from 'vue' export default { setup() { const count = ref(0) return () => h('div', count. value) } }

What is top level await?

Top level awaitYou can use the await keyword on its own (outside of an async function) within a JavaScript module. This means modules, with child modules that use await , wait for the child module to execute before they themselves run, all while not blocking other child modules from loading.

How do I use a Vue template in JavaScript?

Vue uses an HTML-based template syntax that allows you to declaratively bind the rendered DOM to the underlying component instance's data. All Vue templates are syntactically valid HTML that can be parsed by spec-compliant browsers and HTML parsers.

What is SFC in Vue?

Vue Single-File Components (a.k.a. *.vue files, abbreviated as SFC) is a special file format that allows us to encapsulate the template, logic, and styling of a Vue component in a single file.


2 Answers

Top-level await must be used in combination with Suspense (which is experimental).

You should be able to just do it in onBeforeMount. Not as elegant; but a solid solution. Something like this:

<script setup lang="ts">
import { apiGetInventory, GetInventoryResponse } from '@/module/inventory/common/api/getInventory';
import {ref, onBeforeMount} from 'vue';

const inventory = ref<GetInventoryResponse>()

onBeforeMount( async () => {
    inventory.value = await apiGetInventory()
})
</script>
like image 109
tauzN Avatar answered Nov 03 '22 01:11

tauzN


Using onBeforeMount is good, but there are a couple of other options.

@skirtle suggested in Vue Discord chat to do the initialization inside an async lambda or function (possibly as an IIFE):

<script setup lang="ts">
let inventory: GetInventoryResponse
const loadData = async () => inventory = apiGetInventory()
loadData()
</script>

@wenfang-du suggested in How can I use async/await in the Vue 3.0 setup() function using Typescript to use promise chaining:

<script setup lang="ts">
let inventory: GetInventoryResponse 
apiGetInventory().then(d: GetInventoryResponse => inventory = d)
</script>

The benefit of doing so is that the code is run before the beforeMount lifecycle hook.

You additionally need to take care of error handling as appropriate in both cases.

like image 33
mrts Avatar answered Nov 02 '22 23:11

mrts