Skip to content

customfields: leave never-set numeric fields unset instead of NaN, clear reused widgets - #269

Open
asig2016 wants to merge 2 commits into
EGroupware:masterfrom
asig2016:fix/customfields-null-numeric-value
Open

customfields: leave never-set numeric fields unset instead of NaN, clear reused widgets#269
asig2016 wants to merge 2 commits into
EGroupware:masterfrom
asig2016:fix/customfields-null-numeric-value

Conversation

@asig2016

@asig2016 asig2016 commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Problem

Float and integer custom fields without a stored value render the literal text "NaN" in edit forms, and saving the entry then fails the /^-?[0-9]*[,.]?[0-9]*$/ pattern validation — the record cannot be saved at all while such a field is visible.

Cause

et2_customfields_list.set_value() coerces missing (and falsy) stored values to null before pushing them into the per-field child widgets:

let value = _value[this.options.prefix + field_name] ? _value[this.options.prefix + field_name] : null;

For float/int custom fields the child is an et2-number, whose value setter passes the value through parseFloat. "" + null is "null", so it slips past the empty-value guard, and parseFloat(null) parses the string "null"NaN. With precision set, NaN.toFixed(precision) produces the string "NaN", which is written into the input.

Fix

Per review feedback: null is no longer pushed into the child widget at all — a pristine widget is left completely untouched, so "not yet set" stays unset instead of becoming "set as empty".

A plain skip is not quite enough, though: preview / CRM-style views reuse one widget instance across records via set_value(), and skipping there would leave the previous record's number displayed on a record where the field is unset. So null is skipped only while the widget currently shows nothing, and clears it otherwise.

The switch matches "number" as well as "float"/"int", because _setup_float/_setup_int rewrite field.type on the shared field object during row creation — "number" is the type set_value() actually sees at runtime.

The newer Et2Customfields web component is not affected: its _fieldValue() already maps missing/null values to "" via nullish coalescing.

Relation to the previous approach (#268)

#268 fixed the same symptom inside Et2Number itself, by normalizing null/undefined/NaN in its value setter. As @nathangray pointed out there, null has the special meaning "not set" for form submission (null values are omitted by etemplate2.getValues()), so changing how the shared widget handles null is riskier than it looks. This PR fixes the producer instead, so null never reaches the widget, and leaves Et2Number untouched. I'd suggest this one replaces #268.

Second commit: Et2Customfields legacy input API + label layout

Rounds out Et2Customfields as a drop-in for the legacy widget and fixes two layout problems that surface in narrow panes:

Legacy input API. App code that addresses the customfields widget as a single input (the way et2_customfields_list allowed) needs:

  • set_value()
  • getValue() — answers with the collected #name => value object only for the legacy DEFAULT_ID custom_fields and null otherwise, matching the legacy widget, so etemplate2 submission stays with the individual #name child widgets
  • snapshot-based isDirty()/resetDirty()resetDirty() snapshots only after the queued Lit re-render (and the child widgets) settle; a synchronous snapshot describes the previously shown fields and makes every set_value() look like unsaved changes

Layout. The label column was max-content, so one long label squeezes every value input to a sliver in a narrow pane; it is now fit-content(45%), making long labels wrap instead. And date/date-time field widgets kept their label attr, so they rendered the label a second time above the input — the container already renders the label column, the attr is now dropped (filters re-set it after the switch, they have no label column).

🤖 Generated with Claude Code

https://claude.ai/code/session_01MFMF3JZxodnWwbNqurKN7s

@asig2016 asig2016 closed this Aug 10, 2026
@asig2016 asig2016 reopened this Aug 10, 2026
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 10, 2026
…without a stored value

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Normalize null to "" for numeric fields at the producer. The switch matches
"number" as well, because _setup_float/_setup_int rewrite field.type on the
shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MFMF3JZxodnWwbNqurKN7s
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 10, 2026
…without a stored value

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Normalize null to "" for numeric fields at the producer. The switch matches
"number" as well, because _setup_float/_setup_int rewrite field.type on the
shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MFMF3JZxodnWwbNqurKN7s
@nathangray

Copy link
Copy Markdown
Contributor

The new Et2Customfields widget is a work-in-progress, and maybe should not be doing that since it takes your earlier approach of setting null -> "" and does it for any numeric customfield.
Maybe instead add a check on cf value===null and skip setting the value instead of setting it to "".
The difference is leaving them unset, as they were before, and setting them all to blank. It may look the same to the user, but my concern is the values change from "not yet set" to "set as empty"

asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 11, 2026
…without a stored value

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Normalize null to "" for numeric fields at the producer. The switch matches
"number" as well, because _setup_float/_setup_int rewrite field.type on the
shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MFMF3JZxodnWwbNqurKN7s
asig2016 and others added 2 commits August 11, 2026 10:01
…ear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review: don't turn "not yet set" into "set as empty" - a pristine
widget is left completely untouched on null. A plain skip is not enough
though: preview / CRM-style views reuse one widget instance across records
via set_value(), and skipping there would leave the previous record's
number displayed on a record where the field is unset. So null is skipped
only while the widget shows nothing, and clears it otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Replaces the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…abels

Legacy-widget drop-in gaps that app code still relies on when it addresses
the customfields widget as one input (the same way et2_customfields_list
behaved):
- set_value()
- getValue(), answering with the collected #name => value object only for
  the legacy DEFAULT_ID "custom_fields" and null otherwise, so etemplate2
  submission stays with the individual child widgets
- snapshot-based isDirty()/resetDirty(); resetDirty() snapshots only after
  the queued Lit re-render and the child widgets settle - a synchronous
  snapshot describes the previously shown fields and makes every
  set_value() look like unsaved changes

Layout: cap the label column at fit-content(45%) so a long label wraps
instead of squeezing the value input to nothing in narrow panes, and drop
the label attr from date/date-time field widgets - the container renders
the label column itself, so date fields showed their label twice.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MFMF3JZxodnWwbNqurKN7s
@asig2016
asig2016 force-pushed the fix/customfields-null-numeric-value branch from 538e22c to f44035d Compare August 11, 2026 07:01
@asig2016 asig2016 changed the title customfields: pass "" instead of null to numeric fields without a stored value customfields: leave never-set numeric fields unset instead of NaN, clear reused widgets Aug 11, 2026
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 11, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 11, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@nathangray

Copy link
Copy Markdown
Contributor

If you need these fixes now, I'd merge this without the recently added values chunk.
We'd rather spend time finishing the new customfields correctly, I have some plans for the new webComponent that are tough to convey, but I hope to get to customfields as soon as I'm through with the new nextmatch.

asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 11, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 11, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 12, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 12, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 19, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 581153c)
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 20, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 581153c)
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 21, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 581153c)
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 21, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 581153c)
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 22, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 581153c)
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 24, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 581153c)
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 25, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 581153c)
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 25, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 581153c)
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 26, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 581153c)
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 26, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 581153c)
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 27, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 581153c)
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 27, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 581153c)
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 28, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 581153c)
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 29, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 581153c)
asig2016 added a commit to asig2016/egroupware that referenced this pull request Aug 31, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 581153c)
asig2016 added a commit to asig2016/egroupware that referenced this pull request Sep 1, 2026
… clear reused widgets

et2_customfields_list.set_value() coerces missing (and falsy) stored values
to null before pushing them into the per-field child widgets. For float/int
custom fields the child is an et2-number, whose value setter runs the value
through parseFloat: parseFloat(null) parses the string "null" and yields
NaN, so the input displays the literal text "NaN" and the subsequent save
fails the pattern validation.

Per review on upstream PR EGroupware#269: don't turn "not yet set" into "set as
empty" - a pristine widget is left completely untouched on null. A plain
skip is not enough though: preview / CRM-style views reuse one widget
instance across records via set_value(), and skipping there would leave the
previous record's number displayed on a record where the field is unset.
So null is skipped only while the widget shows nothing, and clears it
otherwise.

The switch matches "number" as well, because _setup_float/_setup_int
rewrite field.type on the shared field object during row creation.

Upstream PR EGroupware#269, replacing the Et2Number-side approach of PR EGroupware#268.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 581153c)
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