9
votes

I am fetching some data using Apollo inside of Nuxt. Somehow, when navigating to that page I get an error of

Cannot read property 'image' of undefined

When I refresh the page, everything works as expected.

I have a found a few threads of people having similar issues but no solution seems to work for me :/

This is my template file right now:

/products/_slug.vue

<template>
  <section class="container">
    <div class="top">
      <img :src="product.image.url"/>
      <h1>{{ product.name }}</h1>
    </div>
  </section>
</template>

<script>
import gql from 'graphql-tag'

export default {
  apollo: {
    product: {
      query: gql`
        query Product($slug: String!) {
          product(filter: { slug: { eq: $slug } }) {
            slug
            name
            image {
              url
            }
          }
        }
      `,
      prefetch({ route }) {
        return {
          slug: route.params.slug
        }
      },
      variables() {
        return {
          slug: this.$route.params.slug
        }
      }
    }
  }
}
</script>

Basically the $apolloData stays empty unless I refresh the page. Any ideas would be much appreciated

EDIT Got one step closer (I think). Before, everything (image.url and name) would be undefined when navigating to the page for the first time.

I added:

data() {
    return {
      product: []
    };
  }

at the top of my export and now at least the name is always defined so if I remove the image, everything works as expected. Just the image.url keeps being undefined.

One thing I noticed (not sure how relevant) is that this issue only occurs using the , if I use a normal a tag it works but of course takes away the vue magic.

EDIT-2 So somehow if I downgrade Nuxt to version 1.0.0 everything works fine

3

3 Answers

7
votes

I stumbled on this issue as well, and found it hidden in the Vue Apollo documents.

Although quite similar to the OP's reply, it appears the official way is to use the "$loadingKey" property.

It's quite confusing in the documents because there are so many things going on. https://vue-apollo.netlify.com/guide/apollo/queries.html#loading-state

<template>
  <main
    v-if="!loading"
    class="my-8 mb-4"
  >
    <div class="w-3/4 mx-auto mb-16">
      <h2 class="mx-auto text-4xl text-center heading-underline">
        {{ page.title }}
      </h2>
      <div
        class="content"
        v-html="page.content.html"
      ></div>
    </div>
  </main>
</template>

<script>
import { page } from "~/graphql/page";

export default {
  name: 'AboutPage',

  data: () => ({
    loading: 0
  }),

  apollo: {
    $loadingKey: 'loading',
    page: {
      query: page,
      variables: {
        slug: "about"
      }
    },
  }
}
</script>

If you need to use a reactive property within vue such as a slug, you can do so with the following.

<template>
  <main
    v-if="!loading"
    class="my-8 mb-4"
  >
    <div class="w-3/4 mx-auto mb-16">
      <h2 class="mx-auto text-4xl text-center heading-underline">
        {{ page.title }}
      </h2>
      <div
        class="content"
        v-html="page.content.html"
      ></div>
    </div>
  </main>
</template>

<script>
import { page } from "~/graphql/page";

export default {
  name: 'AboutPage',

  data: () => ({
    loading: 0
  }),

  apollo: {
    $loadingKey: 'loading',
    page: {
      query: page,
      variables() {
        return {
          slug: this.$route.params.slug
        }
      }
    },
  }
}
</script>
4
votes

I think it's only a problem of timing on page load.

You should either iterate on products, if you have more than one, or have a v-if="product != null" on a product container, that will render only once the data is fetched from GraphQL.

In that way you'll use the object in your HTML only when it's really fetched and avoid reading properties from undefined.

1
votes

To fix this, you add v-if="!$apollo.loading" to the HTML container in which you're taying to use a reactive prop.