[Vuejs]-How do I render a child component within an iframe in Vue?

3👍

So as posted in one of the comments, Vuex works perfectly for this.

I also ended up creating a custom “IFrame” component that renders whatever you have inside its slot in an iframe.

Here is my Vuex store:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export const store = new Vuex.Store({
    state: {
        form: {
            name: null,
            email: null,
            message: null
        },
        senderEmail: null,
        term: null,
        styles: null
    },
    mutations: {
        updateForm(state, form) {
            state.form = form
        },
        updateEmail(state, email) {
            state.senderEmail = email
        },
        updateTerm(state, term) {
            state.term = term
        },
        stylesChange(state, styles) {
            state.styles = styles
        }
    }
})

my IFrame component:

import Vue from 'vue'
import { store } from '../../store'

export default {
    name: 'IFrame',
    data() {
        return {
            iApp: null,

        }
    },
    computed: {
        styles() {
            return this.$store.state.styles
        }
    },
    render(h) {
        return h('iframe', {
            on: {
                load: this.renderChildren
            }
        })
    },
    watch: {
        styles(val) {
            const head = this.$el.contentDocument.head

            $(head).html(val)
        }
    },
    beforeUpdate() {
        this.iApp.children = Object.freeze(this.$slots.default)
    },
    methods: {
        renderChildren() {
            const children = this.$slots.default
            const body = this.$el.contentDocument.body

            const el = document.createElement('div') // we will mount or nested app to this element
            body.appendChild(el)

            const iApp = new Vue({
                name: 'iApp',
                store,
                data() {
                    return {
                        children: Object.freeze(children)
                    }
                },
                render(h) {
                    return h('div', this.children)
                }
            })

            iApp.$mount(el)

            this.iApp = iApp
        }
    }
}

finally here is how data is passed to the PreviewComponent from the ConfirmationComponent:

export default {
    name: 'ConfirmationComponent',
    mounted() {
        this.$store.commit('updateEmail', this.senderEmail)
        this.$store.commit('updateTerm', this.term)
    },
    watch: {
        'form.name'(val) {
            this.updateIframe()
        },
        'form.email'(val) {
            this.updateIframe()
        }
    },
    methods: {
        updateIframe() {
            this.$store.commit('updateForm', this.form)
        }
    }
}

then lastly the actual PreviewComponent:

import styles from '../../../templates/styles'

export default {
    name: 'PreviewComponent',
    mounted() {
        this.$store.commit('stylesChange', styles)
    },
    computed: {
        redemption_url() {
            return `${window.config.stitcher_website}/gift?code=`
        },
        custom_message() {
            if (this.form.message) {
                let div = document.createElement('div')

                div.innerHTML = this.form.message

                let text = div.textContent || div.innerText || ''

                return text.replace(/(?:\r\n|\r|\n)/g, '<br>')
            }
            return null
        },
        form() {
            return this.$store.state.form
        },
        term() {
            return this.$store.state.term
        },
        senderEmail() {
            return this.$store.state.senderEmail
        }
    }
}

hopefully this will help somebody.

Leave a comment