javascript - Vue.js $scopedSlots don't work for Vue instance - Stack Overflow

I'm working in a Vue ponent that I'll publish when it's finished that wraps Clusterize.j

I'm working in a Vue ponent that I'll publish when it's finished that wraps Clusterize.js (there is a vue-clusterize ponent but it only works for v1.x). What I want to achieve is to render a huge list of items pretty fast using Vue. I actually need it for a table. I tried with vue-virtual-scroll but it doesn't support tables and the performance is not that good. So I wanted to try with Clusterize.js.

Because I want this ponent to be highly configurable I decided that you will be able to provide a scoped slot for each row of the items list where you will receive the item. The problem is when I try to assign the scoped slot from the clusterize ponets to each row before mounting the ponent it doesn't work.

Here you have some snippets of my code (it is just a mvp)

clusterize.vue

Template

<div class="clusterize">
<table>
  <thead>
    <tr>
      <th>Headers</th>
    </tr>
  </thead>
</table>
<div
  ref="scroll"
  class="clusterize-scroll">
  <table>
    <tbody
      ref="content"
      class="clusterize-content">
      <tr class="clusterize-no-data">
        <td>Loading...</td>
      </tr>
    </tbody>
  </table>
</div>

Script

import Vue from 'vue';
import Clusterize from 'clusterize.js';

export default {
  name: 'Clusterize',
  props: {
    items: {
      type: Array,
      required: true,
    },
  },
  data() {
    return {
      clusterize: null,
    };
  },
  puted: {
    rows() {
      return this.items.map(item => '<tr><slot :item="1"/></tr>');
    },
  },
  watch: {
    rows() {
      this.clusterize.update(this.rows);
    },
  },
  mounted() {
    const scrollElem = this.$refs.scroll;
    const contentElem = this.$refs.content;

    this.clusterize = new Clusterize({
      rows: this.rows,
      scrollElem,
      contentElem,
    });

    this.clusterize.html = (template) => {
      contentElem.innerHTML = template;
      const instance = new Vue({ el: contentElem });

      instance.$slots = this.$slots;
      instance.$scopedSlots = this.$scopedSlots;
      instance.$mount();

      console.log(instance.$scopedSlots); // empty
      console.log(instance.$slots) // not empty
    };
  },
};

ponent.vue

<clusterize :items="test">
  <template slot-scope="props">
    item
  </template>
</clusterize>

The thing is that if it don't use a scoped slot it works perfectly but I really need to use them otherwise the ponent doesn't have any sense.

I'll appreciate any help or advice. Thank you so much in advance.

I'm working in a Vue ponent that I'll publish when it's finished that wraps Clusterize.js (there is a vue-clusterize ponent but it only works for v1.x). What I want to achieve is to render a huge list of items pretty fast using Vue. I actually need it for a table. I tried with vue-virtual-scroll but it doesn't support tables and the performance is not that good. So I wanted to try with Clusterize.js.

Because I want this ponent to be highly configurable I decided that you will be able to provide a scoped slot for each row of the items list where you will receive the item. The problem is when I try to assign the scoped slot from the clusterize ponets to each row before mounting the ponent it doesn't work.

Here you have some snippets of my code (it is just a mvp)

clusterize.vue

Template

<div class="clusterize">
<table>
  <thead>
    <tr>
      <th>Headers</th>
    </tr>
  </thead>
</table>
<div
  ref="scroll"
  class="clusterize-scroll">
  <table>
    <tbody
      ref="content"
      class="clusterize-content">
      <tr class="clusterize-no-data">
        <td>Loading...</td>
      </tr>
    </tbody>
  </table>
</div>

Script

import Vue from 'vue';
import Clusterize from 'clusterize.js';

export default {
  name: 'Clusterize',
  props: {
    items: {
      type: Array,
      required: true,
    },
  },
  data() {
    return {
      clusterize: null,
    };
  },
  puted: {
    rows() {
      return this.items.map(item => '<tr><slot :item="1"/></tr>');
    },
  },
  watch: {
    rows() {
      this.clusterize.update(this.rows);
    },
  },
  mounted() {
    const scrollElem = this.$refs.scroll;
    const contentElem = this.$refs.content;

    this.clusterize = new Clusterize({
      rows: this.rows,
      scrollElem,
      contentElem,
    });

    this.clusterize.html = (template) => {
      contentElem.innerHTML = template;
      const instance = new Vue({ el: contentElem });

      instance.$slots = this.$slots;
      instance.$scopedSlots = this.$scopedSlots;
      instance.$mount();

      console.log(instance.$scopedSlots); // empty
      console.log(instance.$slots) // not empty
    };
  },
};

ponent.vue

<clusterize :items="test">
  <template slot-scope="props">
    item
  </template>
</clusterize>

The thing is that if it don't use a scoped slot it works perfectly but I really need to use them otherwise the ponent doesn't have any sense.

I'll appreciate any help or advice. Thank you so much in advance.

Share Improve this question asked Aug 9, 2018 at 16:24 andreszandresz 531 silver badge5 bronze badges 1
  • 1 Can you please provide a reproduction (jsfiddle)? – Marlon Barcarol Commented Aug 9, 2018 at 16:55
Add a ment  | 

1 Answer 1

Reset to default 2

The issue should be caused by mount different Vue instance to same el multiple times (please look into the second demo, you shouldn't mount multiple instances to same element, the following instances will not mount since the element is already “blocked” by first instance).

My solution: create Vue instance (doesn't bind to el) in the air then take vm.$el as the output.

Please look into below simple demo,

Vue.config.productionTip = false
Vue.ponent('clusterize', {
  template: `<div class="clusterize">
<table>
  <thead>
    <tr>
      <th>Headers</th>
    </tr>
  </thead>
</table>
<div
  ref="scroll"
  class="clusterize-scroll">
  <table>
    <tbody
      ref="content"
      id="clusterize-id"
      class="clusterize-content">
      <tr class="clusterize-no-data">
        <td>Loading...</td>
      </tr>
    </tbody>
  </table>
</div></div>`,
  props: {
    items: {
      type: Array,
      required: true,
    },
  },
  data() {
    return {
      clusterize: null,
      clusterVueInstance: null
    };
  },
  puted: {
    rows() {
      return this.items.map(item => {
      	return '<tr><td><span>' +item+'</span><slot :item="1"/></td></tr>'
      });
    },
  },
  watch: {
    rows() {
      this.clusterize.update(this.rows);
    },
  },
  mounted() {
    const scrollElem = this.$refs.scroll;
    const contentElem = this.$refs.content;

    this.clusterize = new Clusterize({
      rows: this.rows,
      scrollElem,
      contentElem,
    });
		
    this.clusterize.html = (template) => {
      this.clusterize.content_elem.innerHTML = template;
      if(this.clusterVueInstance) {
				this.clusterVueInstance.$destroy()
        this.clusterVueInstance = null
      }
      
      this.clusterVueInstance = new Vue({  template: '<tbody>'+template+'</tbody>' })
      //or use Vue.extend()
      this.clusterVueInstance.$slots = this.$slots
      this.clusterVueInstance.$scopedSlots = this.$scopedSlots
      this.clusterVueInstance.$mount()
      this.clusterize.content_elem.innerHTML = this.clusterVueInstance.$el.innerHTML
      //console.log(this.clusterVueInstance.$scopedSlots); // empty
      //console.log(this.clusterVueInstance.$slots) // not empty*/
    };
  }
})

app = new Vue({
  el: "#app",
  data() {
    return {
      test: ['Puss In Boots', 'test 1', 'test2'],
      index: 0
    }
  },
  mounted: function () {
  	//this.test = ['Puss In Boots', 'test 1', 'test2']
  },
  methods: {
    addItem: function () {
      this.test.push(`test ` + this.index++)
    }
  }
})
<link href="https://cdn.bootcss./clusterize.js/0.18.0/clusterize.min.css" rel="stylesheet"/>
<script src="https://unpkg./[email protected]/dist/vue.js"></script>
<script src="https://cdn.bootcss./clusterize.js/0.18.0/clusterize.min.js"></script>
<div id="app">
  <button @click="addItem()">
  Add Item
  </button>
  <clusterize :items="test">
  <template slot-scope="props">
    item: {{props.item}}
  </template>
  </clusterize>
</div>

Please look into below demo: created multiple Vue instance to same el, but Vue always uses first instance to render (I can't find any useful statement at Vue Guide, probably from the source codes from Vue Github we can find out the logic. If someone knows, please feel free to edit my answer or add a ment).

Vue.config.productionTip = false
app1 = new Vue({
  el: '#app',
  data () {
    return {
    test: 'test 1'
    }
  },
  mounted(){
    console.log('app1', this.test)
  }
})

app2 = new Vue({
  el: '#app',
  data () {
    return {
    test: 'test 2'
    }
  },
  mounted(){
    console.log('app2', this.test)
  }
})
//app1.$data.test = 3
//app1.$mount() //manual mount
app2.$data.test = 4
app2.$mount() //manual mount
<script src="https://unpkg./[email protected]/dist/vue.js"></script>
<script src="https://cdn.bootcss./clusterize.js/0.18.0/clusterize.min.js"></script>
<div id="app">
  <a>{{test}}</a>
</div>

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745554399a4632729.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信