FormulaBuilderComponent Extraction
Project: ifile-teapot-web-collect (web-dynaforms) Date: 2026-02-20 → 2026-02-23 Status: Complete ✅
Overview
Extracted the formula builder from a monolithic ng-template + ~300 lines of scattered methods inside RightPanelStaticComponent into a standalone, reusable FormulaBuilderComponent. The builder can now be dropped into any context — main tab (legacy mode), formula library editor, and future error tab validation.
Why
right-panel-static.component.tswas ~1700+ lines with formula UI logic mixed into canvas/library/rule management code- The formula builder needed to work in 3+ contexts (main tab, library editor, error tab) — duplicating templates via
ng-templatewas fragile - Error handling was tangled between parent and child concerns
Files
| File | Status | Purpose |
|---|---|---|
| formula-builder.component.ts | NEW | Component logic |
| formula-builder.component.html | NEW | Template (84 lines) |
| formula-builder.component.scss | NEW | Styles |
| right-panel-static.component.ts | MODIFIED | Dead code removed |
| right-panel-static.component.html | MODIFIED | Template uses <app-formula-builder> |
Component API
// INPUTS
@Input() availableFields: { label: string; value: string }[] = [];
@Input() set formulaValue(value: string) { ... } // jsonKey format
@Input() errorMessage: string = ''; // field-level error text
// OUTPUTS
@Output() formulaChange = new EventEmitter<string>(); // jsonKey on success
@Output() validationResult = new EventEmitter<{ valid, message }>();
Template Usage
<!-- Main Tab (legacy) -->
<app-formula-builder
[availableFields]="formulaFieldSuggestions"
[formulaValue]="selectedItem?.formula || ''"
[errorMessage]="selectedItem?.__formulaError || ''"
(formulaChange)="onFormulaBuilderChange($event)"
></app-formula-builder>
<!-- Formula Editor Tab (library) -->
<app-formula-builder
[availableFields]="formulaFieldSuggestions"
[formulaValue]="editingFormula?.formula || ''"
[errorMessage]="selectedItem?.__formulaError || ''"
(formulaChange)="onFormulaBuilderChange($event)"
></app-formula-builder>
<!-- Error Tab (future) — same pattern -->
<app-formula-builder
[availableFields]="..."
[formulaValue]="item.formula || ''"
(formulaChange)="..."
></app-formula-builder>
What Moved Into the Builder
Properties
| Property | Purpose |
|---|---|
formulaInput | Display-format formula (@Label mentions) bound to <input> |
showFormulaSuggestions | Controls suggestion dropdown visibility |
filteredFormulaSuggestions | Filtered field list for current @ query |
selectedFormulaSuggestionIndex | Keyboard-navigated selection index |
formulaMessage | Validation result text (green/red) |
isFormulaValid | Validation state |
operators | Math operator chip definitions (from config) |
Methods
| Method | Purpose |
|---|---|
onFormulaInput() | Typing handler — clears validation, manages @ suggestions |
onFormulaKeyDown() | Keyboard nav (arrows, Enter, Escape, Tab, Backspace) |
onFormulaBlur() | Hides suggestion dropdown |
selectSuggestion() | Inserts selected @ mention |
addToOperatorFormula() | Inserts operator at cursor with smart spacing |
toValueFormula() | @Label → jsonKey (for saving) |
toLabelFormula() | jsonKey → @Label (for display) |
escapeRegex() | Utility: escapes special regex chars |
scrollSelectedSuggestionIntoView() | Scrolls active suggestion into viewport |
onValidateFormula() | Calls validate API → sets message → emits on success |
What Stays in RightPanelStaticComponent
| Item | Why |
|---|---|
formulaFieldSuggestions | Needs canvasElements |
onFormulaBuilderChange() | Saves to selectedItem/editingFormula + clears __isFormulaError |
Library CRUD (createNewFormula, editFormulaInLibrary, saveFormulaFromEditor, etc.) | Manages selectedItem.formulaLibrary |
| Rule management | Business logic tied to canvas state |
toLabelFormula() / getFormulaDisplayLabel() | For library list view display |
updateAllFormulas() | jsonKey rename propagation across canvas |
Dead Code Removed
Removed from right-panel-static | Reason |
|---|---|
formulaInput property | Builder manages its own display |
formulaMessage property | Builder manages its own validation messages |
isFormulaValid property | Builder manages its own validation state |
editingFormulaInput | Already dead |
operators + OPERATORS import | Builder imports its own |
onFormulaValidationResult() | Validation display is fully inside builder |
toValueFormula() | Builder has its own; right-panel never needed it |
(validationResult) template bindings | No longer consumed by parent |
formulaMessage = '' stale writes | Dead references in createNewFormula, editFormulaInLibrary, cancelFormulaEdit |
Standalone <small> error in template | Error message now inside builder via errorMessage |
Two Error Types
1. "Formula not configured" (field-level, external)
- Set by:
dynaform.service.tswhen a formula field has noformulaproperty (legacy) or no default formula (rules mode) - Passed via:
[errorMessage]="selectedItem?.__formulaError || ''" - Displayed by: Builder — red text inside the component + red border on input
- Cleared by:
onFormulaBuilderChange()callsstate.setFormulaError(item, false)on successful validation
2. Formula validation result (internal)
- Set by:
FormulaBuilderComponent.onValidateFormula()after API call - Displayed by: Builder — green ("Formula is valid") or red ("Invalid formula") text
- Cleared by:
onFormulaInput()when user types, or field switch via setter
Both errors trigger the same red border: errorMessage || (!isFormulaValid && formulaMessage)
Problems Faced & Resolutions
Problem 1: Validation message persisting across fields
Symptom: Click field "formula1" → validate invalid → shows red error. Click field "formula2" → the red error from "formula1" still shows.
Root cause: After extraction, formulaMessage and isFormulaValid are internal to the builder. Previously, the right-panel cleared them in its selectedItem$ subscription when switching items. After extraction, no one was resetting them.
Fix: Reset formulaMessage and isFormulaValid in the formulaValue input setter when the value changes (field switch).
Problem 2: "Formula is valid" message disappears on first click
Symptom: Type formula → click Validate → API succeeds → green "Formula is valid" flashes and immediately disappears. Click Validate again on the same input → message stays.
Root cause: Validation success flow:
formulaMessage = "Formula is valid"✅formulaChange.emit(formula)→ parent savesselectedItem.formula = formula[formulaValue]binding updates (was'', now'field_1 + field_2')- Setter fires →
prev !== value→ resetsformulaMessage❌
Second click works because value is already saved → setter sees same value → no reset.
Fix: Added lastEmittedFormula tracking:
// In onValidateFormula, before emit:
this.lastEmittedFormula = formula;
this.formulaChange.emit(formula);
// In setter:
if (value === this.lastEmittedFormula) {
this.lastEmittedFormula = null;
return; // Skip reset — this is our own value bouncing back
}
Problem 3: hasError vs errorMessage redundancy
Symptom: Two inputs controlling the same visual (red border): hasError: boolean and errorMessage: string. hasError was always set alongside errorMessage, making it redundant.
Fix: Removed hasError entirely. Red border condition uses errorMessage || (!isFormulaValid && formulaMessage) — the presence of errorMessage text is sufficient.
Problem 4: ngOnChanges firing for all inputs
Symptom: ngOnChanges fires whenever any input changes (availableFields, errorMessage, formulaValue), requiring manual filtering via changes['formulaValue']. Not scalable or type-safe.
Fix: Replaced with @Input() set formulaValue() getter/setter pattern:
- Only fires for
formulaValuechanges - No
SimpleChangesimport needed - No
firstChangeguard needed - Handles initial bind + subsequent changes in one place
- Removes
ngOnInitentirely
Problem 5: Library editor missing error state
Symptom: "Formula not configured" error only showed in the main tab builder. The library editor builder didn't show the error, even though the field is still invalid regardless of which tab you're editing in.
Fix: Both builder instances now receive [errorMessage]="selectedItem?.__formulaError || ''".
Communication Flow
Verification
- ✅ Build passes (exit code 0)
- ✅ Validation messages show correctly on first click for both valid and invalid formulas
- ✅ Messages reset when switching between formula fields
- ✅ "Formula not configured" error displays in both main tab and library editor
- ✅ Red border appears for both error types, clears appropriately
- ✅ No dead code remains in
right-panel-staticrelated to formula builder UI