Watch doesn't get fire/trigger when global property change

1.1k views Asked by At

I’m very new to Vue and I begin with Vue 3. I was trying to migrate a template from Vue 2 to Vue 3 so I can start with my project.

I have this plugin file.

Sidebar\Index.ts

import SidebarPlugComp from './SidebarPlugComp.vue'
import SidebarLinkPlugComp from './SidebarLinkPlugComp.vue'


// tiny internal plugin store
const SidebarStore = {
    showSidebar: false,
    sidebarLinks: [],
    displaySidebar (value: boolean) {
        this.showSidebar = value
    }
}

const SidebarPlugin = {
    install (app: any) {
    
        app.config.globalProperties.$sidebar = SidebarStore
        app.component('side-bar-plug-comp', SidebarPlugComp)
        app.component('sidebar-link-plug-comp', SidebarLinkPlugComp)
    }
}

export default SidebarPlugin

Also I have a BaseTopNavLay layout file so I can toggle the sidebar with handleSidebarToggle onclick button method

<template>
    \\...
                <div class="navbar-toggle d-inline" :class="{toggled: $sidebar.showSidebar}">
                    <button type="button"
                            class="navbar-toggler"
                            aria-label="Navbar toggle button"
                            @click.prevent="handleSidebarToggle">
                        <span class="navbar-toggler-bar bar1"></span>
                        <span class="navbar-toggler-bar bar2"></span>
                        <span class="navbar-toggler-bar bar3"></span>
                    </button>
   \\   ...
</template>

<script lang="ts">
    import { defineComponent } from 'vue'
    import { ModalComp } from '../components'

   
    export default defineComponent({
        name: 'BaseTopNavLay',
        components: {
            ModalComp
        },
        // ...
        methods: {
            handleSidebarToggle (): void {
                this.$sidebar.displaySidebar(!this.$sidebar.showSidebar)
            },
            handleHideSideBar (): void {
                this.$sidebar.displaySidebar(false)
            },
        }
    })
</script>

And here is the watch in the App.vue file

<template>
    <component :is="this.$route.meta.layout || 'div'">
        <router-view />
    </component>
</template>


<script lang="ts">
    import { defineComponent } from 'vue'


    export default defineComponent({
        name: 'Application',
        methods: {
            toggleNavOpen () {
                console.log('here')
                let root = document.getElementsByTagName('html')[0]
                root.classList.toggle('nav-open')
            }
        },
        /*watch: {
            '$sidebar.showSidebar': function(newVal, oldVal) {
                console.log(newVal, oldVal)
            }
        }*/
        mounted () {
            //@ts-ignore
            this.$watch('$sidebar.showSidebar', this.toggleNavOpen)
        }
    })

</script>

Wherever I test the var this.$sidebar.showSidebar I can access to its value properly. Also, the onclick method is changing the SidebarStore object in Sidebar\Index.ts plugin file.

Can anyone give me a hint what am I missing here? Why the watch doesn't get fired. Thanks in advance.

1

There are 1 answers

3
Daniel On BEST ANSWER

The problem is that you have not made your $sidebar reactive, and a watch needs to use a reactive variable.

You can keep the store where you have it, but I'd put it into a separate file (store.js) and import where needed, no need to put it on app.config.globalProperties.$sidebar (but that might be a personal preference

// store.js
// using reactive (all values)
export const SidebarStore = Vue.reactive({
    showSidebar: false,
    sidebarLinks: [],
})
// or using ref (one for each)
// export const showSidebar = Vue.ref(false);

export const displaySidebar = (value: boolean) => {
    SidebarStore.showSidebar.value = value;
}

this will make SidebarStore and displaySidebar available anywhere in your code

use like this

<template>
    \\...
                <div class="navbar-toggle d-inline" :class="{toggled: $sidebar.showSidebar}">
                    <button type="button"
                            class="navbar-toggler"
                            aria-label="Navbar toggle button"
                            @click.prevent="handleSidebarToggle">
                        <span class="navbar-toggler-bar bar1"></span>
                        <span class="navbar-toggler-bar bar2"></span>
                        <span class="navbar-toggler-bar bar3"></span>
                    </button>
   \\   ...
</template>

<script lang="ts">
    import { defineComponent } from 'vue'
    import { ModalComp } from '../components'
    import { SidebarStore, displaySidebar } from '../store'

   
    export default defineComponent({
        name: 'BaseTopNavLay',
        components: {
            ModalComp
        },
        // ...
        methods: {
            handleSidebarToggle (): void {
                displaySidebar(!SidebarStore.showSidebar)
            },
            handleHideSideBar (): void {
                displaySidebar(false)
            },
        }
    })
</script>