Search code examples
vue.jsvuexnuxt.js

Nuxt/Vue reactive body attributes


I want to add the class menu-opened on the body tag when I click on the menu burger div.

My div

<div v-on:click="openMenu"></div>

The openMenu method

methods: {
     openMenu() {
       console.log('open menu launch')
       this.$store.dispatch('menu/setMenu', true)
     }
    }

My store

state = {
    isMenuOpen: false
}

actions = {
    setMenu({ commit }, value) {
      commit('SET_MENU_OPEN_STATUS', value)
    }
}

mutations= {
    SET_MENU_OPEN_STATUS(state, newState){
        state.isMenuOpen = newState
    }
}

On my template, i got this code to add the class on the body based on the state of the isMenuOpen value :

export default {
    data() {
        return {
           menuState: this.$store.state.isMenuOpen
        }
    },
    head () {
      return {
        bodyAttrs: {
          class: this.menuState ? 'menu-opened' : ''
        }
      }
    }
}

My store is working well, the value change when I click on my div, but it's not adding the class, like if the head function is not reactive...

Thanks for your help


Solution

  • In Nuxt

    This is because the head method is only called once on initial page load. If you would like to update the values you can, but you will need to call the head() function again.

    You could do this with a watcher or you could call it from your store.

    A quick and kind of dirty way would be with a combination of computed properties and a watcher..

    export default {
        data() {
            return {
               menuState: this.$store.state.isMenuOpen
            }
        },
        head () {
          return {
            bodyAttrs: {
              class: this.isMenuOpen ? 'menu-opened' : ''
            }
          }
        },
       computed: {
         isMenuOpen () {
           return this.$store.state.isMenuOpen
         }
       },
       watch: {
         isMenuOpen () {
           this.head()
         }
       }
    }
    

    In Vue

    You need to watch on events like mounted or beforeDestroy described there

    https://v2.vuejs.org/v2/guide/instance.html

    and use pure js to modify dom like

    const bodyElement = document.querySelector('body')
    bodyElement.classList.add('a');
    bodyElement.classList.remove('b');
    bodyElement.classList.toggle('c');
    

    https://developer.mozilla.org/pl/docs/Web/API/Element/classList