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

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
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;
},
},
};

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

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

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>

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)
}
})
}

Related

vuetify v-radio doesn't select one radio in table nuxt js

so i try to make a multiple grid radio with condition it select one radio button and save it as json with key: question and value: column where the radio buttoin selected
<template>
<v-radio-group class="mt-0" v-model="answer" :rules="answerRule">
<thead>
<tr>
<th>Pilihan</th>
<th v-for="(option, keys) in columns" :key="keys + 'A'">
{{ option.value }}
</th>
</tr>
<tr v-for="(option, keys) in rowsCols" :key="keys + 'C'">
<th>{{ option.value }}</th>
<td
v-for="(optioncol, keys) in option.columns"
:key="keys + 'B'"
>
<v-radio-group
v-model="answer"
#change="update"
:rules="answerRule"
>
<v-radio
class="radioA"
solo
v-bind:key="option.id"
:value="optioncol"
>
</v-radio>
</v-radio-group>
</td>
</tr>
</thead>
<tbody></tbody>
</v-radio-group>
</template>
here how it looks in the browser but it selected all row
[![enter image description here][1]][1]
here is my script on how to load the data and try to save the json...
export default {
props: ['question'],
data() {
return {
rows: this.question.options,
answer: [],
columns: this.question.optionsColumns,
answerRule: [],
}
},
methods: {
async update() {
try {
let payload = {
questionId: this.question.id,
value: this.answer,
questionName: this.question.question,
}
//update question options
await this.$store.commit('answers/update', payload)
} catch (err) {
this.$store.commit('alerts/show', {
type: 'error',
message: err.response
? this.$t(err.response.data.message)
: this.$t('SERVER_ERROR'),
})
}
},
},
beforeMount() {
if (this.question.required) {
this.answerRule.push(
(v) => v.length > 0 || this.$t('QUESTION_REQUIRED')
)
}
},
}
</script>
Any help on that? because i've try and still cannot figure it out
Here is the Data that i already change into new format :
row:[{id:1,value:"asdasd",columns:[a,b,c]},{id:2,value:"asdasd",columns:[a,b,c]}{id:3,value:"asdasd"}{id:1,value:"asdasd",columns:[a,b,c]}]
yet i still got the same problem
[1]: https://i.stack.imgur.com/6fsIF.png

Dynamic pricing calculator in vue js

*Edited
I'm building a dynamic pricing calculator feature using vue js. I've made the code and there are still some problems that make the features that I made not work properly. I have a dropdown menu which when clicked will display different tabs and different content (dynamic component). And also I have an estimate card for details and prices, and every item can be deleted.
I have several problems which are as follows:
When I was on the Storage tab, and submit data after I selected several options, the software tab details and price also appeared in estimation card. I want is the pricing details just the Storage. Likewise if I just submit in the Software tab, then only details and prices appear from the Software tab. And if I submit in Software and Storage, the result of both will appear.
When I submit data on the Storage tab, and I go to the Software tab, the price details on the estimation card are gone. I want the data still appear. it's not possible if I use <keep-alive> because it's not a dynamic component.
I have the delete button item in the estimation card. What I want it to be will delete the item according to the option that was clicked. But still not working very well.
Currently, I only have 2 tabs, Storage and Software. If I have other tabs, how do I make this system dynamic?
Can anyone help me solve this problem and explain it step by step?
this is my code that I made on Codesandbox: https://codesandbox.io/s/dynamic-price-calculator-o77ig
You must make the priceEstimationData dynamic. You can show the data from props without using watch. I'm only providing some code here. For full version please go to the codesandbox.
<!-- PriceEstimation.vue -->
<template v-if="priceEstimationData">
<div
:key="id"
v-for="(data, id) in priceEstimationData"
class="estimation-category"
>
<div class="category-price d-flex justify-content-between">
{{ data ? data.type[0].toUpperCase() + data.type.slice(1) : ""}}
Delete
</div>
<table class="table table-borderless">
<tbody>
<tr>
<td>
<p>
Storage Type: <span>{{ data.typeName }}</span>
</p>
<p>
Quantity Storage: <span>{{ data.qunantity }}</span>
</p>
<p>
Duration Storage: <span>{{ data.duration }}</span>
</p>
</td>
<td class="text-right">
<p>${{ data.typeValue }}</p>
<p>${{ data.qunantity * data.quantityPrice }}</p>
<p>${{ data.duration * data.durationPrice }}</p>
</td>
</tr>
</tbody>
</table>
</div>
</template>
After looking at your code. I notice that you are using the watch property. I have removed the watch property to make the estimation data still. See the code below
// TheCalculator.vue
// remove watch that makes the priceEstimationData null
/* watch: {
selectedTab: function () {
this.priceEstimationData = null;
},
}, */
computed: {
selectedTabProps() {
return this.selectedTab === "storage-calculator"
? { event: this.setPriceEstimationData }
: null;
},
},
I have modified your code and move the removeItem methods to parent. Because the child only read the data. See below
// TheCalculator.vue
methods: {
setSelectedTab(tab) {
this.selectedTab = tab;
},
setPriceEstimationData(data) {
this.priceEstimationData.push(data);
},
removeItem(id) {
// remove findIndex function
this.priceEstimationData.splice(id, 1);
},
},
I have modified TheCalculator.vue, PriceEstimation.vue, ServiceCalculator.vue, and StorageCalculator.vue.
Edit
I have fixed the duration bug in ServiceCalculator.vue
// ServiceCalculator.vue
// fixes duration bug
updateDurationServices(val) {
this.duration = val;
},
Codesandbox: https://codesandbox.io/s/dynamic-price-calculator-forked-ls7n6
Enjoy!
Here's a very crude bunch of components that gets this job done - without the SUBMIT button.
Vue.component("ProductSetup", {
props: ['productKey', 'product'],
methods: {
handleInputChange(e) {
this.$emit("update:product-type", {
...e,
product: this.productKey,
})
},
handleQuantityChange(e) {
this.$emit("update:product-quantity", {
...e,
product: this.productKey,
})
},
},
template: `
<div>
<label
v-for="(val, key) in product.types"
:key="key"
>
{{ key }}
<input
type="radio"
:value="val"
:name="productKey"
#change="() => handleInputChange({ type: key })"
/>
</label>
<input
type="number"
min="0"
#input="(e) => handleQuantityChange({ quantity: e.target.value })"
/>
</div>
`,
})
Vue.component("PriceEstimate", {
props: ["products"],
template: `
<div>
<div
v-for="(product, productKey) in products"
>
{{ productKey }}<br />
{{ productKey }} {{ product.chosen }}: {{ product.types[product.chosen] }}<br />
Quantity {{ productKey }}: {{ product.quantity * product.pricePerPiece }}<br />
TOTAL: {{ product.types[product.chosen] + product.quantity * product.pricePerPiece }}
</div>
</div>
`,
})
new Vue({
el: "#app",
computed: {
estimates() {
return Object.fromEntries(
Object.entries(
this.products
).filter(([key, val]) => {
return val.chosen && val.quantity
})
)
},
},
data() {
return {
products: {
storage: {
chosen: null,
types: {
type1: 60,
type2: 70,
},
pricePerPiece: 20,
quantity: null,
},
os: {
chosen: null,
types: {
os1: 60,
os2: 70,
os3: 80,
},
pricePerPiece: 35,
quantity: null,
},
},
}
},
methods: {
handleUpdateProduct({
type,
product
}) {
this.products[product]["chosen"] = type
},
handleUpdateQuantity({
quantity,
product
}) {
this.products[product]["quantity"] = quantity
}
},
template: `
<div>
<div
v-for="(obj, product) in products"
>
Chosen {{ product }}: {{ obj.chosen }}<br />
Quantity of {{ product }}: {{ obj.quantity }}
<hr />
<product-setup
:product-key="product"
:product="obj"
#update:product-type="(e) => handleUpdateProduct(e)"
#update:product-quantity="(e) => handleUpdateQuantity(e)"
/>
<hr />
</div>
<price-estimate
:products="estimates"
/>
</div>
`,
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
The problem is in the parameter received by priceEstimationData, it does have servicesDuration nor servicesTypePrice.
In ServiceCalculator.vue, the "data" is:
var data = {
servicesTypeName: selectedServicesType.name,
servicesTypeValue: selectedServicesType.value,
qunantity: this.quantiti,
durationServices: this.durationServices,
};
You must pass also the propierties servicesDuration and servicesTypePrice.
Regards

Vue js: How to hide button

i'm new to Vue js in this code below , i wanted to hide button "Clear Filter" when nothing selected and show the button only when function " selectedAnswer(index)" called so that it will show only when filter applied otherwise it should be hided , is there a way to do it in my code?
and thanks in advance
<template>
<div class="container" width=800px>
<b-row>
<b-col cols="8">
<h1> Recently Asked </h1>
<ul class="container-question" v-for="(question1,index) in questions" :key="index">
<li>
{{question1.question}}
</li>
</ul>
</b-col>
<b-button class="outline-primaryy" style="margin:auto;" #click="ClearFilter" >Clear Filter</b-button>
</div>
<router-view />
</div>
</template>
<script>
export default {
data() {
return {
questions: [],
answered: null,
index: 0,
selectedIndex: null,
}
},
methods: {
selectedAnswer(index) {
this.selectedIndex = index;
this.questions = this.questions.filter((question) => question.incorrect_answers.includes(index))
console.log(index)
},
ClearFilter() {
this.questions = this.unmutated
},
watch: {
question1: {
handler() {
this.selectedIndex = null;
this.answered = false;
},
},
},
},
mounted: function() {
fetch('https://opentdb.com/api.php?amount=10&category=9&difficulty=medium&type=multiple', {
method: 'get'
})
.then((response) => {
return response.json()
})
.then((jsonData) => {
this.questions = jsonData.results
this.unmutated = jsonData.results;
})
}
}
</script>
You just need to add a v-if="selectedIndex" to your btn element.
ie
<b-button v-if="selectedIndex" class="outline-primaryy" style="margin:auto;" #click="ClearFilter" >Clear Filter</b-button

Retain filled form fields on page refresh in Vue

The issue I am facing here is, I am not able to figure out how can I retain the values in the form on page refresh. Each time I refresh the page all the filled values in the form are gone.
Help me resolve this issue. I was thinking of using localStorage but not sure how I can implement it.
<template>
<v-card class="mb-12">
<v-form :model='user' class="content-padding" ref='pdfInputs'>
<div class="section-header">
User
</div>
<v-container fluid>
<ul>
<li v-for="(input, index) in user.inputs">
<input type="text" v-model="input.one"> - {{ input.one }}
<input type="text" v-model="input.two"> - {{ input.two }}
<button type="button" #click="deleteRow(index)">Delete</button>
</li>
</ul>
</v-container>
</v-form>
</v-card>
</template>
<script>
export default {
data () {
return {
user: {
inputs: []
}
}
}
methods: {
addRow() {
this.user.inputs.push({
one: '',
two: ''
})
},
deleteRow(index) {
this.user.inputs.splice(index,1)
}
}
}
</script>
There is watch functionality in vue
export default {
data () {
return {
user: {
inputs: []
}
}
},
mounted() {
this.user.inputs = JSON.parse(localStorage.getItem('form')) || [];
},
watch: {
user: {
handler: function() {
localStorage.setItem('form', JSON.stringify(this.user.inputs));
},
deep: true
}
},
methods: {
addRow() {
this.user.inputs.push({
one: '',
two: ''
})
},
deleteRow(index) {
this.user.inputs.splice(index,1)
}
}
}

Passing data to the modal in Vue not working

I am trying to pass data to the UserModal. But the issue I am facing here is that the value of
user_clicked field is set when the openuserdialog method runs(checked in console: the value is assigned) but I am not able to pass it as an argument to the modal. Please help me solve the problem.
<v-data-table :items="users" :disable-initial-sort="true" :mustSort="true" hide-actions>
<template slot="items" slot-scope="props">
<td>{{ props.item.file_type.name }}</td>
<td>{{ props.item.created_at | moment }}</td>
<td><a #click="openUserDialog(props.item.id, props.item.user_type)" href='javascript:void(0);' class="details-link"><span class="hidden-xs-only">UserTypes</span><span class="hidden-sm-and-up">User Types</span></a></td>
</template>
</v-data-table>
<v-dialog v-model="userDialog" max-width="1275">
<UserModal :document="user_clicked" />
<div class="text-xs-right">
<v-btn class='vue-file-button text-right' #click="closeUserDialog" >Close</v-btn>
</div>
</v-dialog>
<script>
import UserModal from 'views/users/shortlisted_users.vue';
export default {
components: {
UserModal
},
data: function() {
return {
userDialog: false,
user_clicked: ''
}
}
methods: {
openUserDialog(document_id, user_type) {
this.userDialog = true;
this.user_clicked = user_type;
console.log(this.user_clicked);
},
closeUserDialog(document_id) {
this.userDialog = false;
}
}
</script>
Update 1
openUserDialog(document_id, user_type) {
this.user_clicked = user_type;
this.userDialog = true;
console.log(this.user_clicked);
}
Update 2
<template>
<div>
<v-card id="users-card">
<Users :users="users"></Users>
</v-card>
</div>
</template>
<script>
import 'vue-awesome/icons';
import Icon from 'vue-awesome/components/Icon';
import Users from 'views/user/_user_table.vue';
export default {
components: {
Icon,
Users
},
props: ['document'],
data: () => ({
users: [],
tab_view: 'tab-users-card'
}),
created: function() {
console.log(this.document);
this.fetchUsers(this.document);
},
methods: {
fetchUsers(document) {
this.$axios.get('/my_account/users/document_suggested_users.json', {
params: {
document: document.id
}
})
.then(response => {
this.users = response.data;
})
},
}
};
</script>
The problem is that you are trying to use document in the created handler of the component which is far too early in its life-cycle.
Instead, one approach is to use a watch handler in your UserModal like this:
watch: {
document: function () {
console.log(this.document);
if (this.document) {
this.fetchUsers(this.document);
}
}
}
Try to declare your prop like so:
props: {
document: Object
}

Categories