Skip to content

Commit b396761

Browse files
[25.1] Use focus/blur to prevent prop updates from changing FormText values while typing
Fixes #21205
1 parent b803f73 commit b396761

File tree

1 file changed

+57
-11
lines changed

1 file changed

+57
-11
lines changed

client/src/components/Form/Elements/FormText.vue

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
:class="['ui-text-area', cls]"
99
:readonly="readonly"
1010
:placeholder="placeholder"
11-
:style="style" />
11+
:style="style"
12+
@focus="onFocus"
13+
@blur="onBlur" />
1214
<b-form-input
1315
v-else
1416
:id="id"
@@ -19,7 +21,9 @@
1921
:state="showState ? (!currentValue ? (optional ? null : false) : true) : null"
2022
:style="style"
2123
:type="acceptedTypes"
22-
:list="`${id}-datalist`" />
24+
:list="`${id}-datalist`"
25+
@focus="onFocus"
26+
@blur="onBlur" />
2327
<datalist v-if="datalist && !inputArea" :id="`${id}-datalist`">
2428
<option v-for="data in datalist" :key="data.value" :label="data.label" :value="data.value" />
2529
</datalist>
@@ -83,25 +87,28 @@ export default {
8387
default: null,
8488
},
8589
},
90+
data() {
91+
return {
92+
isFocused: false,
93+
localValue: "",
94+
};
95+
},
8696
computed: {
8797
acceptedTypes() {
8898
return ["text", "password"].includes(this.type) ? this.type : "text";
8999
},
90100
currentValue: {
91101
get() {
92-
const v = this.value ?? "";
93-
if (Array.isArray(v)) {
94-
if (v.length === 0) {
95-
return "";
96-
}
97-
return this.multiple
98-
? this.value.reduce((str_value, v) => str_value + String(v) + "\n", "")
99-
: String(this.value[0]);
102+
// If focused, return local value to prevent external updates from resetting user input
103+
if (this.isFocused) {
104+
return this.localValue;
100105
}
101-
return String(v);
106+
return this.valueToString(this.value);
102107
},
103108
set(newVal, oldVal) {
104109
if (newVal !== oldVal) {
110+
// Store the local value while editing
111+
this.localValue = newVal;
105112
this.$emit("input", newVal);
106113
}
107114
},
@@ -118,6 +125,45 @@ export default {
118125
: null;
119126
},
120127
},
128+
watch: {
129+
value(newValue) {
130+
// Only update localValue from prop when not focused
131+
if (!this.isFocused) {
132+
this.localValue = this.valueToString(newValue);
133+
}
134+
},
135+
},
136+
mounted() {
137+
// Initialize localValue with the initial prop value
138+
this.localValue = this.valueToString(this.value);
139+
},
140+
methods: {
141+
/**
142+
* Converts the `FormText` value to a string representation.
143+
* Handles arrays for multiple inputs and single values.
144+
*/
145+
valueToString(v) {
146+
const val = v ?? "";
147+
if (Array.isArray(val)) {
148+
if (val.length === 0) {
149+
return "";
150+
}
151+
return this.multiple
152+
? val.reduce((str_value, item) => str_value + String(item) + "\n", "")
153+
: String(val[0]);
154+
}
155+
return String(val);
156+
},
157+
onFocus() {
158+
// Set focus state to prevent external updates
159+
this.isFocused = true;
160+
},
161+
onBlur() {
162+
// When field loses focus, sync with the latest prop value
163+
this.isFocused = false;
164+
this.localValue = this.valueToString(this.value);
165+
},
166+
},
121167
};
122168
</script>
123169
<style scoped>

0 commit comments

Comments
 (0)