Skip to content

Fix unchecked boolean checkboxes not saving as false#216

Merged
ChuckBuilds merged 3 commits intomainfrom
fix/boolean-checkbox-save
Jan 28, 2026
Merged

Fix unchecked boolean checkboxes not saving as false#216
ChuckBuilds merged 3 commits intomainfrom
fix/boolean-checkbox-save

Conversation

@ChuckBuilds
Copy link
Copy Markdown
Owner

@ChuckBuilds ChuckBuilds commented Jan 28, 2026

Summary

  • Fixes a bug where unchecked boolean checkboxes in plugin config forms would not persist their false state
  • HTML checkboxes don't submit values when unchecked, so the existing config value (true) persists when starting from existing config for partial form updates
  • Additionally, merge_with_defaults() fills in schema defaults for missing fields, causing booleans with "default": true to always re-enable

This directly fixes the reported issue where NFL/NBA leagues in the odds-ticker plugin could not be disabled via the UI, while NHL (which defaults to false) appeared to work.

Changes

  • Backend (api_v3.py): Add _set_missing_booleans_to_false() helper that walks the schema after form processing and sets any boolean field absent from form data to false
  • Backend (api_v3.py): Handle "on"/"off" strings in _parse_form_value_with_schema for backwards compatibility
  • Template (plugin_config.html): Add value="true" to boolean checkboxes so checked state sends "true" instead of "on"

Test plan

  • Enable odds-ticker plugin, verify NFL/NBA/MLB can be toggled on and off
  • Verify NHL and other leagues (default: false) still toggle correctly
  • Verify other plugins with boolean fields still save correctly
  • Verify array-of-objects with boolean columns (custom feeds) still work

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Unchecked checkboxes in plugin configuration forms now reliably reset to False.
    • Form parsing updated to consistently interpret checkbox values (e.g., checked/unchecked) as True/False.
    • Checkbox inputs now submit a stable true value when checked to avoid ambiguous form submissions.

✏️ Tip: You can customize this high-level summary in your review settings.

HTML checkboxes don't submit values when unchecked. The plugin config
save endpoint starts from existing config (for partial updates), so an
unchecked checkbox's old `true` value persists. Additionally,
merge_with_defaults fills in schema defaults for missing fields, causing
booleans with `"default": true` to always re-enable.

This affected the odds-ticker plugin where NFL/NBA leagues (default:
true) could not be disabled via the checkbox UI, while NHL (default:
false) appeared to work by coincidence.

Changes:
- Add _set_missing_booleans_to_false() that walks the schema after form
  processing and sets any boolean field absent from form data to false
- Add value="true" to boolean checkboxes so checked state sends "true"
  instead of "on" (proper boolean parsing)
- Handle "on"/"off" strings in _parse_form_value_with_schema for
  backwards compatibility with checkboxes lacking value="true"

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 28, 2026

Warning

Rate limit exceeded

@ChuckBuilds has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 11 minutes and 17 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

Adds logic to ensure unchecked boolean form fields become False in saved plugin configs, normalizes boolean-like form values ("on"/"off"/"true"/"false") to booleans, and updates the checkbox template to submit "true" when checked.

Changes

Cohort / File(s) Summary
Boolean form handling logic
web_interface/blueprints/api_v3.py
Added _set_missing_booleans_to_false(config, schema_props, form_keys, prefix='') to recurse schema and set unsubmitted boolean fields to False (skips top-level enabled). Updated _parse_form_value_with_schema to coerce "on"/"off" (and "true"/"false") to booleans. Integrated the helper into the plugin config save flow after array-default handling.
Template checkbox rendering
web_interface/templates/v3/partials/plugin_config.html
Modified Default checkbox input to include value="true" with a comment, so a checked box submits "true" instead of the browser default "on".

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hop through forms where checkboxes hide,
If you don't tick me, I turn to False with pride.
"True" now I shout when pressed in play,
No more mysterious "on" leading the way.
— a rabbit, nibbling at config hay

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix unchecked boolean checkboxes not saving as false' directly and clearly summarizes the main bug fix addressed in this pull request.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@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: 2

🤖 Fix all issues with AI agents
In `@web_interface/blueprints/api_v3.py`:
- Around line 3091-3095: The boolean coercion currently treats any "on"/"off"
strings as booleans; modify the block that checks stripped.lower() in
('true','on') and ('false','off') so that "on"/"off" are only coerced when the
field's schema type indicates a boolean (e.g., field_schema.get('type') ==
'boolean' or similar), while still allowing unconditional parsing of
"true"/"false"; in practice, keep the existing checks for "true"/"false" as-is
and wrap the "on"/"off" branches with a guard that inspects the schema/type for
the target field before returning True/False.
- Around line 3197-3231: The _set_missing_booleans_to_false function currently
only recurses object schema properties and misses boolean fields inside arrays
of objects; update it to also handle prop_type == 'array' where
prop_schema.get('items') is an object with 'properties' by filtering form_keys
for keys that belong to this array (keys that start with f"{full_path}.") and
then recurse into the items' properties (items['properties']) using those
filtered keys and the same full_path as prefix so missing boolean fields inside
array items get set to False via _set_nested_value just like nested objects.
🧹 Nitpick comments (1)
web_interface/templates/v3/partials/plugin_config.html (1)

754-759: No-schema boolean fallback path may not benefit from this fix.

This fallback path (for plugins without schema definitions) still lacks value="true" and cannot benefit from the backend's _set_missing_booleans_to_false() helper since there's no schema to walk. Unchecked checkboxes here would still not submit any value.

Consider adding value="true" for consistency, or using the hidden input fallback pattern (as seen in lines 402-407 and 469-475) to ensure the field is always submitted.

♻️ Option 1: Add value="true" (minimal change)
 {% if value is sameas true or value is sameas false %}
 <label class="flex items-center cursor-pointer">
-    <input type="checkbox" name="{{ key }}" {% if value %}checked{% endif %}
+    <input type="checkbox" name="{{ key }}" value="true" {% if value %}checked{% endif %}
            class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
     <span class="ml-2 text-sm text-gray-600">Enabled</span>
 </label>

- Only coerce "on"/"off" strings to booleans when the schema type is
  boolean; "true"/"false" remain unconditional
- Extend _set_missing_booleans_to_false to recurse into arrays of
  objects (e.g. custom_feeds.0.enabled) by discovering item indices
  from submitted form keys and recursing per-index

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown

@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

🤖 Fix all issues with AI agents
In `@web_interface/blueprints/api_v3.py`:
- Around line 3203-3262: The function _set_missing_booleans_to_false currently
sets missing booleans by calling _set_nested_value with dotted paths (e.g.
"feeds.custom_feeds.0.enabled"), but _set_nested_value only handles dicts and
will replace lists with {} thereby corrupting array-of-object configs; change
the logic to avoid calling _set_nested_value for paths that contain numeric
array indices and instead walk/ensure the corresponding nested structure
in-place: when recursing into an array item (inside
_set_missing_booleans_to_false where you compute item_prefix and indices),
locate the list at the parent key in the actual config, ensure the item at that
index exists and is a dict (create a dict but never replace the list), then set
the boolean directly on that item dict (e.g.
config['feeds']['custom_feeds'][0]['enabled'] = False); keep using
_set_nested_value only for pure-dict paths and preserve lists by descending into
them when handling array item indices.

_set_nested_value uses dict-style access for all path segments, which
corrupts lists when paths contain numeric array indices (e.g.
"feeds.custom_feeds.0.enabled").

Refactored _set_missing_booleans_to_false to:
- Accept an optional config_node parameter for direct array item access
- When inside an array item, set booleans directly on the item dict
- Navigate to array lists manually, preserving their list type
- Ensure array items exist as dicts before recursing

This prevents array-of-object configs (like custom_feeds) from being
converted to nested dicts with numeric string keys.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@ChuckBuilds ChuckBuilds merged commit 10d70d9 into main Jan 28, 2026
1 check passed
@ChuckBuilds ChuckBuilds deleted the fix/boolean-checkbox-save branch January 29, 2026 15:32
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.

1 participant