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

Cannot pass vue-i18n object into web-component #216

Open rostamiani opened 4 years ago

rostamiani commented 4 years ago

I have a component that uses vue-i18n and works as a normal vue component.

When creating web component I had TypeError: this is undefined error with typescript, then created the component with this approach:

const layout = new ModuleLayout().$options;
Vue.customElement('my-layout', layout);

And this is my i18n object:

import messages from '@/locales/i18n.json';

export const i18n = new VueI18n({
  locale: process.env.VUE_APP_I18N_LOCALE || 'en',
  fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
  messages: messages
})

For adding i18n I tried these approaches but none works:

const layout = new ModuleLayout({ i18n }).$options;

Vue.customElement('my-layout', layout);
const layout = new ModuleLayout().$options;
layout.i18n = i18n;

Vue.customElement('my-layout', layout);

In both ways I have these errors:

TypeError: "this.$i18n is undefined"
TypeError: "i18n is undefined"
LucasBerger commented 4 years ago

Hey,

don't know if that helps but a coworker of mine added i18next with a mixin that he then installed in Vue:

i18next-mixin.ts:

import _Vue from 'vue'
import i18next from 'i18next'
import LanguageDetector from 'i18next-browser-languagedetector'

import localesDE from '@/locales/de.json'
import localesEN from '@/locales/en.json'

declare global {
  interface Window {
    webpackHotUpdate: Function;
  }
}

interface TranslationOptions {
  defaultValue: string | null;
}

function install (Vue: typeof _Vue): void {
  i18next
    .use(LanguageDetector)
    .init({
      debug: typeof window.webpackHotUpdate !== 'undefined',
      defaultNS: 'common',
      whitelist: ['de', 'en'],
      fallbackLng: 'de',
      returnNull: true,
      resources: {
        en: { common: localesEN },
        de: { common: localesDE }
      },
      detection: {
        order: ['querystring', 'cookie', 'navigator'],
        lookupQuerystring: 'language',
        lookupCookie: 'language',
        checkWhitelist: true
      }
    })

  Vue.prototype.i18next = Vue.observable({ language: 'none' })
  Vue.prototype.$t = function (namespace: string, options: TranslationOptions | undefined) {
    // Important line, keep it else Vue will not call this function when the language changes
    // eslint-disable-next-line
    const lng = Vue.prototype.i18next.language

    if (!options) {
      options = { defaultValue: (typeof window.webpackHotUpdate !== 'undefined' ? namespace : '') }
    } else {
      options.defaultValue = (typeof window.webpackHotUpdate !== 'undefined' ? namespace : '')
    }

    return i18next.t(namespace, options)
  }
}

export default install

then you install it to Vue using:

import I18Nmixin from './mixins/i18next-mixin'

Vue.use(I18Nmixin);

and in the Element you use the $t() method, but I think you can change it by changing it in this line:

Vue.prototype .$t = function (namespace: string, options: TranslationOptions | undefined) {

I hope this helps at least a bit,

Regards Lucas

karol-f commented 4 years ago

Hi, all seems to work for me - https://codesandbox.io/s/vue-template-o1fsc?file=/src/main.js

rostamiani commented 4 years ago

Hi, all seems to work for me - https://codesandbox.io/s/vue-template-o1fsc?file=/src/main.js

It's working because you are not using Typescript + Class based components. Using typescript I you have to create a new instance of Test component in the main.ts file

Sadly I couldn't start a typescript sandbox to reproduce the error... But this is my code:

import ModuleLayout from '@/components/modules/layout/ModuleLayout.vue';
import VueCustomElement from 'vue-custom-element';

Vue.use(VueCustomElement);
Vue.customElement('skyroom-layout', ModuleLayout); // line 32

And the error:

Uncaught TypeError: this is undefined
    VueComponent VueJS
    connectedCallback vue-custom-element.esm.js:484
    connectedCallback vue-custom-element.esm.js:57
    define vue-custom-element.esm.js:70
    registerCustomElement vue-custom-element.esm.js:104
    vueCustomElement vue-custom-element.esm.js:477
    ts main.ts:32
    Webpack 7
vue.runtime.esm.js:5154

And this code has no errors but without i18n:

import ModuleLayout from '@/components/modules/layout/ModuleLayout.vue';
import VueCustomElement from 'vue-custom-element';

Vue.use(VueCustomElement);

const layout = new ModuleLayout({ i18n }).$options;
Vue.customElement('skyroom-layout', layout);
karol-f commented 4 years ago

Hi, it seems to work like this. Please let me know if it will work for You - https://codesandbox.io/s/vue-template-72o5z?file=/src/main.js

import Vue from "vue";
import vueCustomElement from "vue-custom-element";
import VueI18n from "vue-i18n";
import Test from "./components/Test.vue";

Vue.use(vueCustomElement);
Vue.use(VueI18n);

Vue.config.productionTip = false;

const i18n = new VueI18n({
  locale: "en",
  messages: {
    en: {
      message: {
        hello: "hello world"
      }
    }
  }
});
Test.prototype.constructor.options.i18n = i18n;

Vue.customElement("test-component", Test.prototype.constructor.options);
rostamiani commented 4 years ago

it works , but with a typescript error:

 Property 'options' does not exist on type 'Function'.

image

Update: While serving, after recompiling this.$i18n is undefined error appears again.

hayreenfly commented 3 years ago

Had problems with this and i solved it with the following code:

const i18n = new VueI18n({
  locale: "en",
  messages: {
    en: {
      message: {
        hello: "hello world"
      }
    }
  }
});

Vue.customElement('my-widget', new App().$options, {
  beforeCreateVueInstance(RootComponentDefinition) {
    RootComponentDefinition.i18n = i18n
    return RootComponentDefinition
  }
})