Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vuejs - keep-alive component toggled with v-if

###The Problem I have a child component that may or may not exist on the page with a v-if. Trying to keep it cached when the user has clicked other things so that search terms and whatnot show up again when the user returns fails no matter how I try to cache with <keep-alive>.

###What I've tried The documentation seems to indicate that all I need to do is wrap my component in a <keep-alive> tag and things should just work. I tried matching some documentation that uses the <component> tag which clearly doesn't work. I also tried using the include prop since just wrapping it doesn't work.

Vue.component('child', {
  template: '<div>child: {{text}}<div>',
  data() {return {text: ""}},
  created(){
    this.$nextTick(() => {
      this.text = `${Math.round(Math.random() * 100)}`
  })
},
  activated: function() {
    this.$nextTick(() => {
        console.log('in activated');
    });
  }
})

var vm = new Vue({
  el: '#app',
  data: function() {
    return {
      showNow:false,
      message: 'This is a test.'
    }
  },
  methods: {
    changeText: function() {
      this.message = 'changed';
    },
    toggle() {
      this.showNow = !this.showNow
    }
  },
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
  <keep-alive include="child">
    <div v-if="showNow">
        <h4>Title of section to be toggled</h4>
        <component is="child"></component>
    </div>
  </keep-alive>
  
  <button @click="toggle()">toggle child</button>
</div>

I've also tried not using include and just wrapping like so <keep-alive><child></child></keep-alive> or using the <component> syntax and neither of those work either.

If you remove the conditional rendering, the activated hook is called, but that obviously defeats the purpose of <keep-alive>! As it stands right now, that hook is never called, which is frustrating.

Also, the example in the documentation doesn't help either because they're not using v-if, just changing which components are rendered via a string....

P.S. I have no idea how to tag this other than Vue.js

like image 885
dlkulp Avatar asked Jan 20 '20 06:01

dlkulp


Video Answer


1 Answers

Here is a lightly modified version of your code with keep-alive working correctly:

Vue.component('child', {
  template: '<div>child: {{text}}</div>',
  data() {return {text: ""}},
  created(){
    console.log('in created')
    this.$nextTick(() => {
      this.text = `${Math.round(Math.random() * 100)}`
    })
  },
  activated: function() {
    this.$nextTick(() => {
    	console.log('in activated');
    });
  }
})

var vm = new Vue({
  el: '#app',
  data: function() {
    return {
      showNow:false,
      message: 'This is a test.'
    }
  },
  methods: {
    changeText: function() {
      this.message = 'changed';
    },
    toggle() {
      this.showNow = !this.showNow
    }
  },
});
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<div id="app">
  <keep-alive>
    <child v-if="showNow"></child>
  </keep-alive>
  
  <button @click="toggle()">toggle child</button>
</div>

The created hook is only called the first time. On subsequent activation the activated hook is called but not created.

The key change is that the <child> component must be a direct child of <keep-alive>. This direct child must also be the component that has the v-if.

You can't use a <div> for this purpose as <keep-alive> specifically looks for a component, not just an element. See:

https://github.com/vuejs/vue/blob/ec78fc8b6d03e59da669be1adf4b4b5abf670a34/src/core/components/keep-alive.js#L85

Another common mistake with keep-alive is to put a v-if on the keep-alive component itself, or on one of its ancestors. This won't work as the keep-alive component itself will be destroyed. The keep-alive component maintains a cache of child components but if the keep-alive is itself destroyed then that cache is lost.

like image 141
skirtle Avatar answered Oct 07 '22 11:10

skirtle