Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prop passed to child component is undefined in created method

Tags:

vue.js

vuejs2

I am using Vue.js 2.

I have a problem with passing value to the child component as a prop. I am trying to pass card to card-component.

In card-component I can access the prop in the Card goes here {{card}} section.

However when I try to access it in created or mounted methods it's undefined.

Parent:

<template>
    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">

                <card-component :card="place.card"></card-component>

            </div>
        </div>
    </div>
</template>

<script>
    import CostComponent from './CostComponent';
    import CardComponent from './CardComponent';

    export default {
        components: {
            CostComponent, CardComponent
        },

        props: ['id'],

        data() {
            return {
                place: []
            }
        },

        created() {
            axios.get('/api/places/' + this.id)
            .then(response => this.place = response.data);
        }
    }
</script>

Child:

<template>
    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <ul class="list-unstyled">

                    Card goes here {{card}}

                </ul>
            </div>
        </div>
    </div>
</template>

<script>
    import CardItemComponent from './CardItemComponent';

    export default {
        components: {
            CardItemComponent
        },

        props: ['card'],

        created() {
            console.log(this.card); // undefined
        },

        mounted() {
            console.log(this.card); // undefined
        },
    }
</script>

I did a lot of googling but none of the solutions I found have fixed my issue.

like image 759
kid_plama Avatar asked Mar 03 '19 23:03

kid_plama


1 Answers

This is purely a timing issue. Here's what happens...

  1. Your parent component is created. At this time it has an empty array assigned to place (this is also a problem but I'll get to that later). An async request is started
  2. Your parent component creates a CardComponent instance via its template

    <card-component :card="place.card"></card-component>
    

    at this stage, place is still an empty array, therefore place.card is undefined

  3. The CardComponent created hook runs, logging undefined
  4. The CardComponent is mounted and its mounted hook runs (same logging result as created)
  5. Your parent component is mounted
  6. At some point after this, the async request resolves and changes place from an empty array to an object, presumably with a card property.
  7. The new card property is passed down into your CardComponent and it reactively updates the displayed {{ card }} value in its template.

If you want to catch when the card prop data changes, you can use the beforeUpdate hook

beforeUpdate () {
  console.log(this.card)
}

Demo

Vue.component('CardComponent', {
  template: '<pre>card = {{ card }}</pre>',
  props: ['card'],
  created () {
    console.log('created:', this.card)
  },
  mounted () {
    console.log('mounted:', this.card)
  },
  beforeUpdate () {
    console.log('beforeUpdate:', this.card)
  }
})
new Vue({
  el: '#app',
  data: {
    place: {}
  },
  created () {
    setTimeout(() => {
      this.place = { card: 'Ace of Spades' }
    }, 2000)
  }
})
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
  <card-component :card="place.card" />
</div>

See https://vuejs.org/v2/guide/instance.html#Lifecycle-Diagram


If place is meant to be an object, you should not be initialising it as an array. Also, if your CardComponent relies on data being present, you may want to conditionally render it.

For example

data () {
  return { place: null }
}

and

<card-component v-if="place" :card="place.card"></card-component>

then CardComponent will only be created and mounted after place has data.

like image 150
Phil Avatar answered Nov 15 '22 10:11

Phil