Skip to main content
iVentureTeam

boolean_radio

New in Odoo 19: boolean_radio shows a boolean as two radio buttons whose labels are full translatable sentences harvested from elements in the same view. Skip either option and the widget refuses to mount.

Siddharth JambukiyaSiddharth JambukiyaOdoo Techno-Functional Consultant
August 25, 2026Updated August 25, 20266 min read
Technical nameboolean_radio
Field typesboolean
Viewsform
Modulehr (Employees)
Used in core2 occurrences across 2 modules: hr_holidays, hr_attendance
VersionsOdoo 20.0, Odoo 19.0
No-code setupNo. Applied in view XML together with the two label elements
Alternativesradio, boolean, boolean_toggle

What the boolean radio field does

Some yes/no questions deserve full sentences. The accrual plan form asks whether accrual is based on worked time, and the honest answers are Yes, consider the worked hours, excluding any time off taken during that period and No, always consider the entire accrual period. A checkbox cannot carry that; two radio buttons with sentence-length labels can. That is boolean_radio.

Mechanically it subclasses the standard radio field and overrides the item list for booleans: two entries, true and false, both with empty labels. The labels arrive in a second step. The widget's two options name DOM element ids, and on mount it copies each element's innerText onto the matching radio label. Core supplies those elements as <template id="is_based_on_worked_time_yes">...</template> blocks sitting right next to the field in the form, which keeps the sentences inside the view where Odoo's translation machinery picks them up.

Selection updates write the real boolean: the override converts the clicked string back to true/false and updates the record. The base class's change handler has no boolean branch, so the inherited call after it is a harmless no-op.

What this means for your team

Configuration mistakes in HR are expensive: an accrual plan counting calendar days when the policy says worked days quietly misallocates leave for every employee on it. The difference between that and a correct setup is often nothing more than whether the form explained the choice. This widget exists to put the explanation inside the control, so the person configuring reads the consequence at the moment of choosing.

Use the same pattern on any consequential boolean in your own modules: approval required versus auto-approved, retroactive versus from-now-on, per-company versus global. The cost is a few lines of view XML; the payoff is one less paragraph in your onboarding doc and one less category of misconfiguration ticket.

Supported options in Odoo 19

Both options below are declared in the source and, unusually, both are mandatory: the component types them as required props, so a missing option does not degrade, it throws a props validation error and the field never renders. Verified in boolean_radio.js, Odoo 19.0.

OptionTypeWhat it does
yes_label_element_idstring (required)DOM id of the element whose innerText becomes the label of the True radio. Required prop: omitting it throws a props validation error and the field does not render.(since Odoo 19.0)
no_label_element_idstring (required)DOM id of the element labeling the False radio. Same contract: required, resolved once at mount, must be present in the DOM at that moment.(since Odoo 19.0)

The ids must exist in the DOM when the widget mounts. The lookup is a plain document.getElementById at mount time: an element inside a collapsed notebook page or a lazily rendered block resolves to nothing and crashes the mount. Core keeps the label templates adjacent to the field, inside the same visible container; do the same.

Working examples

The core pattern, from the accrual plan form

<field name="is_based_on_worked_time" widget="boolean_radio"
       options="{'yes_label_element_id': 'worked_yes', 'no_label_element_id': 'worked_no'}"/>
<template id="worked_yes">Yes, consider the worked hours, excluding time off taken in the period.</template>
<template id="worked_no">No, always consider the entire accrual period.</template>

The <template> elements render nothing themselves; they exist purely as text containers the widget reads by id.

Readonly gating still works

<field name="expected_hours_from_contract" widget="boolean_radio"
       readonly="base_off != 'quantity'"
       options="{'yes_label_element_id': 'ehc_yes', 'no_label_element_id': 'ehc_no'}"/>

The hr_attendance overtime rule form does exactly this: extractProps forwards the dynamic readonly, so conditional editability behaves like any field.

Global selectors, dropped options and a dead template

Three source-level details define this widget's limits in 19. First, the label transplant targets the radios through a document-wide query: document.querySelectorAll("[data-value='true']")[0]. The first boolean radio on the page wins, so two boolean_radio fields visible at once would write both label pairs onto the first widget's radios. Core never puts two on one screen; treat that as a constraint, not a coincidence.

Second, extractProps rebuilds the prop set from scratch and forwards only readonly plus the two ids. The base radio's horizontal option and its label and domain props are silently discarded, which is why the two radios always stack vertically no matter what the view asks. Third, a small fossil: the value getter's non-boolean branch returns the item list instead of a value, a copy-paste slip that is unreachable because the widget declares boolean as its only supported type, but a good reminder that this is a purpose-built component, not a general radio.

The template file ships one more oddity: a QWeb template named hr_holidays.BooleanRadio that replaces the boolean checkbox with a YES/NO select. No component references it, the class inherits the plain radio template instead, so it is dead markup in 19, and the development branch deletes the file.

Version compatibility

VersionStatusNotes
Odoo 20.0Partial / changedNot released. New first_element option, reversed default order, collision fix. See below.
Odoo 19.0VerifiedIntroduced here, in the hr module.
Odoo 18.0Not availableWidget does not exist.
Odoo 17.0Not availableWidget does not exist.
Odoo 16.0Not availableWidget does not exist.

Upgrade note. Nothing to migrate into 19: the widget is new. When 20 lands, re-check every usage's option set: without the new first_element option the radio order reverses to No first, which silently changes the visual default of existing forms.

What is changing in Odoo 20

Odoo 20 is expected at Odoo Experience in Brussels, 24 to 26 September 2026. We diffed this widget on the public development branch at the time of writing; the branch is unstable and can still change.

The rework is substantial. A third option appears, first_element, deciding which value renders first, and the default order flips: without it, No comes before Yes, the opposite of 19. The label transplant switches from the global attribute selector to per-instance element ids, fixing the two-widgets-per-page collision. The descriptor starts spreading the base radio's options and extractProps, so horizontal becomes functional. The dead hr_holidays.BooleanRadio template file is deleted, and the source quirk of typing first_element as Boolean with a capital B ships as written. We re-verify after release.

Common problems and fixes

SymptomCause and fix
Props validation error, field never rendersOne or both label options are missing; both are required props. Pass yes_label_element_id and no_label_element_id in the options dictionary.
Crash at mount referencing null innerTextThe referenced ids do not exist in the DOM when the widget mounts. Place the label elements next to the field, inside the same rendered container.
Radio labels are emptyThe label elements exist but hold no text, or the ids point at the wrong elements. Check the ids match and the template elements contain plain text.
Two boolean_radio fields show the same labelsThe 19 implementation targets radios with a global selector; the first widget on the page wins. Keep one boolean_radio per screen in 19; the development branch fixes the selector.
horizontal option is ignoredextractProps forwards only readonly and the two ids, dropping the base radio options. Accept vertical stacking in 19; horizontal starts working on the development branch.

Boolean radio field vs the alternatives

WidgetBest forKey difference
boolean_radioA consequential yes/no where each answer needs a full explanatory sentenceBoolean as two radios; labels copied at mount from view elements named by two required options
radioSelections and many2ones as radio buttons with native labelsLabels come from the field's own values; supports horizontal; no boolean support
booleanThe standard compact checkboxOne control, label to the side; no room for per-answer explanations
boolean_toggleA switch that saves immediatelyAutosaving toggle; visual state only, no explanatory text

Reach for boolean_radio only when the two answers need sentences. For a compact yes/no, the plain checkbox wins; for selections with three or more values, the standard radio widget already does labels natively, no transplant required.

Frequently asked questions

Why do the labels live in the view instead of the options?+
Translation. Text in view XML is extracted and translated like any view content, while option values are not. The widget copies the translated innerText at mount, so each language sees its own sentences.
What are those template elements in the core views?+
Inert text containers. <template id="..."> renders nothing visible, which makes it a convenient place to park the label sentences the widget reads by id.
Can I put two boolean_radio fields on the same form?+
Not safely in Odoo 19: the label transplant selects radios document-wide and both widgets would write onto the first one's labels. The development branch switches to per-instance ids, lifting the limit in 20.
Can the radios sit side by side instead of stacked?+
Not in 19: the widget's extractProps drops the base radio's horizontal option. On the development branch the descriptor forwards the base options and horizontal works.
Does clicking a radio save the record?+
It updates the field like any edit; saving follows the form's normal flow. The widget adds no autosave, unlike boolean_toggle.

HR settings your managers keep second-guessing?

Accrual plans, overtime rules and approval chains fail through unclear forms more than bad logic. Hire our Odoo developers to build self-explaining HR configuration screens, sentence-labeled choices included, that your team gets right the first time.

Book a free consultation

How this page was produced

Verified by reading boolean_radio.js and its template file in the Odoo 19.0 hr module, the base radio_field.js it extends, and both core usages with their label template elements in hr_holidays and hr_attendance. Absence in 16 through 18 was confirmed against those branches, and the Odoo 20 section reflects a line-by-line diff of the development branch. Spotted an error? Tell us and we will correct the page.