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

Is it possible to dynamically update titleTemplate? #322

Closed
joshuajazleung opened this issue Feb 16, 2019 · 15 comments

Comments

@joshuajazleung
Copy link

commented Feb 16, 2019

I use metaInfo() function syntax, and return a titleTemplate function which binds to my root component property. I want the template to change when my property updated. I tested but it's not working. Maybe I missed something?

metaInfo() {
    return {
      // if no subcomponents specify a metaInfo.title, this title will be used
      title: '',
      // all titles will be injected into this template
      titleTemplate: titleChunk => (this.locale === 'zh-cn'
        ? `${titleChunk} XXX`
        : `${titleChunk} YYY`),
    };
  },
@pimlie

This comment has been minimized.

Copy link
Collaborator

commented Feb 20, 2019

This should be possible indeed, but this in the titleTemplate fn might be set to a different component then the one you expect. More specifically, its probably the last component which triggered the update for that batchUpdate sequence. See the src code here:

const applyTemplate = component => template => chunk =>

Could you please verify that this is really the component you expect it to be? If its not, the only solution for now is to set const self = this before the return and use self.locale instead of this.locale

@manniL

This comment has been minimized.

Copy link
Member

commented Feb 20, 2019

Ah, right. arrow functions and scope binding is a tricky thing, because you can't re-bind them. Better go for titleTemplate(chunk){} if you need the this inside.

@karakum

This comment has been minimized.

Copy link

commented Feb 21, 2019

same problem.
Title changed only while page rendering. In browser there is no update of title(through computed property in titleTemplate function), it still the same as on page render.

@pimlie

This comment has been minimized.

Copy link
Collaborator

commented Feb 21, 2019

@karakum Could you or @joshuajazleung please post a working/minimal reproduction?

@karakum

This comment has been minimized.

Copy link

commented Feb 21, 2019

@pimlie https://codesandbox.io/s/k2x31ly5oo
just open app in new window to see window title

@manniL

This comment has been minimized.

Copy link
Member

commented Feb 21, 2019

@karakum Yeah, looks like titleTemplate is bound to the root component. that's why it doesn't have the computed props or data attributes available.

There is a hacky workaround though:

head() {
    return {
      title: { t: "Index title", c: this.newMessages },
      titleTemplate: ({ t, c }) => {
        return `У вас новые сообщения: ${c} шт - ${t}`;
      }
    };
  },
@karakum

This comment has been minimized.

Copy link

commented Feb 21, 2019

@manniL looks good, but what about default layout component. And I have to modify all pages for this title-object value.
I have this.newMessages in layout and head() / title are in pages components.

@manniL

This comment has been minimized.

Copy link
Member

commented Feb 21, 2019

@karakum you can use head in layout as well ☺️ Maybe a higher order function could work for that (as @pimlie suggested).

@karakum

This comment has been minimized.

Copy link

commented Feb 21, 2019

@manniL @pimlie here my configuration example (layout(with titleTemplate) and page(with title))
https://codesandbox.io/s/v05kop794y
I don't understand about 'higher order function', can you explain and help modify my codesandbox to work properly?

@pimlie

This comment has been minimized.

Copy link
Collaborator

commented Feb 21, 2019

I had another idea, could you please try this? (CSB doesnt work for me)

  head() {
    const newMessages = this.newMessages
    return {
      title: "Index title",
      titleTemplate: function(t) {
        return `У вас новые сообщения: ${newMessages} шт - ${t}`;
      }
    };
  },
@karakum

This comment has been minimized.

Copy link

commented Feb 22, 2019

@pimlie it doesn't work.

@pimlie

This comment has been minimized.

Copy link
Collaborator

commented Feb 22, 2019

@karakum @joshuajazleung

The reason why this problem exists is due to how reactivity works in Vue and outside the scope of vue-meta (unless we refactor everything which we might do for vue-meta 2.0). The proof for this is because in the csb you get 0 messages and not undefined messages.
If metaInfo (or head in Nuxt) is a function then that function is treated as a computed property. Vue will automatically create an observer to track changes to that computed property. The way Vue does that (in a very short explanation) is by just calling the method and track which variables are accessed by the method, so when one of those variables changes it knows the return value of the function (might) changes too.

Thats why I thought my previous idea would work, the titleTemplate function is called from deep within vue-meta completely outside the scope of the component observers. So by setting a local newMessages variable before we return the metaInfo object we explicitly tell Vue that head() is depending on the this.newMessages variable.

The following example does work as expected with the local variable fix but not when I use this.locale directly within titleTemplate (it's an adaptation of the basic-render example). So I think @karakum has a second issue with his code, what exactly eludes me for the moment. Try to debug if the value of newMessages really changes in your layout

import Vue from 'vue'
import VueMeta from 'vue-meta'

Vue.use(VueMeta)

Vue.component('child', {
  name: 'Child',
  props: ['page'],
  data () {
    return {
      locale: 'nl'
    }
  },
  render (h) {
    return h('h3', null, this.page)
  },
  metaInfo () {
    const locale = this.locale
    return {
      title: this.page,
      titleTemplate: (c) => (locale === 'nl' ? 'Hallo Wereld' : 'Hello World') + ` - ${c}`
    }
  }
})

const app = new Vue({
  template: `
    <div id="app">
      <h1>Basic Render</h1>
      <p>Inspect Element to see the meta info</p>
      <child page="This is a prop"></child>
    </div>
  `
}).$mount('#app')

setTimeout(() => {
  app.$children[0].locale = 'en'
}, 2000)
@karakum

This comment has been minimized.

Copy link

commented Feb 22, 2019

@pimlie I take my words back - this realy worked! I don't know why it happend before, but now all right.
const cnt=this.newMessages in head() resolve issue;
Thanks a lot!

@pimlie

This comment has been minimized.

Copy link
Collaborator

commented Feb 22, 2019

Great!

@joshuajazleung Please let us know if your issue is resolved as well by setting a local variable

@pimlie pimlie added the question label Mar 7, 2019

@pimlie

This comment has been minimized.

Copy link
Collaborator

commented Apr 23, 2019

Closing this as it should be resolved, please re-open or create a new issue if you are still experiencing issues

@pimlie pimlie closed this Apr 23, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.