DreadedSlug
DreadedSlug

Reputation: 583

Bootstrap Popover Content not Reactive Vue2

I am trying to create a component for a popover using Bootstrap4 in Vue:

<template>
    <div>
        <div :id="uid + '_Content'" class="d-none">
            <slot name="content">

            </slot>
        </div>
        <div :class="'call' + uid">
            <slot name="caller">

            </slot>
        </div>
    </div>
</template>

<script>
module.exports = {
    data() {
        return {
            uid: this.genUID()
        }
    }, 
    props: {
        title: {
            type: String,
            required: false,
            default: ''
        }
    },
    mounted() {
        _this = this;
        // PopOver Definition
        const popover = new bootstrap.Popover(document.querySelector('.call' + this.uid), {
            container: 'body',
            title: this.title,
            html: true,
            placement: 'auto',
            sanitize: false,
            customClass: 'noselect',
            content: () => {
                return document.querySelector("#" + _this.uid + "_Content").innerHTML;
            }
        });
    },
    methods: {
        genUID() {
            return "Popover_" + Math.random().toString(16).slice(2);
        }
    }
}
</script>

However, when passing content to <slot name="content"> from another component, the data is not reactive. Is there any config information that I am missing to make it reactive? Is this even possible with Vue and (regular) Bootstrap (not Bootstrap-Vue).

Upvotes: 0

Views: 884

Answers (1)

kdau
kdau

Reputation: 2099

You're losing reactivity because your content option to bootstrap.Popover is returning a string of your element's HTML, not the element itself. The popover just copies the HTML as it exists when it is opened. If you pass the element, Bootstrap will reparent the element itself into the popover, so changes to the element's children should be reflected. (Note that this could still be disrupted by a virtual DOM change that rewrote the element itself, which is why Bootstrap-Vue would still be better here.) If the popover might be reused, you'll need to reparent the element back into your component's own tree each time the popover is closed. You'll also need to make provision for the _Content element to only be hidden while it isn't reparented.

Here's how it all would look:

<template>
    <div ref="container" class="Popover__Container">
        <div ref="content" class="Popover__Content">
            <slot name="content">

            </slot>
        </div>
        <div ref="caller" class="Popover__Caller">
            <slot name="caller">

            </slot>
        </div>
    </div>
</template>

<script>
module.exports = {
    props: {
        title: {
            type: String,
            required: false,
            default: ''
        }
    },
    mounted() {
        const content = this.$refs.content;
        // PopOver Definition
        const popover = new bootstrap.Popover(this.$refs.caller, {
            container: 'body',
            title: this.title,
            html: true,
            placement: 'auto',
            sanitize: false,
            customClass: 'noselect',
            content: content
        });
        $(this.$refs.caller).on('hidden.bs.popover', () =>
        {
          this.$refs.container.prepend(content);
        });
    }
}
</script>

<style scoped>
.Popover__Container > .Popover__Content {
    display: none;
}
</style>

(I've also replaced the UID approach with refs, since that is a more Vue-like approach.)

Upvotes: 1

Related Questions