Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

want to use vuetify snackbar as a global custom component in vuejs

i used snackbar to show success messages in vuejs. i want to make a global custom snackbar component.

<template>
  <div name="snackbars">
    <v-snackbar
      v-model="snackbar"
      :color="color"
      :timeout="timeout"
      :top="'top'"
    >
      {{ text }}

      <template v-slot:action="{ attrs }">
        <v-btn dark text v-bind="attrs" @click="snackbar = false">
          Close
        </v-btn>
      </template>
    </v-snackbar>
  </div>
</template>

<script>
export default {
  props: {
    snackbar: {
      type: Boolean,
      required: true,
    },
    color: {
      type: String,
      required: false,
      default: "success",
    },
    timeout: {
      type: Number,
      required: false,
      default: 3000,
    },
    text: {
      type: String,
      required: true,
    },
  },
};
</script>

then i import this as a component in my every form like this.

<SnackBar :snackbar="snackbar" :color="color" :text="text" />

but my issue is i can't use snackbar as a prop in my child component. it shows me this 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: "snackbar" 

how can i fix this issue. can anyone help me?

like image 732
Kalana Mihiranga Avatar asked Jul 09 '20 04:07

Kalana Mihiranga


3 Answers

i found a way to fix my solution using vuex.

    <template>
      <div name="snackbars">
        <v-snackbar v-model="show" :color="color" :timeout="timeout" :top="'top'">
          {{ text }}
    
          <template v-slot:action="{ attrs }">
            <v-btn dark text v-bind="attrs" @click="show = false">
              Close
            </v-btn>
          </template>
        </v-snackbar>
      </div>
    </template>
    
    <script>
    export default {
      created() {
        this.$store.subscribe((mutation, state) => {
          if (mutation.type === "snackbar/SHOW_MESSAGE") {
            this.text = state.snackbar.text;
            this.color = state.snackbar.color;
            this.timeout = state.snackbar.timeout;
            this.show = true;
          }
        });
      },
      data() {
        return {
          show: false,
          color: "",
          text: "",
          timeout: 0,
        };
      },
    };
    </script>

in my vuex module i wrote like this

    export default {
      namespaced: true,
      state: {
        text: "",
        color: "",
        timeout: "",
      },
      mutations: {
        SHOW_MESSAGE(state, payload) {
          state.text = payload.text;
          state.color = payload.color;
          state.timeout = payload.timeout;
        },
      },
      actions: {
        showSnack({ commit }, payload) {
          commit("SHOW_MESSAGE", payload);
        },
      },
    };

then i import snackbar child component into my parent component and send data like this.

    ...mapActions("snackbar", ["showSnack"]),
    saveDetails() {
       this.showSnack({
            text: "Successfully Saved!",
            color: "success",
            timeout: 3500,
          });
     }
like image 94
Kalana Mihiranga Avatar answered Sep 23 '22 12:09

Kalana Mihiranga


I realize this is old, but thanks to google, I am going to add my solution. I use this, because I don't see the point of using vuex for a snackbar. It's more work then needed.

Create a vue component named vtoast

<template>
  <v-snackbar
      :color="color"
      :timeout="timer"
      v-model="showSnackbar"
      bottom
      right
  >
    <v-icon left>{{icon}}</v-icon>{{message}}
  </v-snackbar>
</template>

<script>
export default {
  name: "vtoast",
  data() {
    return{
      showSnackbar: false,
      message: '',
      color: 'success',
      icon: 'mdi-check',
      timer: 3000
    }
  },
  methods:{
    show(data) {
      this.message = data.message || 'missing "message".'
      this.color = data.color || 'success'
      this.timer = data.timer || 3000
      this.icon = data.icon || 'mdi-check'
      this.showSnackbar = true
    }
  }
}
</script>

Somewhere in the root of your main app, add the following. (I usually put mine in App.vue)

<template>
...
    <!-- toast -->
    <vtoast ref="vtoast"/>
...
</template>

<script>
import vtoast from '@/your/vtoast/directory/vtoast'
export default{
    name: 'App', //or whatever your root is
    components:{
        vtoast
    },
    mounted() {
      this.$root.vtoast = this.$refs.vtoast
    },
}
</script>

And access it like so...

this.$root.vtoast.show()
this.$root.vtoast.show({message: 'Ahoy there!'})
like image 37
Alan Spurlock Avatar answered Sep 23 '22 12:09

Alan Spurlock


Another solution is to use a computed value with getter and setter.

Using options api

<template>
  <v-snackbar v-model="show" :color="color">
    {{ message }}
    <template v-slot:action="{ attrs }">
      <v-btn text v-bind="attrs" @click="show = false">Close</v-btn>
    </template>
  </v-snackbar>
</template>

<script>
import { mapGetters } from 'vuex';

export default {
  computed: {
    ...mapGetters({
      message: 'snackbar/message',
      color: 'snackbar/color'
    }),
    show: {
      get() {
        return this.$store.state.snackbar.show
      },
      set(v) {
        this.$store.commit('snackbar/SET_SHOW', v)
      }
    }
  }
}
</script>

Using composition api plugin

<template>
  <v-snackbar v-model="show" :color="color">
    {{ message }}
    <template v-slot:action="{ attrs }">
      <v-btn text v-bind="attrs" @click="show = false">Close</v-btn>
    </template>
  </v-snackbar>
</template>

<script>
import { defineComponent, computed } from '@vue/composition-api';

export default defineComponent({
  setup(_props, { root }) {
    const show = computed({
      get: () => root.$store.state.snackbar.show,
      set: (v) => root.$store.commit('snackbar/SET_SHOW', v),
    });
    const message = computed(() => root.$store.state.snackbar.message);
    const color = computed(() => root.$store.state.snackbar.color);

    return {
      show,
      message,
      color,
    };
  },
});
</script>

A better implementation using composables here https://gist.github.com/wobsoriano/2f3f0480f24298e150be0c13f93bac20

like image 31
wobsoriano Avatar answered Sep 23 '22 12:09

wobsoriano