Vue Suspense in Ionic view

483 Views Asked by At

I have this view:

<template>
  <ion-page>
    <ion-router-outlet></ion-router-outlet>
    <suspense>
      <template #default>
        {{ pages }}
      </template>
      <template #fallback>
        <div>Loading...</div>
      </template> 
    </suspense>
  </ion-page>
</template>
...
async setup() {
    const storageRef = storage.ref();
    const bookRef = storageRef.child("content/books/bk0000001");
    const pages = await bookRef.listAll();
    console.log({ pages }); // is logged correctly

    return { pages }
}

Although it seems to load the content fine but the template is not rendered, the page remains empty neither the default not the fallback content is displayed.

What am I doing wrong?

1

There are 1 best solutions below

0
On BEST ANSWER

When a component's setup() is async, the component has to be within a <suspense> in the parent of the component (not in the component itself).

You should move the {{ pages }} markup and the data fetching from the async setup() into its own component:

<!-- AsyncPages.vue -->
<template>
  <ul>
    <li v-for="page in pages" :key="page.id">{{ page.title }}</li>
  </ul>
</template>

<script>
import { defineComponent } from 'vue'

export default defineComponent({
  async setup() {
    const resp = await fetch('https://jsonplaceholder.typicode.com/posts')
    const pages = await resp.json()
    return { pages }
  }
})
</script>

And update the parent to use the new component in a <suspense>:

<!-- Parent.vue -->
<template>
  <ion-page>
    <ion-router-outlet></ion-router-outlet>
    <suspense>
      <template #default>
        <AsyncPages />
      </template>
      <template #fallback>
        <div>Loading...</div>
      </template>
    </suspense>
  </ion-page>
</template>

<script>
import { defineComponent } from 'vue'
import AsyncPages from '@/components/AsyncPages.vue'

export default defineComponent({
  components: {
    AsyncPages
  }
})
</script>

demo 1

Alternatively, you could rewrite the above into a non-async setup(), using an onMounted async hook instead:

<!-- Parent.vue -->
<template>
  <ion-page>
    <ion-router-outlet></ion-router-outlet>
    <ul v-if="pages">
      <li v-for="page in pages" :key="page.id">{{ page.title }}</li>
    </ul>
    <div v-else>Loading...</div>
  </ion-page>
</template>

<script>
import { defineComponent, onMounted, ref } from 'vue'

export default defineComponent({
  setup() {
    const pages = ref(null)

    onMounted(async () => {
      const resp = await fetch('https://jsonplaceholder.typicode.com/posts')
      pages.value = await resp.json()
    })

    return { pages }
  }
})
</script>

demo 2