Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue component data not updating from props

Tags:

vuejs2

I'm building a SPA with a scroll navigation being populated with menu items based on section components.

In my Home.vue I'm importing the scrollNav and the sections like this:

<template>
    <div class="front-page">
        <scroll-nav v-if="scrollNavShown" @select="changeSection" :active-section="activeItem" :items="sections"></scroll-nav>
        <fp-sections @loaded="buildNav" :active="activeItem"></fp-sections>
    </div>
</template>

<script>
    import scrollNav from '.././components/scrollNav.vue'
    import fpSections from './fpSections.vue'

    export default {
        data() {
            return {
                scrollNavShown: true,
                activeItem: 'sectionOne',
                scrollPosition: 0,
                sections: []
            }
        },
        methods: {
            buildNav(sections) {
                this.sections = sections;
                console.log(this.sections)
            },
            changeSection(e) {
                this.activeItem = e
            },
        },
        components: {
            scrollNav,
            fpSections
        }
    }    
</script>

this.sections is initially empty, since I'm populating this array with data from the individual sections in fpSections.vue:

<template>
    <div class="fp-sections">
        <keep-alive>
            <transition
                @enter="enter"
                @leave="leave"
                :css="false"    
            >
                <component :is="activeSection"></component>
            </transition>
        </keep-alive>
    </div>
</template>

<script>
    import sectionOne from './sections/sectionOne.vue'
    import sectionTwo from './sections/sectionTwo.vue'
    import sectionThree from './sections/sectionThree.vue'

    export default {
        components: {
            sectionOne,
            sectionTwo,
            sectionThree
        },
        props: {
            active: String
        },
        data() {
            return {
                activeSection: this.active,
                sections: []
            }
        },
        mounted() {
            this.buildNav();
        },
        methods: {
            buildNav() {
                let _components = this.$options.components;
                for(let prop in _components) {
                    if(!_components[prop].hasOwnProperty('data')) continue;
                    this.sections.push({
                        title: _components[prop].data().title,
                        name: _components[prop].data().name
                    })
                }
                this.$emit('loaded', this.sections)
            },
            enter(el) {
                twm.to(el, .2, {
                    autoAlpha : 1
                })
            },
            leave(el, done) {
                twm.to(el, .2, {
                    autoAlpha : 0
                })
            }
        }
    }
</script>

The buildNav method loops through the individual components' data and pushes it to a scoped this.sections array which are then emitted back to Home.vue

Back in Home.vue this.sections is populated with the data emitted from fpSections.vue and passed back to it as a prop.

When I inspect with Vue devtools the props are passed down correctly but the data does not update.

What am I missing here? The data should react to props when it is updated in the parent right?

like image 701
kmaar Avatar asked Feb 04 '23 17:02

kmaar


2 Answers

:active="activeItem"

this is calld "dynamic prop" not dynamic data. You set in once "onInit". For reactivity you can do

computed:{
   activeSection(){ return this.active;}
}

or

watch: {
    active(){
    //do something
    }
}
like image 185
Kirill Matrosov Avatar answered May 28 '23 16:05

Kirill Matrosov


You could use the .sync modifier and then you need to emit the update, see my example on how it would work:

Vue.component('button-counter', {
  template: '<button v-on:click="counter += 1">{{ counter }}</button>',
  props: ['counter'],
  watch: {
    counter: function(){
       this.$emit('update:counter',this.counter)
    }
  },
})

new Vue({
  el: '#counter-sync-example',
  data: {
    foo: 0,
    bar: 0
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<div id="counter-sync-example">
  <p>foo {{ foo }} <button-counter :counter="foo"></button-counter> (no sync)</p>
  <p>bar {{ bar }} <button-counter :counter.sync="bar"></button-counter> (.sync)</p>
</div>
like image 45
Andy Avatar answered May 28 '23 17:05

Andy