1

I created a component for displaying some information in a nice looking way. This component is just a wrapper for the content, which is rendered inside the parent component. The parent component implements the child component this way:

    <my-component v-for="item in items" :key="item.id">
        <template slot="header">
            {{ item.title }}
        </template>
        <template slot="meta">
            <div>
                <template v-if="typeof item.additionalData != 'undefined'">
                    {{ item.additionalData.text }}
                </template>
            </div>
        </template>
    </my-component>

Its working fine, until I want to change the data. items is a variable in the parent component and at render time, the data is parsed in the right way. When I change something inside of items after rendering, the child component doesn't recognize it. The reason is because the item.additionalData is added through an AJAX call after the component was already rendered.

The docs say

Everything in the parent template is compiled in parent scope; everything in the child template is compiled in child scope.

but it seems like this is only true at render time.

Am I not able to use my component this way or is there a solution for that?

M. Andrea
  • 21
  • 2
  • You could try https://vuejs.org/v2/api/#vm-forceUpdate – Randy Sep 19 '17 at 13:22
  • Here they explain how to wait for the Ajax call to complete, and then render: https://laracasts.com/discuss/channels/vue/vue-20-waiting-for-async-ajax-data – Randy Sep 19 '17 at 13:24
  • By the way, welcome to StackOverflow. If any of the answers below solved your problem, please be sure to mark the one that worked best for you as accepted so the community can benefit from your feedback. If your question is still unsolved, please update your answer with further information so we can help you get to the bottom of the issue. – Jason Smith Sep 21 '17 at 03:23

2 Answers2

1

From the docs:

Due to the limitations of modern JavaScript (and the abandonment of Object.observe), Vue cannot detect property addition or deletion. Since Vue performs the getter/setter conversion process during instance initialization, a property must be present in the data object in order for Vue to convert it and make it reactive.

So I suspect the fact that your item.additionalData field is initially undefined means that it's getting missed by Vue later on when it's added. The best solution is to make item.additionalData defined, but set to null or false. If that's not possible, consider using Vue.set(object, key, value) as outlined here.

tony19
  • 125,647
  • 18
  • 229
  • 307
Jason Smith
  • 1,171
  • 7
  • 18
  • I also tried it with a variable called additionalLoaded, which was initially set to false and later (after AJAX call) to true, but it doesn't update inside the slot. – M. Andrea Sep 19 '17 at 13:37
  • That's because Vue does the minimum amount of work possible when updating the page. Since it can't monitor `additionalData` for changes, it skips that slot. – Jason Smith Sep 19 '17 at 13:39
  • @M.Andrea There's not a way to do that that I'm aware of. – Jason Smith Sep 19 '17 at 13:55
0

I had a similar problem with adding a search icon to the item template. The way I resolved it was to make (in terms of your example) additionalData a computed property which is then fully reactive. This is the basic form:

computed: {
  additionalDataText() {
    return item.additionalData ? item.additionalData.text : null
  }
} 

I'm unclear if your code shows the parent component or the child component.
As it is, the simplest change you can make requires the computed property to receive item as a parameter like so:

computed: {
  additionalDataText() {
    return function (item) {
      return item.additionalData ? item.additionalData.text : null
    }
  }
} 

and your template would be

<my-component v-for="item in items" :key="item.id">
    <template slot="header">
        {{ item.title }}
    </template>
    <template slot="meta">
        <div>
            <template v-if="additionalDataText(item)">
                {{ additionalDataText(item) }}
            </template>
        </div>
    </template>
</my-component>

This technique is from Unirgy's answer to this question: Can I pass parameters in computed properties in Vue.Js. There's a bit of controversy there about this method, but I tried it in my project and it seems to work.
Note, in the referenced question the accepted answer suggests using a method, but this does not give you the reactivity you require.

Richard Matsen
  • 20,671
  • 3
  • 43
  • 77