Kick Buttowski
Kick Buttowski

Reputation: 6739

setTimeout for v-alert after adding Item to an array basket

this is my rightTableMenu template

<template>
  <div>
    <h1 align="center">{{ title }}</h1>
    <v-alert type="info" icon="mdi-emoticon-sad" v-if="basketStatus">
      Empty Basket, please add some to basket
    </v-alert>
    <div v-if="changeAlertStatus()">
      <v-alert
        type="success"
        icon="mdi-emoticon-happy"
        :value="alert"
        transition="fade-transition"
      >
        thank you
      </v-alert>
      <v-simple-table>
        <template v-slot:default>
          <thead>
            <tr>
              <th class="text-left">Quantity</th>
              <th class="text-left">Name</th>
              <th class="text-left">Price</th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="item in basket" :key="item.name">
              <td>
                <v-icon @click="increaseQuantity(item)">add_box</v-icon>
                <span>{{ item.quantity }}</span>
                <v-icon @click="decreaseQuantity(item)"
                  >indeterminate_check_box
                </v-icon>
              </td>
              <td>{{ item.name }}</td>
              <td>{{ (item.price * item.quantity).toFixed(2) }}</td>
            </tr>
          </tbody>
        </template>
      </v-simple-table>
      <v-divider color="black"></v-divider>
      <v-row id="basket_checkout" style="margin: 0">
        <v-col>
          <p>Subtotal:</p>
          <p>Delivery:</p>
          <p>Total amount:</p>
        </v-col>
        <v-col class="text-right">
          <p>${{ subTotalResult }}</p>
          <p>$10</p>
          <p class="font-weight-bold">${{ totalPriceResult }}</p>
        </v-col>
      </v-row>
      <v-row>
        <v-spacer></v-spacer>
        <v-btn depressed class="orange" v-on:click="submitOrder">
          <v-icon>shopping_basket</v-icon>
        </v-btn>
      </v-row>
    </div>
  </div>
</template>

as you see there are two alerts one is showing when there is not item inside the array basket by checking the following

basketStatus() {
  return this.$store.getters.basket.length === 0;
},

which is computed property my data property section is

  data() {
    return {
      title: "Current Basket",
      alert: false,
    };
  },

but for the second v-alert, I wanna to have the alert to be shown and disappear after few sec and so far I have done the following for it

  async changeAlertStatus() {
      if (this.$store.getters.basket.length !== 0) {
        this.alert = true;
        try {
          const response = await setTimeout(() => {
            this.alert = false;
          }, 100);
          console.log("this is the resonse " + response);
        } catch (err) {
          console.log("fetch failed", err);
        }
      } else {
        this.alert = false;
      }
    },

which is a method I am confused how to interject the function inside the div part without using v-if directive and my async changeAlertStatus gets in the infinite loop when I check it inside the console and the v-alert does not get disappear

enter image description here

any thoughts on that?

if there is more info needed , please let me know

thank you

just in case my leftTableMenu is follows

<template>
  <div>
    <div v-if="showError['situation']">
    <!-- 
basically, when you close the alert, the value of the alert goes to false
so you need to turn it to true when there is an error  :value="showError.situation" -->
      <app-alert :text="showError.message"  :value.sync="showError.situation"></app-alert>
    </div>
    <h1 align="center">{{ title }}</h1>
    <v-simple-table od="menu-table">
      <template v-slot:default>
        <thead>
          <tr>
            <th class="text-left">Name</th>
            <th class="text-left">Price</th>
            <th class="text-left">Add</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="item in menuItems" :key="item.name">
            <td>
              <span id="id_name">{{ item.name }}</span>
              <br />
              <span id="menu_item_description">{{ item.description }}</span>
            </td>
            <td>{{ item.price }}</td>
            <td>
              <v-btn text v-on:click="addToBasket(item)">
                <v-icon color="orange">1add_shopping_cart</v-icon>
                <span></span>
              </v-btn>
            </td>
          </tr>
        </tbody>
      </template>
    </v-simple-table>
  </div>
</template>
<script>
export default {
  name: 'LeftTableMenu',
  data() {
    return {
      title: "Menu Items", 
    };
  },
  methods: {
    addToBasket(item) {
      this.$store.dispatch("addToBasket", item);
    },
  },
  computed: {
    showError() {
      return this.$store.getters.showError;
    },
    menuItems() {
      return this.$store.getters.menuItems;
    },
  },
};

Upvotes: 1

Views: 631

Answers (4)

A. L
A. L

Reputation: 12669

Should probably be watching backStatus and then do your alert stuff

watch: {
    // whenever question changes, this function will run
    backStatus: function (newVal, oldVal) {
        this.alert = newVal;
        const response = setTimeout(() => {
            this.alert = oldVal;
        }, 100); 
        // swap the vals around if needed
    }
}

maybe you might need immediate too, but that's up to how your want to display things.

https://v2.vuejs.org/v2/guide/computed.html#Watchers

Upvotes: 1

Igor Trindade
Igor Trindade

Reputation: 111

You can achieve this timeout on alert using watch like the others guys said:

<template>
  <div class="w-full">

    <div class="w-full" v-for="item in cart" :key="item.id">
      <p>{{item.name}}</p>
    </div>

    <div class="w-full p-2 bg-yellow" v-if="alert">
      <p>Your cart is empty</p>
    </div>
  </div>
</template>

<script>
import axios from 'axios'
export default {
  name: 'CartList',
  data() {
    return {
      cart: [],
      alert: true
    }
  },
  watch: {
    cart(val) {
      if(!val.length) {
        this.alert = true
      } else {
        setTimeout(() => {
         this.alert = false
        }, 2000)
      }

    }
  },
  mounted() {
    this.getCart()
  },
  methods: {
    getCart() {
      axios('/cart/get').then((response) => {
        this.cart = response.data.cart
      })
    }
  }
}
</script>

But you can add some extra code to your request function and add the timeout there too:

getCart() {
  axios('/cart/get')
  .then((response) {
    if(response.data.cart.length) {
      setTimeout( () => { 
        this.alert = false
      }, 2000)
    }
  })
}

Upvotes: 0

dimshik
dimshik

Reputation: 1281

You can add a watcher on your computed property to see if it's changed.

When it changes you can update your data to show or the "Success" alert and then set a timeout to hide it back again after some time.

Here is an updated example with some changed param names for clarity.

I changed the computed name to be emptyBasket

computed: {
  emptyBasket() {
    return this.$store.getters.basket.length === 0;
  }
},

I added showSuccessAlert to data

data() {
  return {
    showSuccessAlert: false
  };
},

And here it the watcher that updates the showSuccessAlert

watch: {
  emptyBasket: {
    immediate: true,
    handler(newVal, oldVal) {
      this.showSuccessAlert = !newVal;
      setTimeout(() => {
        this.showSuccessAlert = oldVal;
      }, 5000);
    }
  }
}

The watcher will be triggered immediately (not sure you need it), newVal and oldVal are representing the new and old state of emptyBasket. So when newVal is false it means that the basket is not empty, hence the update of showSuccessAlert = !newVal

I created a simple working sandbox with your code.

Here is the link:

https://codesandbox.io/s/smoosh-cherry-ngpqu?file=/src/App.vue

Upvotes: 1

Denno
Denno

Reputation: 2178

Rather than calling changeAlertStatus in the v-if directive, can that just be bound to the this.alert property? Then, when the Add to Cart button is clicked, its callback can set this.alert to true, causing the alerts to display. Just after setting this.alert to true, register the setTimeout to revert it back to false

Example: (Please excuse the abstract-ness of it, I feel like this is some missing code from the original post, specifically the add to cart button)

<template>
  <div id="app">
    <div class="alerts" v-if="alert">
      <div>Thank you</div>
    </div>
    <button @click="handleAddToCart">
      Add to cart
    </button>
  </div>
</template>

<script>
module.exports = {
  el: "#app",
  data: {
    alert: false,
    basketStatus: false
  },
  methods: {
    handleAddToCart() {
      this.alert = true;
      setTimeout(() => {
        this.alert = false;
      }, 3000);
    }
  }
};
</script>

Upvotes: 0

Related Questions