Skip to content

fix(useFormInput) fix debounce behavior on blur #2721

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 7, 2025

Conversation

tbl0605
Copy link
Contributor

@tbl0605 tbl0605 commented May 31, 2025

Describe the PR

Hi,
currently, the debouncing function only works partially. It takes into account the delay provided to update (at regular intervals) the model value of the field being edited, but it doesn't update the model immediately when the @blur event is generated.
If we use a high debouncing value (for example 2 seconds), the following problem is easily reproducible:
let's imagine a field that transforms characters to uppercase at the end of input (via the @blur event, when exiting the field).

<BFormInput
  v-model="msg"
  name="myInput"
  maxlength="20"
  :debounce="2000"
  @blur="msg = msg.toUpperCase()"
/>

If the (partial or complete) user input took less than 2 seconds at the time of exiting the field, only part of the field will be capitalized, and after (maximum) 2 seconds, the field's content will be updated/completed again and returned to lowercase (because the debouncing function completes its action after the field has already lost focus).

chrome-capture-2025-5-29
Overall, a field v-model value should be up to date when you leave a field being edited to make the following scenarios reliable:

  • field validators (like vee-validate) rely on valid/up-to-date model values when handling @blur events
  • a form should be able to be submitted immediately with "final" field model values
  • any other actions done combining @blur events and model values

The debounce feature is the last feature I really miss in bootstrap-vue-next (compared to bootstrap-vue). Until now, I was using a locally patched version of bootstrap-vue-next to implement it, and after a while, I thought it was time to share my patch upstream. ^^

Note that unfortunately, the useDebounceFn function in @vueuse/core does not currently support the cancel feature. However, its implementation was discussed in this pending pull request, no changes have been made since then in @vueuse/core. In the meantime, I had to duplicate the useDebounceFn function in the new src/utils/debounce.ts file, adding the missing cancel support.

Small replication

chrome-capture-2025-5-29

PR checklist

What kind of change does this PR introduce? (check at least one)

  • Bugfix 🐛 - fix(...)
  • Feature - feat(...)
  • ARIA accessibility - fix(...)
  • Documentation update - docs(...)
  • Other (please describe)

The PR fulfills these requirements:

  • Pull request title and all commits follow the Conventional Commits convention or has an override in this pull request body This is very important, as the CHANGELOG is generated from these messages, and determines the next version type. Pull requests that do not follow conventional commits or do not have an override will be denied

Summary by CodeRabbit

  • New Features

    • Improved debounce behavior for form inputs, allowing more precise control over immediate and delayed updates.
    • Added support for canceling pending updates when input fields lose focus, ensuring values update as expected.
  • Bug Fixes

    • Enhanced handling of invalid numeric input by automatically converting NaN values to zero.

Copy link

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

Copy link

coderabbitai bot commented May 31, 2025

Walkthrough

A local debounce utility was introduced and integrated into the form input composable, replacing the previous third-party debounce import. The form input logic was updated to support immediate and debounced model value updates, with enhanced control for synchronous updates and cancellation on blur events.

Changes

File(s) Change Summary
packages/bootstrap-vue-next/src/composables/useFormInput.ts Switched debounce import to local utility, enhanced updateModelValue signature, refined debounce and blur handling logic.
packages/bootstrap-vue-next/src/utils/debounce.ts Added new debounce utility implementing useDebounceFn with cancellation and promise support.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant FormInputComposable
    participant DebounceUtility

    User->>FormInputComposable: Input value
    FormInputComposable->>DebounceUtility: Debounce updateModelValue (if not immediate)
    DebounceUtility-->>FormInputComposable: Debounced model update

    User->>FormInputComposable: Blur event
    FormInputComposable->>DebounceUtility: Cancel pending debounce
    FormInputComposable->>FormInputComposable: Synchronously update model value
Loading

Poem

In the warren where code does bounce,
Debounce hops in with a local renounce.
On blur we cancel, on input we wait,
Synchronous updates—rabbits celebrate!
With timers and promises, all neat and tight,
Our forms now handle input just right.
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

pkg-pr-new bot commented May 31, 2025

bsvn-vite-ts

npm i https://pkg.pr.new/bootstrap-vue-next/bootstrap-vue-next@2721
npm i https://pkg.pr.new/bootstrap-vue-next/bootstrap-vue-next/@bootstrap-vue-next/nuxt@2721

commit: b850f0c

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f1905e8 and 6c54a77.

📒 Files selected for processing (2)
  • packages/bootstrap-vue-next/src/composables/useFormInput.ts (4 hunks)
  • packages/bootstrap-vue-next/src/utils/debounce.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/bootstrap-vue-next/src/composables/useFormInput.ts (1)
packages/bootstrap-vue-next/src/types/CommonTypes.ts (1)
  • Numberish (3-3)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (10)
packages/bootstrap-vue-next/src/utils/debounce.ts (4)

1-10: LGTM! Proper type imports and organization.

The imports are well-organized and properly typed using VueUse core types. This approach allows reusing established type definitions while implementing custom logic.


11-11: LGTM! Simple and effective no-op implementation.

The noop function is correctly implemented and will be useful for cleanup scenarios.


16-30: LGTM! Proper promise wrapper with cancellation support.

The createFilterWrapper function correctly wraps the filter to provide a promise-based interface while preserving the cancel method. The implementation properly handles the this context and argument passing.


103-109: LGTM! Well-documented export with proper typing.

The useDebounceFn function is properly typed and documented. The default values and return type correctly extend the base VueUse types with cancellation support.

packages/bootstrap-vue-next/src/composables/useFormInput.ts (6)

14-14: LGTM! Import cleanup maintains compatibility.

Good practice to keep the VueUse imports that are still needed while removing only useDebounceFn.


17-17: LGTM! Proper integration of local debounce utility.

The import correctly references the newly implemented local debounce utility.


31-31: LGTM! Improved number handling with nanToZero option.

Adding {nanToZero: true} ensures consistent behavior when dealing with invalid debounce values, preventing NaN from causing issues in timer logic.


50-57: LGTM! Well-implemented immediate update control.

The updateModelValue function enhancement properly supports both immediate and debounced updates. The logic clearly separates the two paths and maintains backward compatibility.


124-128: Excellent fix for the debounce blur issue!

This is the core fix that addresses the PR objective. The implementation correctly:

  1. Cancels any pending debounced updates before comparing and updating the model value
  2. Forces an immediate update on blur using the new immediate parameter
  3. Maintains the existing force update logic

This ensures that blur events immediately update the model value, preventing the inconsistency described in the PR where transformations (like uppercase conversion) would be partially applied and then reverted.


111-117:

✅ Verification successful

Verify the early return condition logic.

The early return condition has been expanded to include debounceNumber.value <= 0. This seems logical since no debouncing would be needed when the debounce value is zero or negative.


🏁 Script executed:

#!/bin/bash
# Description: Verify the interaction between lazy modifiers and debounce values
# to ensure the early return logic is correct

# Search for any existing tests or documentation about the combination of these conditions
rg -A 10 -B 5 "lazy.*debounce|debounce.*lazy" --type ts --type vue

# Look for any existing blur event test cases
rg -A 15 -B 5 "onBlur|blur.*event" --type ts --type vue

Length of output: 182


🏁 Script executed:

#!/bin/bash
# Correct RG invocation to include .ts and .vue files via globs

# Search for any existing tests or documentation about lazy + debounce interactions
rg -g '*.ts' -g '*.vue' -A 10 -B 5 "lazy.*debounce|debounce.*lazy"

# Search for any existing blur event test cases
rg -g '*.ts' -g '*.vue' -A 15 -B 5 "onBlur|blur.*event"

Length of output: 21658


OnBlur early-return logic is correct

The added debounceNumber.value <= 0 check aligns with the common-props documentation—debounce only applies when set to a positive value and is ignored otherwise. Combined with the existing lazy, lazyFormatter, and trim flags, this condition ensures that on-blur formatting or flush behavior only runs when needed. No changes required.

@VividLemon VividLemon merged commit 6cab846 into bootstrap-vue-next:main Jun 7, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants