MD.OFFICE
FAL

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.ts was ~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-template was fragile
  • Error handling was tangled between parent and child concerns

Files

FileStatusPurpose
formula-builder.component.tsNEWComponent logic
formula-builder.component.htmlNEWTemplate (84 lines)
formula-builder.component.scssNEWStyles
right-panel-static.component.tsMODIFIEDDead code removed
right-panel-static.component.htmlMODIFIEDTemplate 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

PropertyPurpose
formulaInputDisplay-format formula (@Label mentions) bound to <input>
showFormulaSuggestionsControls suggestion dropdown visibility
filteredFormulaSuggestionsFiltered field list for current @ query
selectedFormulaSuggestionIndexKeyboard-navigated selection index
formulaMessageValidation result text (green/red)
isFormulaValidValidation state
operatorsMath operator chip definitions (from config)

Methods

MethodPurpose
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()@LabeljsonKey (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

ItemWhy
formulaFieldSuggestionsNeeds canvasElements
onFormulaBuilderChange()Saves to selectedItem/editingFormula + clears __isFormulaError
Library CRUD (createNewFormula, editFormulaInLibrary, saveFormulaFromEditor, etc.)Manages selectedItem.formulaLibrary
Rule managementBusiness 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-staticReason
formulaInput propertyBuilder manages its own display
formulaMessage propertyBuilder manages its own validation messages
isFormulaValid propertyBuilder manages its own validation state
editingFormulaInputAlready dead
operators + OPERATORS importBuilder imports its own
onFormulaValidationResult()Validation display is fully inside builder
toValueFormula()Builder has its own; right-panel never needed it
(validationResult) template bindingsNo longer consumed by parent
formulaMessage = '' stale writesDead references in createNewFormula, editFormulaInLibrary, cancelFormulaEdit
Standalone <small> error in templateError message now inside builder via errorMessage

Two Error Types

1. "Formula not configured" (field-level, external)

  • Set by: dynaform.service.ts when a formula field has no formula property (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() calls state.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:

  1. formulaMessage = "Formula is valid"
  2. formulaChange.emit(formula) → parent saves selectedItem.formula = formula
  3. [formulaValue] binding updates (was '', now 'field_1 + field_2')
  4. Setter fires → prev !== value → resets formulaMessage

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 formulaValue changes
  • No SimpleChanges import needed
  • No firstChange guard needed
  • Handles initial bind + subsequent changes in one place
  • Removes ngOnInit entirely

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-static related to formula builder UI