enhancement
major
minor
major
minor
Found in an application (E-Home, 8.0.0-alpha10): expressions of a rule language are entered as text and should be stored in their canonical form (the application parses the text and prints it canonically). The forms article describes validation (constraints), but no hook that maps an entered value to the stored one (e.g. trim, canonicalize) in the React view layer; <value-listeners> appear to belong to the classic UI.
Workaround in E-Home: the text is stored as typed (after the constraint checked its syntax) and canonicalized only on export.
Wanted: a field-level (or model-attribute annotation) normalizer applied when the value is taken from the input, documented in view-layer/forms, or a statement which existing mechanism does this.
Solution
A model attribute gets the annotation <value-normalizer>, listing normalizers that the forms of the React view layer apply in order to every value entered for that attribute before it is written to the form's overlay (and hence stored on save):
<property name="code" type="tl.core:String"> <annotations> <value-normalizer> <trim/> <upper-case/> </value-normalizer> </annotations> </property>
- Built-in normalizers (com.top_logic.model.normalize, offered in the model editor): <trim/>, <collapse-whitespace/> (trim and inner white space runs to one space), <lower-case/>, <upper-case/>, <empty-to-null/> (an empty text is stored as no value). A value that is not a text is kept; the values of a multiple attribute are normalized one by one.
- <normalize-by-expression function="value -> obj -> ..."> (NormalizeByExpression, com.top_logic.model.search) is the TL-Script normalizer, the sibling of <constraint-by-expression>; obj is the form's overlay, so the function may read the other entered values.
- ValueNormalizer (com.top_logic.model.annotate.util, next to ConstraintCheck) is the algorithm interface for Java implementations: normalize(object, attribute, value); a text normalizer extends TextNormalizer. A value the normalizer cannot canonicalize is returned unchanged; the attribute's constraints report the problem.
- A <value-input> of a view declares the annotation in its own <annotations>; a script then receives null as object, since the input is bound to no attribute.
- The normalization sits in the field model (AbstractFieldModel.setValue, with the object and attribute supplied by AttributeFieldModel), so every input control reaches it, the cells of a composition table included. The field shows the normalized value once the input is left: a rewritten value is pushed to the input even when it equals the value the field held before. Only an entered value is normalized; a stored value is shown as it is.
- A field whose attribute has a normalizer sends its value on blur instead of while typing (FieldSpec.isSendValueOnBlur(), set by FieldControlService and honoured by the text and password inputs; the number input always sends on blur), so the rewrite does not interfere with typing.
- Documented in the forms article of the view layer (section "Normalizing an entered value"); the React demo shows it in the constraint test view (attribute normalizedText with <collapse-whitespace/><lower-case/> and a value input with a script normalizer).
Only the React view layer applies the annotation; the classic UI form factory does not. Writes from scripts or imports are not normalized.