alexcode / vue2vis

A Vuejs 2 adapter for Visjs
MIT License
217 stars 59 forks source link

Question: Is it possible to use a vue component for an items content? #38

Closed pleek91 closed 5 years ago

pleek91 commented 5 years ago

I'd love to be able to pass in a vue component as an items content. Has anyone figured out a way to do so?

I was able to use the template option to render vue components for each item, however vis calls the template method often when interacting with a timeline causing new vue instances to be generated each time.

template: function(item, element, data) {

    // create a class from the component that was passed in with the item
    let ComponentClass = Vue.extend(item.component);

    // create a new vue instance from that component and pass the item in as a prop
    let instance = new ComponentClass({
        propsData: { data: item },
        parent: vm
    });

    // mount it
    instance.$mount();

    // return the mounted element
    return instance.$el;
}

If I return only the rendered html from the component and destroy it then I get something that is reactive (the html rerenders whenever the item is modified) but I lose any ability to interact with the component itself (since its just html and not a vue instance).

template: function(item, element, data) {

    // create a class from the component that was passed in with the item
    let ComponentClass = Vue.extend(item.component);

    // create a new vue instance from that component and pass the item in as a prop
    let instance = new ComponentClass({
        propsData: { data: item },
        parent: vm
    });

    // mount it
    instance.$mount();

    // grab the html
    const html = instance.$el.outerHTML;

    // destroy it so we don't end up with a bunch of unnecessary vue instances
    instance.$destroy();

    // return rendered html
    return html;
}

Anyone have any experience or thoughts on this?

alexcode commented 5 years ago

Hi @pleek91, I do something similar to your first solution but I keep my component instance indexed by the item id to avoid recreating the component each time.

template: function(item, element, data) {
    let instance = componentCache[item.id];
    if(!instance) {
        // create a class from the component that was passed in with the item
        let ComponentClass = Vue.extend(item.component);

        // create a new vue instance from that component and pass the item in as a prop
        instance = new ComponentClass({
            propsData: { data: item },
            parent: vm
        });

        // mount it
        instance.$mount();
    }

    // return the mounted element
    return instance.$el;
}

Also, it's important to destroy each components in the beforeDestroy hook to avoid memory leaks.

beforeDestroy() {
    componentCache.values().forEach(c => c.$destroy());
}

Not sure that's the best solution but it avoid to recreate the component.

pleek91 commented 5 years ago

@alexcode that makes sense. But doesn't that prevent the component from receiving any changed data? Perhaps in your case the data will never change.

Thanks

alexcode commented 5 years ago

@pleek91, you're right. However, I currently pass the ID to my vue component and look at the $parent in order to workaround the reactivity issue.