简体   繁体   中英

Update props in component through router-view in vue js

I want to pass a boolean value from one of my views in a router-view up to the root App.vue and then on to one of the components in the App.vue. I was able to do it but I am facing an error:

Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "drawer"

Below is my code:

Home.vue:

<template>
  <div class="home"  v-on:click="updateDrawer">
    <img src="...">
  </div>
</template>
<script>
export default {
name: "Home",
methods:{
updateDrawer:function(){
  this.$emit('updateDrawer', true)
}
};
</script>

The above view is in the router-view and I am getting the value in the App.vue below:

<template>
<v-app class="">
<Navbar v-bind:drawer="drawer" />
<v-main class=" main-bg">
  <main class="">
    <router-view v-on:updateDrawer="changeDrawer($event)"></router-view>
  </main>
</v-main>
</v-app>
</template>

<script>
import Navbar from '@/components/Navbar'

export default {
name: 'App',
components: {Navbar},

data() {
return {
  drawer: false
}
},
methods:{
changeDrawer:function(drawz){
  this.drawer = drawz;
}
},
};
</script>

I am sending the value of drawer by binding it in the navbar component.

Navbar.vue:

<template>
<nav>
<v-app-bar app fixed class="white">
  <v-app-bar-nav-icon
    class="black--text"
    @click="drawer = !drawer"
  ></v-app-bar-nav-icon>
  </v-app-bar>
  <v-navigation-drawer
  temporary
  v-model="drawer"
  >
   ...
 </v-navigation-drawer>
</nav>
</template>
<script>
export default {
props:{
drawer:{
  type: Boolean
}
},
};
</script>

This will work one time and then it will give me the above error. I appreciate if any one can explain what should I do and how to resolve this issue.

In Navbar.vue you are taking the property "drawer" and whenever someone clicks on "v-app-bar-nav-icon" you change drawer to be !drawer.

The problem here is that you are mutating (changing the value) of a property from the child side (Navbar.vue is the child). That's not how Vue works, props are only used to pass data down from parent to child (App.vue is parent and Navbar.vue is child) and never the other way around.

The right approach here would be: every time, in Navbar.vue, that you want to change the value of "drawer" you should emit an event just like you do in Home.vue.

Then you can listen for that event in App.vue and change the drawer variable accordingly.

Example:

Navbar.vue

<v-app-bar-nav-icon
    class="black--text"
    @click="$emit('changedrawer', !drawer)"
></v-app-bar-nav-icon>

App.vue

<Navbar v-bind:drawer="drawer" @changedrawer="changeDrawer"/>

I originally missed the fact that you also used v-model="drawer" in Navbar.vue. v-model also changes the value of drawer, which again is something we don't want. We can solve this by splitting up the v-model into v-on:input and :value like so:

<v-navigation-drawer
    temporary
    :value="drawer"
    @input="val => $emit('changedrawer', val)"
>

Some sort of central state management such as Vuex would also be great in this scenario ;)

What you could do, is to watch for changes on the drawer prop in Navbar.vue, like so:

<template>
  <nav>
    <v-app-bar app fixed class="white">
      <v-app-bar-nav-icon
        class="black--text"
        @click="switchDrawer"
      />
    </v-app-bar>
    <v-navigation-drawer
      temporary
      v-model="navbarDrawer"
    >
      ...
    </v-navigation-drawer>
  </nav>
</template>

<script>
export default {
  data () {
    return {
      navbarDrawer: false
    }
  },
  props: {
    drawer: {
      type: Boolean
    }
  },
  watch: {
    drawer (val) {
      this.navbarDrawer = val
    }
  },
  methods: {
    switchDrawer () {
      this.navbarDrawer = !this.navbarDrawer
    }
  }
}
</script>

Home.vue

<template>
  <div class="home">
    <v-btn @click="updateDrawer">Updrate drawer</v-btn>
  </div>
</template>

<script>
export default {
  name: 'Home',
  data () {
    return {
      homeDrawer: false
    }
  },
  methods: {
    updateDrawer: function () {
      this.homeDrawer = !this.homeDrawer
      this.$emit('updateDrawer', this.homeDrawer)
    }
  }
}
</script>

App.vue

<template>
  <v-app class="">
    <Navbar :drawer="drawer" />
    <v-main class="main-bg">
      <main class="">
        <router-view @updateDrawer="changeDrawer"></router-view>
      </main>
    </v-main>
  </v-app>
</template>

<script>
import Navbar from '@/components/Navbar'

export default {
  name: 'App',
  components: { Navbar },

  data () {
    return {
      drawer: false
    }
  },
  methods: {
    changeDrawer (newDrawer) {
      this.drawer = newDrawer
    }
  }
}
</script>

This doesn't mutate the drawer prop, but does respond to changes.

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