0

Trying to get a 'displayImages' array as a computed property. Using a default 'selected' property = 0. this.selected changes accordingly on mouseover and click events.

When trying to get the computed 'displayImages' it says:

"this.variations[this.selected] is undefined."

I'm using an api to get my product data and images.

<template>
  <div id="product-page">
    <v-card width="100%" class="product-card">
      <div class="image-carousel">
        <v-carousel height="100%" continuos hide-delimiters>
          <v-carousel-item
            v-for="(image, i) in displayImages"
            :key="i"
            :src="image"
          >
          </v-carousel-item>
        </v-carousel>
      </div>
      <div class="details">
        <h2>{{ this.title }}<br />Price: ${{ this.price }}</h2>
        <p>{{ this.details }}</p>
        <ul style="list-style: none; padding: 0">
          <li
            style="border: 1px solid red; width: auto"
            v-for="(color, index) in variations"
            :key="index"
            @mouseover="updateProduct(index)"
            @click="updateProduct(index)"
          >
            {{ color.color }}
          </li>
        </ul>
        <div class="buttons">
          <v-btn outlined rounded
            >ADD TO CART<v-icon right>mdi-cart-plus</v-icon></v-btn
          >
          <router-link to="/shop">
            <v-btn text outlined rounded> BACK TO SHOP</v-btn>
          </router-link>
        </div>
      </div>
    </v-card>
  </div>
</template>

<script>
export default {
  name: "Product",
  props: ["APIurl"],
  data: () => ({
    title: "",
    details: "",
    price: "",
    variations: [],
    selected: 0,
  }),
  created() {
    fetch(this.APIurl + "/products/" + this.$route.params.id)
      .then((response) => response.json())
      .then((data) => {
        //console.log(data);
        this.title = data.title;
        this.details = data.details.toLowerCase();
        this.price = data.price;
        data.variations.forEach((element) => {
          let imagesArray = element.photos.map(
            (image) => this.APIurl + image.url
          );
          this.variations.push({
            color: element.title,
            images: imagesArray,
            qty: element.qty,
            productId: element.productId,
          });
        });
      });
  },
  computed: {
    displayImages() {
      return this.variations[this.selected].images;
    },
  },
  methods: {
    updateProduct: function (index) {
      this.selected = index;
      console.log(index);
    }
  },
};
</script>
5
  • That is because when your DOM is first rendered, the this.variations array is empty and so this.variations[this.selected] returns undefined. Attempts to access .images in the undefined object will throw an error. You should add a guard clause where if it is undefined, simply return an empty array. Commented Sep 29, 2020 at 16:33
  • @Terry thank you!... any other way to prevent this maybe with the proper lifecycle hooks? or is this the way to do it.. thanks again Commented Sep 29, 2020 at 16:55
  • Not that I know of, since the array is populated asynchronously so it is always empty at the start. Commented Sep 29, 2020 at 19:02
  • @Terry thanks.. if you want me to accept your answer you need to "answer the question" down below Commented Sep 29, 2020 at 21:25
  • Consider that done :) Commented Sep 29, 2020 at 21:35

2 Answers 2

1

To properly expand on my comment, the reason why you are running into an error is because when the computed is being accessed in the template, this.variations is an empty array. It is only being populated asynchronously, so chances are, it is empty when VueJS attempts to use it when rendering the virtual DOM.

For that reason, accessing an item within it by index (given as this.selected) will return undefined. Therefore, attempting to access a property called images in the undefined object will return an error.

To fix this problem, all you need is to introduce a guard clause in your computed as such:

computed: {
  displayImages() {
    const variation = this.variations[this.selected];

    // GUARD: If variation is falsy, return empty array
    if (!variation) {
      return [];
    }

    return variation.images;
  },
}

Bonus tip: if you one day would consider using TypeScript, you can even simplify it as such... but that's a discussion for another day ;) for now, optional chaining and the nullish coalescing operator is only supported by bleeding edge versions of evergreen browsers.

computed: {
  displayImages() {
    return this.variations[this.selected]?.images ?? [];
  },
}
Sign up to request clarification or add additional context in comments.

Comments

1

For avoid this kind of error, you must to use the safe navigation property.

Remember, it's useful just when the app is loading.

Try something like that:

<script>
export default {
  name: 'Product',
  computed: {
    displayImages() {
      if (this.variations[this.selected]) {
        return this.variations[this.selected].images;
      }
      return [];
    },
  },
};
</script>

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.