i am using vue3
with options api
as shown in the following stackbiltz
link here
i have input
and i want to set the value that to be displayed in the input
to, for example, to 4 whenever the user attempted to enter values lower than 4
it seems ridiculous but when i run the code, initially, if i entered a value lower than 4, then the value to be displayed in the input
is 4. this scenario is ok. but if i attempted for the second time to enter another value lower than 4, then the contents or
the value in the input
never changes
can you please tell me how to achieve that?
scenario
1st input: for input = 1 => it will change to 4..tha is oK
2nd input: for input = 3 => it will not change to 4...this is not ok because as long as the input is lower than 4, 4 must be displayed in the input box
note:
i know that if the user successviley attempted to enter values lower than 4, the `input` witll display or will change automatically to be set to 4 for the first attempt only, and for the further attempts the value in the `input` will not change to 4 despite
the user enters values lower than 4 because the `newVal` of the `watcher` does not change. but i do not know how to overcome this problem
update:13.11.2023:
as shown in the below posted code, this.§forceUpdate
is used as recommended by one of the memebers in SO community.
i would like to know if there is any other possible solution to sovle this question without resorting to this.§forceUpdate
?
code:GUIEpochAgo.vue
<template>
days ago:
<input
type="input"
:value="compPropsVModelInputDaysAgo"
@input="$emit('update:compPropsVModelInputDaysAgo', $event.target.value)"
:disabled="isInputDaysAgoDisabled"
@change="onInputDaysAgoChanged($event.target.value)"
/>
</template>
<script>
let debugTag = 'D.GUIEpochAgo.vue::';
let infoTag = 'I.GUIEpochAgo.vue::';
let verboseTag = 'V.GUIEpochAgo.vue::';
let warnTag = 'W.GUIEpochAgo.vue::';
let errorTag = 'E.GUIEpochAgo.vue::';
let WTFTag = 'WTF.GUIEpochAgo.vue::';
//
export default {
name: 'GUIEpochAgo',
data() {
return {
};
},
components: {},
props: {
vModelInputDaysAgo: {
type: null,
},
isInputDaysAgoDisabled: {
type: Boolean,
default: false,
},
},
emits: ['vModelInputDaysAgo'],
computed: {
compPropsVModelInputDaysAgo: {
get() {
console.log('get.compPropsVModelInputDaysAgo:', this.vModelInputDaysAgo);
return this.vModelInputDaysAgo;
},
set(value) {
console.log('set.compPropsVModelInputDaysAgo:', value);
this.$emit('update:vModelInputDaysAgo', value);
},
},
},
methods: {
onInputDaysAgoChanged(evt) {
const msg = JSON.stringify({ msg: verboseTag + 'onInputDaysAgoChanged():', evt:evt, typeOfevt:typeof evt });
console.log(msg);
if (parseInt(evt) < 4) {
evt = '4';
}
this.compPropsVModelInputDaysAgo = evt;
this.$forceUpdate()
},
},
};
</script>
<style>
</style>
code:App.vue
<template>
<div id="app">
<div class="hello">
<GUIEpochAgo
v-model:vModelInputDaysAgo="vModelInputDaysAgo"
></GUIEpochAgo>
</div>
</div>
</template>
<script>
import GUIEpochAgo from './components/GUIEpochAgo.vue'
export default {
name: 'App',
data() {
return {
vModelInputDaysAgo: null,
};
},
components: {
GUIEpochAgo
},
watch: {
vModelInputDaysAgo(newVal, oldVal) {
if (isFinite(newVal)) {
const newValAsFloat = parseFloat(newVal);
if (newValAsFloat < 4) {
this.vModelInputDaysAgo = '4';
} else {
this.vModelInputDaysAgo = newVal;
}
} else {
this.vModelInputDaysAgo = 4;
}
},
},
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Two-way binding works in an optimized way that the question shows, it doesn't cover the case when a changed value is provided through a prop at the same tick when it was emitted. The debugging would show that input element receives a new prop but isn't updated because it wasn't marked as changed by the renderer.
It's reasonable for a component to be responsible for its own validation, so the value can be limited inside input component. This requires it to have internal state that is synchronized with parent state:
<input
type="input"
v-model="value"
:disabled="isInputDaysAgoDisabled"
/>
...
data() {
return {
value: null,
};
},
watch: {
value: {
immediate: true,
handler(newVal) {
this.compPropsVModelInputDaysAgo = this.value = Math.max(parseInt(newVal) || 0, 4);
}
},
compPropsVModelInputDaysAgo: {
immediate: true,
handler(newVal) {
this.value = Math.max(parseInt(newVal) || 0, 4);
}
},
}
That two watchers depend on each other requires to cover all cases that may lead to a recursion, like inconsistent '4'
string and 4
digit values.