0

I want a way to run a function (which talks to the backend) whenever a component is re-displayed.

I understand that the mounted hook will fire if the component is re-added to the DOM by a v-if directive. But, if the component is hidden and re-shown via a v-show directive, this will not fire. I need to update the component regardless of what directive is in control of it's visibility.

I looked at the updated hook but this seems to not be the indented use case.

How do I run a function whenever a component is displayed (not only for the first time)?

CarterKF
  • 63
  • 2
  • 7
  • Give the component a required prop called `visible` or something like that and watch it for changes – Phil Apr 20 '20 at 00:37
  • @Phil ideally I am looking for a more compact way to do this. If for example I had two nested `v-show` directives I would have to watch for both of the conditions. – CarterKF Apr 20 '20 at 01:32
  • Sounds inefficient. You could run a loop to [detect the visibility](https://stackoverflow.com/questions/19669786/check-if-element-is-visible-in-dom) of `this.$el` (say using [`requestAnimationFrame()`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)) but IMO it's much better to be imperative and inform your component about its visibility through props. – Phil Apr 20 '20 at 02:21

3 Answers3

2

updated fires whenever data passed to your component changes. Therefore it will work if you pass in whatever condition controls your v-show, as a prop.

Generic example:

Vue.config.devtools = false;
Vue.config.productionTip = false;
Vue.component('child', {
  props: {
    shown: {
      type: Boolean,
      default: true
    }
  },
  template: '<div>{{shown}}</div>',
  mounted() {
    console.log('child mounted');
  },
  updated() {
    // runs whenever any prop changes
    // (optional condition) only run when component is shown
    if (this.shown) {
      console.log('child updated');
    }
  }
});
new Vue({
  el: '#app',
  data: () => ({
    showChild: true
  })
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <label><input type="checkbox" v-model="showChild" /> Show child</label>
  <child v-show="showChild" :shown="showChild" />
</div>

Now updated hook works properly, because it fires everytime :shown changes its value, which maps precisely on your show/hide logic.

tao
  • 82,996
  • 16
  • 114
  • 150
  • I think this implementation gets a bit complex. The component could potentially be nested within multiple `v-show` clauses. Then I would have to pass each condition into the prop. Would I not? – CarterKF Apr 21 '20 at 18:04
  • @Carter, I'd argue your model gets a bit complex, not the implementation. But you can ***easily*** simplify it: if you always add in the parent `shown` property to the child's condition, changing the parent `shown` will propagate to all children, any number of levels deep. The condition is to always add in the current level's `shown` into any child level's `shown` and never break the chain (`:shown="shown && showChild"`). Besides, what other options do you have? – tao Apr 22 '20 at 08:41
  • Anyway, there's a discrepancy between the level of complexity we're discussing here and what your question asks for. Please present a [mcve] and I might be able to advise on a better suited model for it. – tao Apr 22 '20 at 08:54
  • okay I am going to accept your answer as I think it is the best presented. I might ask another question later more specific to my needs (with a some examples). Thanks your help. – CarterKF Apr 23 '20 at 18:15
0

maybe you can achieve it in two ways

1.use :key whenever you want to rerender your component whether it is shown, change the value of key can rerender it.

<template>
    <h1 :key="key">Text</h1>
</template>
<script>
export default{
    data(){
        return {
            key:this.getRandomString()
        }
    },
    methods(){
        getRandomString(length = 32) {
          let chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
          let max_pos = chars.length;
          let random_string = '';
          for (let i = 0; i < length; i++) {
            random_string += chars.charAt(Math.floor(Math.random() * max_pos));
          }
          return random_string;
        },
        yourMethod(){
            // communicate with backend
            let data = await axios.get(...);

            this.key = this.getRandomString();
        }
    }
}
</script>
  1. use vm.$forceUpdate()
...
    yourMethod(){
        // communicate with backend
        let data = await axios.get(...);

        this.$forceUpdate();
    }
...
DengSihan
  • 2,119
  • 1
  • 13
  • 40
-1

you could implement this in a couple of ways. However since you would like to got the v-show way, here is how I would suggest you go about it.

v-show (v-show, watcher):

The v-show is definitely dependent on a variable (data, or computed). Create a watcher, to watch that data/computed property change. Depending on the value of the data/computed property, execute whatever function you intend to on the watcher.

Tolumide
  • 944
  • 9
  • 11
  • I see what your saying, but ideally I would like the logic to be encapsulated within the component. As the component can be used in multiple places and it would be ideal for it to be generic – CarterKF Apr 20 '20 at 01:29