简体   繁体   中英

Vue.js - Template with parent scope not updating

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?

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 .

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM