'Delay' in the attribution/existence of properties in the Vue instance

Asked

Viewed 71 times

2

I have the following code:

Vue.component('child-comp', {
  template: '<p>Child here</p>',
  props: ['item'],
  methods: {
     alt: function(msg) {
    	alert(msg);
     }
  }
});

new Vue({
  el: '#app',
  data() {
     return {
        item: null
     }
  },
  methods: {
     load_child: function() {
        this.item = true;
        this.$refs['comp1'].alt(this.item.toString());
     }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>

<div id="app">
  <button v-on:click="load_child">execute child</button>
  <child-comp v-bind:item="item" v-if="item != null" ref="comp1"></child-comp>
</div>

As you can see at the first click gives error, saying:

Cannot read Property 'alt' of Undefined / Typeerror: this. $refs.comp1 is Undefined

When starting from the previous line this component (this.item = true;) should already exist/be defined

To get around it I did:

Vue.component('child-comp', {
  template: '<p>Child here</p>',
  props: ['item'],
  methods: {
     alt: function(msg) {
    	alert(msg);
     }
  }
});

new Vue({
  el: '#app',
  data() {
    return {
        item: null
    }
  },
  methods: {
     load_child: function() {
        this.item = true;
        setTimeout(() => {this.$refs['comp1'].alt(this.item.toString());}, 100);
     }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>

<div id="app">
  <button v-on:click="load_child">execute child</button>
  <child-comp v-bind:item="item" v-if="item != null" ref="comp1"></child-comp>
</div>

But I don’t want it and it doesn’t seem right at all.

I’m looking for an alternative to this, did I implement something wrong, or should have done something I didn’t do/know there is.

  • It wouldn’t be enough to run this.alt() within the mounted of child-comp?

  • Yes @Sergio , in this case I put yes, but in the project I’m doing there will be arguments to get into "alt()", which are dynamic and vary according to the parent component

  • I still think you should wear props. Being dependent on asynchrony can cause problems. If alt() be asynchronous that will give other problems.

  • @Sergio, all right I believe and I know you know what you are saying. But I could give an example of what your solution would be, as an answer. I accepted the one because it seems to me the most suitable so far. I edited the answer to which alt() receive arguments

  • I gave an answer with what I would do in the case that seems to be yours. If the question is more detailed it is easier to answer.

2 answers

2

What is happening is that you must wait for the change of the DOM, because the value you need is in it. vuejs has the solution for these types of behavior, is the nextTick. This command is used to wait for the DOM change after the above code and as soon as the DOM is changed, it will be executed. See the documentation at this link to learn more.

Vue.component('child-comp', {
  template: '<p>Child here</p>',
  props: ['item'],
  methods: {
    alt: function() {
      alert(this.item);
    }
  }
});

new Vue({
  el: '#app',
  data() {
    return {
      item: null
    }
  },
  methods: {
    load_child: function() {
      this.item = true;

      // LINHA ADICIONADA
      this.$nextTick(function() {
        this.$refs['comp1'].alt();
      });

    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>

<div id="app">
  <button v-on:click="load_child">execute child</button>
  <child-comp v-bind:item="item" v-if="item != null" ref="comp1"></child-comp>
</div>

2


In similar cases I usually use arrays, pass the content by props and display the elements in the DOM depending on this array. So everything is sequential, with no possible running conditions.

Vue.component('child-comp', {
  template: '<div><h3>{{item.title}}</h3><p>{{item.body}}</p></div>',
  props: ['item']
});
const url = 'https://jsonplaceholder.typicode.com/posts/';
new Vue({
  el: '#app',
  data() {
    return {
      items: [],
      postNr: 1
    }
  },
  methods: {
    load_child: function() {
      axios.get(url + this.postNr++)
        .then(res => this.items.push(res.data))
        .catch(e => console.log(e));
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.17.0/axios.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>



<div id="app">
  <button v-on:click="load_child">load post</button>
  <child-comp :item="item" :key="item.id" v-for="item in items"></child-comp>
</div>

  • Hello Sergio, I posted a question some time ago here: en.stackoverflow.com/questions/252268/... . You think there’s a better solution than the one I answered? If there is please answer that will be accepted with pleasure, perhaps exchange the current component for NotFound. (sorry to text you here, no other way to talk to you)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.