Making a value from a fetch call available immediately - vue.js

I have a Go program written that has a form which checks for the existence of a file by calling a fetch on a route inside the Go code. If the file exists or not, a boolean is return inside of a JSON as fileExists. I'm having trouble with the fetch call's JSON updating this.found boolean immediately.
What happens is that when I press enter or click the buttons, the form is submitted via call to onSubmit where the checkFile() is called which does the fetch. Somehow, I have to press enter twice to see the value returned by the fetch as it is not updating the this.found immediately. I am probably thinking about this the wrong way, but I figure it wouldn't to ask. Here's the code, if anyone can help so that clicking or submitting will be based on the correct value returned by the checkFile call:
<div class="jumbotron">
<div class="container">
<div class="alert alert-dark" role="alert">
</div>
<h1 class="display-3">Title</h1>
<div id="app">
<form ref="myForm" method="POST" v-on:submit.prevent="onSubmit" action="/push" class="needs-validation" id="myForm" novalidate="true">
<div class="form-group">
Canned List:
<input v-model="cannedlist" ref="cannedlist" type="text" class="form-control" name="fileListFile" id="filelist"
aria-describedby="fileListFileHelp"
autocomplete="off" :disabled="!!individuallist" v-on:submit.prevent="onSubmit" />
</div>
<div class="form-group">
Path List:
<textarea v-model="individuallist" ref="cannedlist" :disabled="!!cannedlist" class="form-control" rows=10 name="fileListRaw" id="files" autocomplete="off"></textarea>
</div>
<div class="form-group">
<button v-on:submit.prevent="onSubmit" type="submit" name="button" value="submit" id="submitButton" class="btn btn-primary" :disabled="isDisabled">Submit</button>
<button v-on:submit.prevent="onSubmit" type="submit" name="button" value="checkOnly" id="checkOnlyButton" class="btn btn-primary" :disabled="isDisabled">Submit 2</button>
</div>
</form>
</div>
</div>
</div>
<script src="/static/js/vue.min.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
// cannedlist: "filelist.txt",
individuallist: "",
found: false,
},
computed: {
isDisabled: function() {
//found = !found;
return (this.cannedlist.length <= 0 && this.individuallist.length <= 0);
},
},
methods: {
isDisabledNew: function() {
alert((this.cannedlist.length <= 0 && this.individuallist.length <= 0));
// return (this.cannedlist.length <= 0 && this.individuallist.length <= 0);
return false;
},
isFieldDisabled: function(e) {
//console.log(app.$refs.individuallist.disabled);
return false;
},
onSubmit: function() {
if (this.cannedlist.length > 0) {
this.checkFile();
if (this.found == true) {
this.$refs.myForm.submit();
return;
}
} else if (this.individuallist.length > 0) {
this.$refs.myForm.submit();
return;
}
},
checkFile: function() {
var url = 'http://localhost:9000/CheckIfFileExists?name=' + this.cannedlist;
return fetch(url)
.then(response => {
if (response.ok) {
var v = response.json().then( response => { this.found = response.fileExists; } );
return this.found;
}
return response.json().then(error => ({ error }));
});
return this.found;
},
}
});
</script>

Your onSubmit function calls checkFile and expects found to be updated:
onSubmit: function() {
if (this.cannedlist.length > 0) {
this.checkFile();
if (this.found == true) {
this.$refs.myForm.submit();
return;
}
} else if (this.individuallist.length > 0) {
this.$refs.myForm.submit();
return;
}
},
But checkFile returns a Promise. The Promise resolves by updating found. So you need to put your found checking inside a then block:
onSubmit: function() {
if (this.cannedlist.length > 0) {
this.checkFile()
.then(() => {
if (this.found == true) {
this.$refs.myForm.submit();
}
});
return;
} else if (this.individuallist.length > 0) {
this.$refs.myForm.submit();
return;
}
},

Here're the changes I made:
methods: {
onSubmit: function(event) {
if (this.cannedlist.length > 0) {
this.checkFile()
// This promise capture is the key I was missing
.then( (data) => {
this.found = data.fileExists;
if (this.found == true) {
this.$refs.myForm.submit();
} else {
alert("File not found: " + this.cannedlist);
}
});
} else if (this.individuallist.length > 0) {
this.$refs.myForm.submit();
}
},
checkFile: function() {
var url = 'http://localhost:9000/CheckIfFileExists?name=' + this.cannedlist;
return fetch(url).then((response) => response.json());
}

Related

Conditional rendering after function - VueJS [duplicate]

This question already has answers here:
Use arrow function in vue computed does not work
(6 answers)
Closed 1 year ago.
I am currently writing a template for sending an email and after the function I want to conditionally render a success or error block. For some reason it is not working. The function itself is working, however neither success or error block is rendered. Please find my code below.
Template:
<form v-if="success==null" #submit.prevent="sendEmail" >
... //form code
<input name="submit" type="submit" class="btn" value="send" />
</form>
<b-alert variant="success" v-if="success">success</b-alert>
<b-alert variant="error" v-if="!success">error</b-alert>
Function:
data() {
return {
success: null
}
},
methods: {
sendEmail: (e) => {
... // request code
.then((result) => {
this.success=true
console.log('success')
}, (error) => {
this.success=false
console.log('error')
})
}
}
I think you just need to use strict comparing for error block
like:
success === false
There can be problems with !success because it's true for both null and false.
Also you can use string instead of boolean|null
var app = new Vue({
el: '#app',
data: {
type: 'form',
formData: {
email: '',
}
},
methods: {
sendEmail() {
if (this.formData.email) {
this.type = 'success';
} else {
this.type = 'error';
}
setTimeout(() => this.type = 'form', 10000);
}
}
})
.success {
color: green;
}
.error {
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<form v-if="type === 'form'" #submit.prevent="sendEmail">
<input type="email" v-model="formData.email" />
<button type="submit">Submit</button>
</form>
<div v-if="type === 'success'" class="success">Success!</div>
<div v-if="type === 'error'" class="error">Error!</div>
</div>
Are you sure your success/error handler are called ?/
In your method sendEmail you should define define it without an arrow function
sendEmail (e) {
... // request code
.then((result) => {
this.success=true
console.log('success')
}, (error) => {
this.success=false
console.log('error')
})
}
Try this
<b-alert :variant="success ? 'success' : 'error'" v-if="success!=null" :key="success">
<span v-if="success">success</span>
<span v-else>error</span>
</b-alert>

How to make increment/decrement cart count in vue

I've created a function for increment and decrement so that I can change the count in the cards.count.
The function for increment count works like this
but the functions for decrement count, it doesn't work.
When I click the function for decrement, this output will appear
Error in v-on handler: "TypeError: Cannot read property 'product_id' of undefined"
I don't know why there is an error in the product_id, even though the increment function also has a product_id.
I hope you can help me figured out the problem
<template>
<div v-if="cartList && cartList.length > 0">
<div
class="item-loop container col"
v-for="(cards, index) in cartList"
:key="generateKey(cards.product_id, cards.count)"
>
<div class="items row">
<div class="image col-4">
<img class="img-fluid pt-2" :src="cards.product_img" />
</div>
<div class="content text-left col-8">
<button
v-on:click="cartList.splice(index, 1)"
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">×</span>
</button>
<h5 class="text-left">{{ cards.product_name }}</h5>
<div class="prices" :key="cards.product_id">
<div class="value-cart form-group row">
<font-awesome-icon
:icon="['far', 'minus-square']"
size="2x"
class="minus-button"
#click="min(cards.product_id)"
/>
<input
type="number"
class="input-pm bg-light form-control p-0 text-center angka"
:value="cards.count"
/>
<font-awesome-icon
:icon="['far', 'plus-square']"
size="2x"
class="plus-button"
#click="plus(cards.product_id)"
/>
</div>
<p>Rp. {{ cards.product_price * cards.count }}</p>
</div>
</div>
</div>
</div>
<div class="cart-order">
<div class="order-total">
<div class="row">
<h4 class="col-6 font-weight-bold text-left">Total</h4>
<h5 class="col-6 font-weight-bold text-right">
Rp. {{ totalprice }}
</h5>
</div>
</div>
<p class="text-left"><strong>*Not including tax(10%)</strong></p>
<b-button
class="mt-3"
variant="primary"
#click="invoice()"
v-b-modal="'modal-checkout'"
block
>Checkout</b-button
>
<b-button class="mt-2" variant="danger" #click="resetcart" block>
Cancel </b-button
><br /><br />
</div>
</div>
</template>
export default {
components: {
Features,
Card,
},
data() {
return {
datamenu: {},
cartList: [],
invoiceid: 0,
tax: 0,
searchMenu: null,
menu: null,
formCheck: {
amount: 0,
invoice: "",
cashier: "abiwardani",
menu_name: "",
},
};
},
methods: {
plus(product_id) {
let result = this.cartList.find((res) => {
if (res.product_id == product_id) {
return res.product_id;
}
});
if (result) {
for (let i = 0; i < this.cartList.length; i++) {
if (this.cartList[i].product_id == product_id) {
const newFoodObject = {
...this.cartList[i],
count: this.cartList[i].count + 1,
};
console.log("plus");
this.$set(this.cartList, i, newFoodObject);
}
}
}
},
min(product_id) {
let result = this.cartList.find((res) => {
if (res.product_id == product_id) {
return res.product_id;
}
});
if (result) {
for (let i = this.cartList.length; i > 0; i--) {
if (this.cartList[i].product_id == product_id && this.cartList[i].count > 0){
const newFoodObject = {
...this.cartList[i],
count: this.cartList[i].count - 1,
};
this.$set(this.cartList, i, newFoodObject);
}
}
}
},
generateKey(key1, key2) {
return `${key1}-${key2}`;
},
},
mounted() {
axios
.get(process.env.VUE_APP_URL + "product")
.then((res) => {
this.datamenu = res.data.result;
})
.catch((err) => {
console.log(err);
});
}
Option 1:
i should start with length-1 and should go up to 0
min(product_id) {
let result = this.cartList.find((res) => {
if (res.product_id == product_id) {
return res.product_id;
}
});
if (result) {
for (let i = this.cartList.length-1; i >= 0; i--) {
if (this.cartList[i].product_id == product_id && this.cartList[i].count > 0){
const newFoodObject = {
...this.cartList[i],
count: this.cartList[i].count - 1,
};
this.$set(this.cartList, i, newFoodObject);
}
}
}
Option 2:
You do not need 2 methods.. just have one
updateQty(product_id,mode) {
let result = this.cartList.find((res) => {
if (res.product_id == product_id) {
return res.product_id;
}
});
if (result) {
for (let i = 0; i < this.cartList.length; i++) {
if (this.cartList[i].product_id == product_id) {
const newFoodObject = {
...this.cartList[i],
count: mode === 'INCRE' ? this.cartList[i].count + 1 : this.cartList[i].count - 1,
};
console.log("plus");
this.$set(this.cartList, i, newFoodObject);
}
}
}
},
use it like
<font-awesome-icon
:icon="['far', 'minus-square']"
size="2x"
class="minus-button"
#click="updateQty(cards.product_id,'INCRE')"
/>
and
<font-awesome-icon
:icon="['far', 'minus-square']"
size="2x"
class="minus-button"
#click="updateQty(cards.product_id,'DECRE')"
/>
Your problem is in the final expression of your for statement inside your min() method.
Change 'i--' with 'i++' to increase your index in each iteration. This will avoid accessing the 'cartList' array with a negative index (which is causing the problem, as it gets undefined):
min(product_id) {
let result = this.cartList.find((res) => {
if (res.product_id == product_id) {
return res.product_id
}
})
if (result) {
for (let i = this.cartList.length; i > 0; i++) {
👆
...
Here is how I did that
HTML:
<button #click="increment()">+</button>
<input :value="amount" />
<button #click="decrement()">-</button>
JS:
data() {
return {
amount: 0,
};
},
methods: {
increment() {
this.amount++;
},
decrement() {
this.amount--;
},
},

How to make disabled button after click in Vuejs

I have a button on my website that gives bonuses to the user. Button have several conditions in 1 button:
<button class="btn btn--small btn--purple" :disabled="isDisabled" #click="takeBonus">Take</button>
<script>
......
computed: {
isDisabled() {
return this.heal_used === 1 || this.diff < 10;
this.$forceUpdate();
},
},
.......
</script
But when user click Take button, and if all success, button is still active this.$forceUpdate(); not working. And i need make when user click Take button, and if all success, make this button disabled.
My full Bonus.vue:
<template>
<div class="inner-page">
<div class="account" v-if="loaded && !$root.isMobile">
<div class="page-header">
</div>
<div class="form-panels hide-below-m">
<div class="col-7" style="margin-top: 5rem;margin-right: 3rem;">
<div class="faucet-component mx-5" rv-class-boost="data.boostIsOpen">
<img src="https://dota2.skins1.games/src/img/components/shine.png?v=8ce59643e70cb2f8550deb6a249b5f29" class="faucet-component__shine-bg">
<div class="faucet-component__content d-flex justify-content-between align-items-center flex-column w-100" style="
height: 15rem;">
<div class="faucet-component__available-amount-block round-circle p-2">
<div class="faucet-component__availabe-amount-coins d-flex justify-content-center align-items-center round-circle h-100" rv-currency="model:amount">Спасение</div>
</div>
<!-- rivets: unless model:cnt | eq 0 --><div class="faucet-component__remaining">
<span rv-t="">Left</span>:
<span>{{ bonus_num }}</span><br>
<span rv-t=""></span>
<span>{{ diff }}</span>
</div>
<!-- rivets: if model:cnt | eq 0 -->
<div class="faucet-component__buttons-container d-flex align-items-center w-75 justify-content-around">
<button class="btn btn--small btn--purple" :disabled="isDisabled" #click="takeBonus">Take</button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
loaded: false,
bonus: {},
diff: {},
user: {},
bonus_num: 0,
heal_used: {}
}
},
mounted() {
this.$root.isLoading = true;
if (!this.$cookie.get('token')) {
this.$root.isLoading = false;
this.$router.go(-1);
}
this.domain = window.location.protocol + '//' + window.location.hostname;
setTimeout(() => {
this.getUser();
}, 100);
},
computed: {
isDisabled() {
return this.heal_used === 1 || this.diff < 10;
this.$forceUpdate();
},
},
methods: {
getUser() {
this.$root.axios.post('/user/getProfile')
.then(res => {
const data = res.data;
console.log(data.heal_used);
console.log(data.diff);
this.loaded = true;
this.user = data.user;
this.bets = data.bets;
this.bonus = data.bonus;
this.diff = data.diff;
this.heal_used = data.heal_used;
this.new_day = data.new_day;
this.bonus_num = data.bonus_num;
this.$root.isLoading = false;
})
.catch(err => {
this.$root.isLoading = false;
this.$router.go(-1);
})
},
takeBonus() {
this.$root.axios.post('/user/takeBonus', {
value: this.user.cashback
})
.then(res => {
const data = res.data;
if (data.type === 'success') {
console.log(data.heal_used);
this.bonus_num = data.bonus_num;
this.$root.user.balance = data.newBalance;
this.heal_used = data.heal_used;
this.$forceUpdate();
}
this.$root.showNotify(data.type, this.$t(`index.${data.message}`));
})
},
}
}
How i can make it, when user click Take button, and if all success, so that the Take button becomes disabled?
I'm sorry but your code has no indentation, so I just did that on jsfiddler so you know "How to make disabled button after click in Vuejs". You can have a look on : https://jsfiddle.net/o81yvn05/1/
<div id="app">
<button :disabled="isDisabled" #click="disableButton()">Please click here</button>
</div>
<script>
new Vue({
el: "#app",
data: {
isDisabled: false,
},
methods: {
disableButton() {
this.isDisabled = true
}
}
})
</script>

Error mesage quickly displaying then hiding as expected in VUE

I have a page which I validate the email add input #blur. This works perfectly and displays the error message if it fails validation rules set but the issue I have is that due to the #blur, when I click my reset button the error quickly displays then hides and this is poor UI and I want to stop it but can't figure out how to.
HTML
<div class="card" v-on:click="select($event)">
<div class="card-body">
<div class="form-group row">
<label class="col-sm-3 col-form-label pr-0" for="emailAddField">Email <span class="text-danger">*</span></label>
<div class="col-sm-9">
<div class="input-group">
<input id="emailAddField" ref="pdEmailAdd" class="form-control" type="search" :value="pdEmailAdd" #input="pdEmailAddInput" #blur="emailInputValChecker($event)" placeholder="Enter an email address">
<div class="input-group-append" :class="emailButtonValidation">
<a class="btn input-group-text primaryBtn" :class="emailButtonValidation" type="button" :href="'mailto:' + pdEmailAdd">
<i class="far fa-envelope"></i>
</a>
</div>
</div>
<div v-if="emailFormatErrorMsg" class="text-danger">Incorrect email address format</div>
</div>
<div class="card-footer">
<button id="resetButton" ref="resetButton" class="btn btn-warning col-4" #click="pdInitPageStates($event)" :disabled="resetDetails">
Reset details
</button>
</div>
</div>
I have 'hacked' at the card trying to use #click on the card to get the id but this didn't work so I set the id in my `data but not happy about it and sure there is a lot better way but I just can't figure it out
Code
data() {
return {
pdEmailAdd: '',
reg: /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,24}))$/,
detailsChanged: false,
emailIncorrectFormat: false,
targetId: 'resetButton', // HACK
targetId2: '', // HACK
}
},
computed: {
emailButtonValidation() {
if (!this.pdEmailAdd || !this.reg.test(this.pdEmailAdd)) {
if (this.pdEmailAdd === '') {
this.emailIncorrectFormat = false;
} else {
this.emailIncorrectFormat = true;
}
return 'disabled'
} else {
this.emailIncorrectFormat = false;
return ''
}
},
resetDetails() {
this.detailsChanged = false;
if (this.pdName != this.$store.state.account.firstname + ' ' + this.$store.state.account.lastname) {
this.detailsChanged = true;
}
if (this.telNoType === 'ddi' && this.pdTelNo != this.$store.state.account.ddi) {
this.detailsChanged = true;
} else if (this.telNoType === 'mobile' && this.pdTelNo != this.$store.state.account.mobile) {
this.detailsChanged = true;
} else if (this.telNoType === 'na' && this.pdTelNo != '') {
this.detailsChanged = true;
}
if (this.pdExtNo != this.$store.state.account.extension) {
this.detailsChanged = true;
}
if (this.pdEmailAdd != this.$store.state.user.adminemail) {
this.detailsChanged = true;
}
return !this.detailsChanged;
}
}
// Another hack to try set it soon as page loads
mounted() {
this.$refs.resetButton.click();
},
methods: {
emailInputValChecker(event) {
this.emailFormatErrorMsg = false;
if (!this.pdEmailAdd || !this.reg.test(this.pdEmailAdd)) {
if (this.pdEmailAdd === '') {
this.emailFormatErrorMsg = false;
} else {
this.select(event)
// Uses the 'dirty hacks'
if (this.targetId !== '' && this.targetId !== 'resetButton' && this.targetId2 !== 'resetButton') {
this.emailFormatErrorMsg = true;
};
}
}
},
select(event) {
this.targetId = event.target.id;
if (this.targetId === 'resetButton') {
this.targetId2 === 'resetButton';
} else if (this.targetId === '') {
this.targetId === 'resetButton';
}
}
}
Basically all I want is the input to check it passes validation when input is left unless the reset button is clicked then ignore it but think I've gone code blind and can't think of a way to do this.
The mousedown event on your reset button is what causes blur on the input to fire. Adding #mousedown.prevent to the reset button will stop that from happening specifically when the reset button is clicked.
This snippet ought to illustrate the solution. Remove #mousedown.prevent from the reset button and you'll see similar behavior to your issue, with the error briefly flashing.
new Vue({
el: '#app',
data: {
email: '',
error: null
},
methods: {
onBlur () {
if (this.email === 'bad') {
this.error = 'bad email!'
} else {
this.error = null
}
},
onReset () {
this.error = null
this.email = ''
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="onReset" #mousedown.prevent>Reset</button>
<p>
Type in "bad" and leave input to trigger validation error<br>
<input type="text" v-model="email" #blur="onBlur"/>
</p>
<p>{{ email }}</p>
<p v-if="error">error!</p>
</div>

vee-validate in steps form

I have a problem I'm trying to learn vee-validate
to a form that has steps but the problem is like as far
as the first drop I validate correctly a field from the second step is no longer works properly or someone could help this is all component and methods i think this is scope problem but i don't now?
<template>
<div>
<div v-if="step === 1">
<input
class="form-control loginForm__input"
type="text"
:class="{'input': true, 'is-invalid': errors.has('email') }"
name="email"
v-validate="'required|email'"
placeholder="Email adress"
v-model="registration.email"
/>
</div>
<div v-if="step === 2">
<input
class="form-control loginForm__input"
:class="{'input': true, 'is-invalid': errors.has('nameFirst') }"
v-validate="'required'"
name="nameFirst"
type="text"
placeholder="name"
v-model="registration.nameFirst"
/>
<div class="LoginButton">
<button
type="button"
class="btn button__blue"
#click="nextStep();progressbarNext();"
>Continue</button>
</div>
</div>
</template>
<script>
export default {
name: "Register",
components: {
},
data: function() {
return {
step: 1,
registration: {
email: null,
nameFirst: null,
}
};
},
computed: {},
methods: {
goBack() {
if (this.step === 1) {
this.$router.push("/");
return;
}
this.step--;
},
nextStep() {
this.$validator.validateAll().then(result => {
console.log(result);
if (result) {
if (this.step === 3) {
this.showModal = true;
(this.stepBannerThree = true), (this.step = 3);
return;
}
this.step++;
if (this.step == 2) {
return (this.stepBannerOne = true);
}
if (this.step == 3) {
return (this.stepBannerTwo = true);
}
return;
}
});
},
}
};
</script> ```