karol-f / vue-custom-element

Vue Custom Element - Web Components' Custom Elements for Vue.js
https://karol-f.github.io/vue-custom-element/
MIT License
1.97k stars 187 forks source link

Vue custom element not initialized #111

Closed tworzenieweb closed 6 years ago

tworzenieweb commented 6 years ago

Hi,

I implemented dynamic banner element using your extension. The issue I am experiencing affected the only firefox.

In short, there is a list of results from search endpoint. These results are decorated with banners every n-th row.

Initially, it works and the banner is properly initialized and visible. But when the user is changing the number of results to display, there is another ajax request that is adding new results and of course new banners into content.

But this time banner component is not initialized. There is no error corresponding to that. I tried manually adding HTML component in the same place. This time it works.

karol-f commented 6 years ago

Hi, if you will share some code it would be easier to solve this issue.

You can fork and edit this sandbox - https://codesandbox.io/s/rw3yjznw1q

tworzenieweb commented 6 years ago
<template>
    <span v-if="isEnabled">
        <div v-if="isJobSearchBanner || isApplicationBanner" class="banner">
            <a @click.prevent="openModal" href="#">
            <img :src="require('./banner/DE.png')" v-if="isGerman" />
            <img :src="require('./banner/EN.png')" v-if="isEnglish" />
            <img :src="require('./banner/FR.png')" v-if="isFrench" />
            <img :src="require('./banner/ES.png')" v-if="isSpanish" />
            </a>
        </div>
        <a v-if="isNavigation" @click.prevent="openModal" href="#" class="menu-link">
            <span class="new">some button text</a>

        <a v-if="isExternalJob" @click.prevent="openModal" href="#" class="extern-job-menu">
            <span class="topcv-logo arrow-div">
                <img src="@/topcv-blue.svg" height="20" class="topcv-logo" />
            </span>
            <span class="text">some text</span>
        </a>

        <modal-box v-if="modalIsOpened" v-on:modal-close="closeModal" v-on:send-cv="send" :errors="errors" :is-completed="isCompleted"></modal-box>
    </span>
</template>

<script type="ts">
    import axios from 'axios';

    export default {
        props: {
            source: {type: String}
        },
        computed: {
            isExternalJob() {
                return this.source === 'external_job' && !this.isCompleted;
            },
            isApplicationBanner() {
              return this.source === 'application_banner' && !this.isCompleted;
            },
            isJobSearchBanner() {
                return this.source === 'job_search_banner' && !this.isCompleted;
            },
            isNavigation() {
                return this.source === 'navigation' && !this.isCompleted;
            },
            isGerman() {
                return this.language === 'de';
            },

            isFrench() {
                return this.language === 'fr';
            },

            isEnglish() {
                return this.language === 'en';
            },

            isSpanish() {
                return this.language === 'es';
            }
        },
        methods: {
            send(event) {
                const file = event.detail[0];
                let formRequest = new FormData();
                formRequest.append('utm_source', this.source);
                formRequest.append('resume_file', file);

                axios.post(this.url, formRequest).then((response) => {
                    this.isCompleted = true;
                },{withCredentials: true}).catch((error) => {
                    console.error(error.response.data.detail);
                });
            },
            closeModal() {
                this.modalIsOpened = false;
            },
            openModal() {
                this.modalIsOpened = true;
            },
            processUserData(userData) {
                const user = userData;
                this.isEnabled = !user.has_used_cv_check;
                this.user = user;
                this.language = user.language;
            }
        },
        mounted() {
            axios.get(this.url, {withCredentials: true}).then((response) => {
                this.processUserData(response.data);
            });
        },
        data: () => {
            return {
                language: null,
                isCompleted: false,
                errors: null,
                url: '/someendpoint',
                user: null,
                modalIsOpened: false,
                isEnabled: false
            }
        }
    }
</script>
import Vue, { ComponentOptions } from 'vue';
import VueCustomElement from 'vue-custom-element';
import BannerComponent from './banner.vue';
import ModalComponent from './modal.vue';
import 'document-register-element/build/document-register-element';

Vue.config.productionTip = false;

Vue.use(VueCustomElement);
Vue.customElement('banner', BannerComponent);
Vue.customElement('modal-box', ModalComponent);
karol-f commented 6 years ago

Hi, these seems like issue not related to vue-custom-element. You fetch data on mounted() so when you change something component does not know it should re-render and fetch new data. Maybe pass some prop so You would know that?

tworzenieweb commented 6 years ago

But this is not the case.

When I am fetching items from search endpoint

then I am setting the component HTML via table row cell innerHtml = <banner></banner>

so the previous version of component is destroyed during overwriting container having search results.

And of course new one is recreated.

I told you that it only fails on Firefox even latest version.

The ajax call in component is only for security reason to see if the feature can be displayed and has no connection with search results where the banner is placed.

karol-f commented 6 years ago

Hi, setting something via innerHtml seems like against Vue and other modern frameworks.

IMHO You should generate HTML inside Vue component or send HTML string via props and use v-html (https://vuejs.org/v2/api/#v-html)

I would love to help you but without working example showing problem, it's hard to suggest you any solution.

Regards!