Vue.js - Input, v-model and computed property - vuejs2

I'm using vue-2.4 and element-ui 1.4.1.
Situation
I have a basic input which is linked with v-model to a computed property. When blur I check if the value input is greater or lower than min and max and I do what I have to do ... Nothing fancy here.
Problem
The value displayed in the input does not always equal enteredValue
Steps to reproduce
1) Input 60 --> Value displayed is the max so 50 and enteredValue is 50 (which is ok)
2) Click outside
3) Input 80 --> Value displayed is 80 and enteredValue is 50
Questions
How can I fix that so the value displayed is always the same as the enteredValue ?
Here is the minimal code to reproduce what I'm facing JSFIDDLE
<div id="app">
The variable enteredValue is {{enteredValue}}
<el-input v-model="measurementValueDisplay" #blur="formatInput($event)"></el-input>
</div>
var Main = {
data() {
return {
enteredValue: '',
max: 50,
min: 10
}
},
computed: {
measurementValueDisplay: {
get: function () {
return this.enteredValue + ' inchs'
},
set: function (newValue) {
}
},
},
methods: {
formatInput($event) {
let inputValue = $event.currentTarget.value;
if (inputValue > this.max) { this.enteredValue = this.max}
else if (inputValue < this.min) { this.enteredValue = this.min}
else this.enteredValue = inputValue
}
}
}
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')

Reading this vuejs, will understand what happens
"computed properties are cached based on their dependencies. A computed property will only re-evaluate when some of its dependencies have changed."
Changed some comportament of the code. Made run:
computed() method not works properly for update value in window. But if looks at console the value yes updated.
So, i remove computed (getter and setter), and put into data, without setter and getter( i dont like this in javascript).
var Main = {
data() {
return {
measurementValueDisplay:'fff',
enteredValue: '',
max: 50,
min: 10
}
},
computed: {
/*measurementValueDisplay: {
get: function () {
console.log('Computed was triggered so I assume enteredValue changed',this.enteredValue);
return this.enteredValue + ' inchs'
},
set: function (newValue) {
console.log('setter de qye', this.enteredValue);
}
},*/
},
methods: {
formatInput($event) {
this.enteredValue = 0;
let inputValue = $event.currentTarget.value;
console.log(inputValue);
if (inputValue > this.max) { this.enteredValue = this.max}
else if (inputValue < this.min) { this.enteredValue = this.min}
else this.enteredValue = inputValue
this.measurementValueDisplay = this.enteredValue + ' inchs'
console.log(this.enteredValue, 'oioioioio0');
}
}
}
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')

Your problem is that the values used in the computed property was not updated with the validation capping at 50 (Was 50, is now updated to 50, no need to recalculate), therefore v-model did not update the input.
I've edited your jsfiddle to use two computed properties:
One with an accessor to validate the entered value, one which returns the value with " inch" appended.
Here is the interesting part:
computed: {
measurementValueDisplay: {
get: function () {
return this.enteredValue
},
set: function (newValue) {
this.enteredValue = 0;
let inputValue = parseInt(newValue);
if(Number.isNaN(inputValue)){this.enteredValue = this.min}
else if (inputValue > this.max) { this.enteredValue = this.max}
else if (inputValue < this.min) { this.enteredValue = this.min}
else this.enteredValue = inputValue
}
},
valueWithInch(){
return this.enteredValue + " inch";
}
},

In case anybody still needs a hack for this one, you can use a value that will always change ( for example a timestamp )
var Main = {
data() {
return {
enteredValue: '',
max: 50,
min: 10,
now: 1 //line added
}
},
computed: {
measurementValueDisplay: {
get: function () {
return (this.now - this.now + 1 ) * this.enteredValue + ' inchs'; //line changed
},
set: function (newValue) {
this.now = Date.now(); //line added
}
},
},
methods: {
formatInput($event) {
let inputValue = $event.currentTarget.value;
if (inputValue > this.max) { this.enteredValue = this.max}
else if (inputValue < this.min) { this.enteredValue = this.min}
else this.enteredValue = inputValue
}
}
}

Related

Vue: Getting a time in a seconds on page mount and creating hh:mm:ss off of that?

I am getting a getSecondsUptime field when a component mounts, saving it to initialData and then want to increment that each second. However, I am getting an infinite loop warning and hundreds of console logs within seconds. I can't tell what I am doing wrong.
data() {
return {
time: 0,
}
},
mounted() {
this.time = this.$store.getters['into/getSecondsUptime'];
setInterval(this.calcTime, 1000)
},
methods: {
calcTime: function() {
console.log('foo');
this.time = this.time + 1;
let date = new Date(null);
date.setSeconds(this.time);
let result = date.toISOString().substr(11, 8);
return result;
}
}
you are calling your calcTime function multiple times. Maybe you can change to like this
mounted() {
this.time = this.$store.getters['into/getSecondsUptime'];
this.calcTime()
},
methods: {
calcTime: function() {
setInterval(() => {
this.time = this.time + 1
}, 1000);
}
}
You can call calcTime by Using window.setInterval which will be called every second. Like below
mounted() :{
this.time = this.$store.getters['into/getSecondsUptime'];
window.setInterval(() => {
this.calcTime();
}, 1000);
},
methods: {
calcTime() {
console.log('foo');
this.time = this.time + 1;
let date = new Date(null);
date.setSeconds(this.time);
let result = date.toISOString().substr(11, 8);
return result;
}
}

Change value after confirmation in input VUE-JS

I have a table with an input column. This input column will have value if it has been saved in ddbb before.
If this value changes, handled with an event '#blur', I show a modal to confirm the change.
My problem is that if you want to keep the old value it will always change...
I tried to change this value with javascript, but it doesn't work... Any suggestions?
This is my code:
<b-tbody>
<b-tr
v-for="(driver, index) in drivers"
:key="driver.clientId">
<b-td
data-label="Client"
:title="driver.clientName">{{ driver.clientName }}</b-td>
<b-td data-label="Numerator">
<b-input
:id="'numeratorDriver_'+index"
class="driver-numerator text-right"
type="text"
:value="(driver.driverNumerator === undefined) ? 0 : $utils.formatNumber(driver.driverNumerator)"
#blur="calculatePricingDriver($event, index)" /></b-td>
<b-td
class="text-right"
data-label="Pricing"
:title="driver.pricingDriver"><span>{{ driver.pricingDriver }}</span></b-td>
</b-tr>
</b-tbody>
<script>
function calculatePricingCustomDriver (event, index) {
let lastValue = this.drivers[index].driverNumerator
if (this.$store.getters.price !== undefined) {
let title = 'Modify numerator'
let message = 'If you modify numerator driver, the calculated pricing will be deleted'
this.$bvModal.msgBoxConfirm(message, {
title: title,
size: 'sm',
buttonSize: 'sm',
okTitle: 'Yes',
okVariant: 'primary',
cancelTitle: 'No',
cancelVariant: 'primary',
hideHeaderClose: false,
centered: true
})
.then(confirmed => {
if (confirmed) {
this.$http.get('delete-price', { params: { id: this.$store.getters.id } })
.then((response) => {
if (response.status === this.$constants.RESPONSE_STATUS_OK) {
this.price = ''
let newNumerator = event.target.value
this.drivers[index].driverNumerator = Number(newNumerator)
let sumTotal = _.sumBy(this.drivers, 'driverNumerator')
for (let i = 0; i < this.drivers.length; i++) {
this.drivers[i].pricingDriver = (this.drivers[i].driverNumerator / sumTotal).toFixed(2)
}
} else {
this.drivers[index].driverNumerator = lastValue
// this is that I want change because it doesn't work fine
document.getElementById('numeratorDriver_' + index).value = lastValue
}
})
} else {
this.drivers[index].driverNumerator = lastValue
document.getElementById('numeratorDriver_' + index).value = lastValue
}
})
.catch(() => {
/* Reset the value in case of an error */
this.$utils.showModalError()
})
} else {
let newNumerator = event.target.value
this.drivers[index].driverNumerator = Number(newNumerator)
let sumTotal = _.sumBy(this.drivers, 'driverNumerator')
for (let i = 0; i < this.drivers.length; i++) {
this.drivers[i].pricingDriver = (this.drivers[i].driverNumerator / sumTotal).toFixed(2)
}
}
}
</script>
how is calculatePricingCustomDriver being loaded into your Vue component? For it to be called like that from #blur you would need to define it as a method:
<template>
<!-- your table-->
</template>
<script>
export default {
name: "MyComponent",
methods : {
calculatePricingCustomDriver () {
// your code
}
}
}
</script>
Or it could be installed as a global mixin

ESLint Expected to return a value at the end of method

i have this code
computed: {
termOptions() {
if (this.newSchedule.originCharges.serviceType !== 'If Any') {
return this.serviceOptions.filter((val) => val.name !== 'If Any');
}
},
isMoreThanToday() {
if (this.newSchedule.validFrom) {
const date = new Date();
const isMore = this.newSchedule.validFrom < date.getDate();
return isMore;
}
},
}
and i got an error saying expected return value at the of method. Is there any way to fix this? thank you.
This is ESLint error. It tells you that a computed property should always return some value.
Your computed properties return values only when the condition of the if is true and don't return anything when it's false.
computed: {
termOptions() {
if (this.newSchedule.originCharges.serviceType !== 'If Any') {
return this.serviceOptions.filter((val) => val.name !== 'If Any');
}
else {
return []
}
},
isMoreThanToday() {
if (this.newSchedule.validFrom) {
const date = new Date();
const isMore = this.newSchedule.validFrom < date.getDate();
return isMore;
}
else {
return false
}
},
}
See this page for details.

Error message showing after cleannig inputs with Vue

I have a simple form to update a password with 3 inputs (old_password, new_password and new_password_confirmation).
After the submit, if it is updated successfully, the inputs are cleaned. The problem is that after this, the input errors are showed even when the conditional is false.
This is one of the inputs:
<input type="password" v-model="old_password">
<small class="text-danger" v-if="errors.old_password != ''">{{errors.old_password}}</small>
The logic:
<script>
export default {
data() {
return {
old_password: '',
errors: []
}
},
watch: {
old_password(value) {
this.old_password = value
if(value.length < 3) {
this.errors.old_password = 'Debes ingresar al menos 4 caracteres'
} else {
this.errors.old_password = ''
}
}
},
methods: {
updatePassword() {
this.$store.dispatch('updatePassword', data)
.then(response => {
this.old_password = ''
this.errors.old_password = ''
})
.catch(error => {
})
}
}
}
</script>
Because once input becomes empty your condition value.length < 3 is true and error will be shown
Simply add to your condition this one && value.length > 0
So the final code will look like
if(value.length < 3 && value.length > 0) {
this.errors.old_password = 'Debes ingresar al menos 4 caracteres'
} else {
this.errors.old_password = ''
}
Also, errors should be an object instead of an array - errors: {}

setInterval doesn't fire in vue

im learning vue and setInterval doesn't work like in normal javascript? the problem is that my update function doesn't get fired.
start gets fired from a button, and hours/minutes/seconds are bound to input fields with v-model, i get all console.logs before the setInterval.
export default Vue.extend({
name: "timer-c",
data() {
return {
startDate: undefined,
hours: "",
minutes: "",
seconds: "",
timeLeft: undefined,
endDate: undefined,
interval: undefined,
text: "00:00:00",
sub: undefined,
};
},
methods: {
start: function() {
if (this.sub === undefined) {
let sum = 0;
if (this.seconds.match(/^\d+$/)) {
sum = sum + this.seconds * 1000;
console.log(sum);
}
if (this.minutes.match(/^\d+$/)) {
sum = sum + this.minutes * 60 * 1000;
}
if (this.hours.match(/^\d+$/)) {
sum = sum + this.hours * 60 * 60 * 1000;
}
if (sum === 0) {
console.log(this.$refs.br_start);
this.failed = true;
} else {
console.log(sum);
this.failed = false;
this.endDate = new Date(Date.now() + sum);
console.log(this.endDate);
this.startDate = new Date(Date.now());
console.log(this.startDate);
this.interval = setInterval(time => this.update, 1000);
//this.sub = this.interval.subscribe(time => this.update(time));
}
}
},
update: function() {
console.log('test');
const timeRemaining = Math.round((Date.now() - this.endDate) / 1000);
this.text = timeRemaining;
if (new Date(Date.now()) >= this.endDate) {
console.log("test");
}
},
Try to not return the function but execute it
this.interval = setInterval(time => { this.update(time) }, 1000);