Skip to content

fix(Field): sync input value when dynamically toggling input slot#13783

Open
lllomh wants to merge 65 commits intoyouzan:mainfrom
lllomh:fix/field-input-slot-echo
Open

fix(Field): sync input value when dynamically toggling input slot#13783
lllomh wants to merge 65 commits intoyouzan:mainfrom
lllomh:fix/field-input-slot-echo

Conversation

@lllomh
Copy link
Contributor

@lllomh lllomh commented Feb 18, 2026

Description

When dynamically controlling whether to use the input slot on the Field component,
the input value doesn't display correctly when switching back to the native input element.

Root Cause

When switching from a custom input slot to the native HTML input element:

  • The newly created input DOM element is not synced with the current modelValue
  • The updateValue() method is only called when modelValue changes, on user input, or on mount
  • If the modelValue hasn't changed while the slot was active, the new element has an empty value

Solution

Added a watcher on inputRef that syncs the value whenever a new input element is created:

watch(
  () => inputRef.value,
  (input) => {
    if (input && input.value !== getModelValue()) {
      input.value = getModelValue();
    }
  },
);

Changes

  • Modified packages/vant/src/field/Field.tsx to add the watcher
  • Added test case in packages/vant/src/field/test/index.spec.js to verify dynamic slot toggling

Related Issue

Closes #13769

…t with the Chinese version under the English version
…y large, the component may return a value outside the max range.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
…y large, the component may return a value outside the max range test.
@gemini-code-assist
Copy link

Summary of Changes

Hello @lllomh, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses two distinct issues within the Vant UI library. It resolves a bug in the Field component where the input value would not correctly synchronize when dynamically toggling between a custom input slot and the native HTML input element. Additionally, it refines the Slider component's value formatting to ensure accurate stepping and clamping, particularly when the calculated stepped value might otherwise exceed the maximum allowed value.

Highlights

  • Field Component Fix: Fixed the Field component's input value synchronization issue that occurred when dynamically toggling between a custom input slot and the native input element.
  • Slider Component Improvement: Improved the Slider component's value formatting logic to correctly handle scenarios where the calculated steppedValue might exceed the max prop, ensuring accurate clamping.
  • New Test Cases: Added new test cases for both Field and Slider components to validate the respective bug fixes and improvements.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • packages/vant/src/field/Field.tsx
    • Added a watch effect on inputRef to ensure the native input element's value is synchronized with modelValue when the element is recreated, specifically addressing dynamic slot toggling.
  • packages/vant/src/field/test/index.spec.js
    • Introduced a new test case that simulates dynamically toggling the input slot of a Field component and verifies that the modelValue is correctly reflected in the native input after switching back.
  • packages/vant/src/slider/Slider.tsx
    • Modified the format function to include logic that checks if the steppedValue exceeds max. If it does, it calculates whether max or the previous valid step is closer to the original value to determine the final stepped value.
  • packages/vant/src/slider/test/index.spec.ts
    • Added two new test cases to validate the Slider component's format function, specifically testing scenarios where steppedValue might exceed max and ensuring the correct value (either max or the closest valid step) is returned.
Activity
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request addresses an issue where the input value in the Field component doesn't sync correctly when dynamically toggling the input slot. The solution involves adding a watcher to inputRef to ensure the value is updated when the input element is recreated. Additionally, there are changes in the Slider component to refine value formatting, specifically around handling values that exceed the maximum boundary when stepping. New test cases have been added for both Field and Slider components to cover these fixes.

Comment on lines +689 to +698
// Sync value when input element is recreated (e.g., when toggling custom input slot)
// https://github.com/youzan/vant/issues/13769
watch(
() => inputRef.value,
(input) => {
if (input && input.value !== getModelValue()) {
input.value = getModelValue();
}
},
);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The new watcher correctly addresses the issue of syncing the input value when the input element is recreated due to dynamic slot toggling. This is a good solution to ensure data consistency.

Comment on lines +667 to +706
test('should sync value when dynamically toggling input slot', async () => {
const wrapper = mount({
data() {
return {
useSlot: false,
modelValue: 'hello',
};
},
render() {
return (
<Field
v-model={this.modelValue}
v-slots={{
input: this.useSlot ? () => 'Custom Input' : undefined,
}}
/>
);
},
});

let input = wrapper.find('input');
expect(input.element.value).toEqual('hello');

// Toggle to use custom input slot
await wrapper.vm.$data.useSlot = true;
await wrapper.vm.$nextTick();
expect(wrapper.find('.van-field__control').text()).toContain('Custom Input');

// Update model value while slot is active
await wrapper.vm.$data.modelValue = 'world';
await wrapper.vm.$nextTick();

// Toggle back to native input
await wrapper.vm.$data.useSlot = false;
await wrapper.vm.$nextTick();

// The native input should have the updated value
input = wrapper.find('input');
expect(input.element.value).toEqual('world');
});

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This new test case effectively covers the scenario of dynamically toggling the input slot and verifies that the value is correctly synced. It's well-structured and clearly demonstrates the bug fix.

Comment on lines +132 to +143

const diff = Math.round((value - min) / step) * step;
return addNumber(min, diff);
const steppedValue = addNumber(min, diff);

if (steppedValue > max) {
const prevSteppedValue = addNumber(min, diff - step);
const distanceToMax = Math.abs(value - max);
const distanceToPrev = Math.abs(value - prevSteppedValue);

return distanceToMax <= distanceToPrev ? max : prevSteppedValue;
}
return steppedValue;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for handling steppedValue > max is a good addition to ensure the slider value correctly snaps to max or the previous stepped value when the calculated stepped value exceeds the maximum. This improves the precision and reliability of the slider's value calculation.

Comment on lines +378 to +388
//https://github.com/youzan/vant/issues/13625
test('should return max when distanceToMax <= distanceToPrev', () => {
const wrapper = mount(Slider, {
props: { min: 0, max: 50, step: 60, modelValue: 45 },
});

const emitted = wrapper.emitted('update:modelValue');
if (emitted && emitted.length > 0) {
const result = emitted[emitted.length - 1][0] as number;
expect(result).toBe(50); // Should return max
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This test case specifically targets the edge scenario where distanceToMax <= distanceToPrev, ensuring the slider correctly returns the max value. This is a good addition for robustness.

Comment on lines +391 to +403
test('should enter steppedValue > max branch', () => {
const wrapper = mount(Slider, {
props: { min: 0, max: 12, step: 20, modelValue: 0 },
});

wrapper.setProps({ modelValue: 11 });

const emitted = wrapper.emitted('update:modelValue');
if (emitted && emitted.length > 0) {
const result = emitted[emitted.length - 1][0] as number;
expect(result).toBeGreaterThanOrEqual(0);
expect(result).toBeLessThanOrEqual(12);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This test case verifies the behavior when steppedValue exceeds max, ensuring the value is correctly clamped within the min and max range. This helps confirm the new logic in format function works as expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug Report] 动态设置 Field 组件的 input 插槽时,输入框的回显有问题

1 participant

Comments