Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Elements: Handle child component #646

Open
4 tasks done
gnuletik opened this issue Aug 26, 2021 · 6 comments
Open
4 tasks done

Custom Elements: Handle child component #646

gnuletik opened this issue Aug 26, 2021 · 6 comments
Labels
🍰 p2-nice-to-have Priority 2: nothing is broken but it's worth addressing Status: Proposal Request for comments Type: Improvement Includes backwards-compatible fixes

Comments

@gnuletik
Copy link

Reporting a bug?

When calling useI18n from a component which is a children from a Custom Element, it throws :

Uncaught TypeError: Cannot read property '__VUE_I18N_SYMBOL__' of null

here :

? instance.appContext.app.__VUE_I18N_SYMBOL__!

This is because the child components do not have instance.isCE.

Would it be possible to :

  • Check recursively parent's property for isCE ?
  • Call inject even if !isCE and check for its result ?

Expected behavior

Should not throw

Reproduction

NA

System Info

Binaries:
    Node: 16.7.0 - /usr/bin/node
    Yarn: 1.22.11 - /usr/bin/yarn
    npm: 7.21.0 - /usr/bin/npm
  Browsers:
    Chromium: 92.0.4515.159
    Firefox: 91.0.1
  npmPackages:
    vue: ^3.2.6 => 3.2.6 
    vue-i18n: ^9.2.0-beta.3 => 9.2.0-beta.3

Screenshot

No response

Additional context

No response

Validations

@gnuletik gnuletik added the Status: Review Needed Request for review comments label Aug 26, 2021
@gnuletik
Copy link
Author

gnuletik commented Aug 27, 2021

The same error occurs when using async custom element with :

customElements.define(
  'my-element',
  defineCustomElement(defineAsyncComponent(() => import('MyComponent.vue')))
)

(but it makes sense because MyComponent is a child of the async component)

Copy link
Member

kazupon commented Aug 27, 2021

Thank you for feedback!

Unfortunately, this is a limitation of Vue Provide / Inject. 😞
https://vue-i18n.intlify.dev/guide/advanced/wc.html#limitations

Vue docs says:

note that this works only between custom elements

https://v3.vuejs.org/guide/web-components.html#definecustomelement

This means that if you use a Vue component from a custom element that is a web component, the inject will not work.

@kazupon kazupon removed the Status: Review Needed Request for review comments label Aug 27, 2021
@gnuletik
Copy link
Author

Thanks for your answer ! I missed that important info !

I created an issue in the vue-next repo : vuejs/core#4476

And will use a custom implementation of useI18n:

import { getCurrentInstance, InjectionKey } from 'vue'
import { I18nInjectionKey } from 'vue-i18n'

export function deepInject<T> (key: InjectionKey<T> | string): T | undefined {
  let inst = getCurrentInstance()
  if (inst === null) {
    throw new Error('getCurrentInstance returned null')
  }
  inst = inst.parent
  if (inst === null) {
    return
  }
  while (inst !== null) {
    // @ts-expect-error
    if (key in inst.provides) {
      // @ts-expect-error
      return inst.provides[key]
    }
    inst = inst.parent
  }
}

interface Options {
  messages?: Record<string, Record<string, string>>
}

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function useI18n (options?: Options) {
  const instance = deepInject(I18nInjectionKey)
  if (instance === undefined) {
    throw new Error('i18n not found in context')
  }
  const { global } = instance

  // merge locale messages
  const messages = options?.messages ?? {}
  const locales = Object.keys(messages)
  if (locales.length > 0) {
    locales.forEach(locale => {
      global.mergeLocaleMessage(locale, messages[locale])
    })
  }

  return global
}

It does not cover all use-cases of the original implementation but it covers my use-case.

Thanks!

@kazupon kazupon added the Status: Proposal Request for comments label Aug 30, 2021
@ploca14
Copy link

ploca14 commented Aug 22, 2023

I think the provide/inject limitation does not effect this. I tried providing the i18n instance from a vue web component and than inject this in a child vue component and it worked fine.

PluginProvider.ce.vue

<template>
  <Suspense>
    <slot />
  </Suspense>
</template>

<script lang="ts">
import { defineComponent, provide } from 'vue';
import { createI18n, I18nInjectionKey } from 'vue-i18n';
import i18nConfig from '~/i18n.config';

const i18n = createI18n<false>(i18nConfig.apply());

export default defineComponent({
  setup(props) {
    provide(I18nInjectionKey, i18n);

    return {};
  },
});
</script>

CustomElement.ce.vue

<template>
  <div>
    {{ t('login') }}
    <ChildElement />  
  </div>
</template>

<script setup lang="ts">
const { t, locale } = useI18n({
  useScope: 'local',
  messages: {
    cs: {
      login: 'Přihlásit',
    },
    en: {
      login: 'Login',
    },
  },
});
</script>

ChildElement.vue

<template>
  <div>
    ChildElement
  </div>
</template>

<script lang="ts" setup>
import { I18nInjectionKey } from 'vue-i18n';

console.log(inject(I18nInjectionKey)); // This properly injects
</script>

@AleksejDix
Copy link

We also run into this issue Please checkout this implementation which just create a wrapper component which doesn't need to be mounted on the application but can be mounted on any vue component which will be converted to web component

It would be nice to have this feature inside native i18n So it can be used to create web components without App context

https://github.com/padcom/vue-i18n

Copy link
Member

kazupon commented Nov 8, 2023

https://github.com/padcom/vue-i18n

That's interesting!
I'll check about it. :)

@kazupon kazupon added the Type: Improvement Includes backwards-compatible fixes label Nov 11, 2023 — with Volta.net
@kazupon kazupon added the 🍰 p2-nice-to-have Priority 2: nothing is broken but it's worth addressing label Apr 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🍰 p2-nice-to-have Priority 2: nothing is broken but it's worth addressing Status: Proposal Request for comments Type: Improvement Includes backwards-compatible fixes
Projects
None yet
Development

No branches or pull requests

4 participants