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

Text in translation tags doesnt work with hot module reloading. #18

Open
trainiac opened this issue Mar 31, 2017 · 4 comments
Open

Text in translation tags doesnt work with hot module reloading. #18

trainiac opened this issue Mar 31, 2017 · 4 comments

Comments

@trainiac
Copy link
Contributor

trainiac commented Mar 31, 2017

When using hot module reloading with webpack, the text doesn't update when making changes. This is because the msgid property in the translate component is decided when the component created event.

I tried moving the logic in the created function into the translation computed property but that doesn't update because $options._renderChildren (which we should consider changing to $slots.default in order to not access a private property) is not a reactive property so nothing would trigger the computed property to update. Even $slots is not a reactive property, so computed property is not the solution.

It seems like the only way this works is if moving the logic into another part of the component life cycle. This probably has some performance implications so I wanted to propose something and I'll submit a PR if it seems appropriate.

 data () {
    // Make msgid a reactive property
    return {
      msgid: ''
    }
  },

  created: function () {
    this.msgid = this.getMsgId()
    this.isPlural = this.translateN !== undefined && this.translatePlural !== undefined;
    if (!this.isPlural && (this.translateN || this.translatePlural)) {
      throw new Error(("`translate-n` and `translate-plural` attributes must be used together: " + (this.msgid) + "."))
    }
  },

  // This is what would make the hot update work.
  beforeUpdate: function () {
    this.msgid = this.getMsgId()
  },

  methods : {
   // move this logic into a function so that in can be reused. 
    getMsgId: function () {
      // Store the raw uninterpolated string to translate.
      // This is currently done by looking inside a private attribute `_renderChildren` of the current
      // Vue instance's instantiation options.
      // However spaces introduced by newlines are not exactly the same between the HTML and the
      // content of `_renderChildren`, e.g. 6 spaces becomes 4 etc. See issue #15 for problems which
      // can arise with this.
      // I haven't (yet) found a better way to access the raw content of the component.

      // use $slots.default instead of $options._renderChildren
      if (this.$slots.default) {
        if (this.$slots.default[0].hasOwnProperty('text')) {
          return this.$slots.default[0].text.trim();
        }
        return this.$slots.default[0].trim();
      }

      return ''
    }
  },

So the changes are:

  1. Make msgid reactive
  2. Include a beforeUpdate function that updates the msgid before rendering.
  3. Break out the logic for computing the msgid into a reusable function.
  4. Use $slots.default instead of $options._renderChildren (maybe make this a separate PR?)
  5. Add a new plugin config option develop that if set to true would extend the translate component configuration with the beforeUpdate. This way the extra function call won't happen on each rerender.
@kemar
Copy link
Contributor

kemar commented Mar 31, 2017

Yup that's true. Hot module reloading won't update the translation tags content.

However, for me in any case, it's never been an issue since such a change most likely implies a change in the translations files so you have to compile translations again, relaunch the dev server etc.

Also I'm curious about how you can use this.$slots.default since no slot is ever used in the component template? I'm not able to access this.$slots in the current state of the component without trigerring an error.

@trainiac
Copy link
Contributor Author

Hmm maybe it's because I'm using a webpack vue-loader build process. I assumed by default every custom component that gets passed content implicitly has $slot.default, but perhaps it's due to my build system.

@trainiac
Copy link
Contributor Author

With the changes I made above, I have access to this.$slots.default.

There are a few examples of rendering components using $slots where it's not defined in templates. https://vuejs.org/v2/guide/render-function.html

@trainiac
Copy link
Contributor Author

trainiac commented Mar 31, 2017

Have you tried only accessing $slots in the render function? Maybe in created $slots has not been created yet. But it is strange that it worked for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants