eiko
eiko

Reputation: 5345

Vue prop undefined in initial call to computed functions

I have the following Vue component:

Vue.component('result', {
  props: ['stuff'],
  data: () => ({}),
  template: "<img :src='tag' class='result'></img>",
  computed: {
    tag: function tag() {
      return `pages/search/img/${this.stuff.type.toLowerCase()}_tag.png`;
    }
  }
});

When the component is created, an error is thrown:

TypeError: Cannot read property 'toLowerCase' of undefined
  at VueComponent.tag

However, when I remove the call to toLowerCase(), the method executes properly, generating a string with the expected type. I could work around this by changing my filenames to have capital letters, but I would rather understand why Vue is behaving this way. Why would a property be undefined only when methods are called on it?

Update: after some troubleshooting, I found that this.stuff.type is undefined the first time tag() is computed. Calling toLowerCase() just forces an error on an otherwise silent bug. Is there a reason the props aren't defined when computed functions are called for the first time? Should I be writing my component differently?

Upvotes: 5

Views: 3385

Answers (2)

Ricky Ruiz
Ricky Ruiz

Reputation: 26791

The stuff prop is undefined when the result component is created.

There are two options to fix this problem:

Either use the v-if directive in the parent component's template to make sure stuff has a value when the result component is created:

<template>
  <result v-if="stuff" :stuff="stuff" />
</template>

Or handle the stuff prop being undefined in the result component.

Vue.component('result', {
  props: {
    // Object with a default value
    stuff: {
      type: Object,
      // Object or array defaults must be returned from
      // a factory function
      default: () => ({ type: 'someType'})
    },
  },

  data: () => ({}),

  template: "<img :src='tag' class='result' >",

  computed: {
    tag: function tag() {
      return `pages/search/img/${this.stuff.type.toLowerCase()}_tag.png`;
    }
  }
})

Note: The img element is a void element, it does not require an end tag.

Upvotes: 4

BTL
BTL

Reputation: 4666

Props are by default null but you can give them a default value to overcome this problem.

Example :

Vue.component('result', {
  props: {
    stuff: {
      type: Object,
      default: {
        type: ''
      }
    }
  },
  data: () => ({}),
  template: "<img :src='tag' class='result'></img>",
  computed: {
    tag: function tag() {
      return `pages/search/img/${this.stuff.type.toLowerCase()}_tag.png`;
    }
  }
});

Upvotes: 0

Related Questions