/

Validation

Combine local node validity with Angular validators on the complete DfDataModel . Core maps validator node ids back to rendered nodes and applies the standard invalid wrapper state.

Local node stateForms and controls inside one custom node.
Graph validatorsPure Angular validators over nodes and connections.
Visual feedbackInvalid nodes are highlighted after the editor is touched.

Node-Level Validation

Override the protected invalidState getter to combine local UI validity with invalidSignal() supplied by graph validators. Core reads the result from the custom component and applies .df-invalid to the editor node wrapper only after the editor is touched. This applies to both local errors and graph errors; their validation state is still available before touch.

form-node.component.ts

    
      @Component({
  standalone: true,
  selector: 'app-form-node',
  imports: [ReactiveFormsModule],
  template: `
    <label>
      Task name
      <input [formControl]="name" />
    </label>
  `,
  host: {
    '(keydown.delete.stop)': '0',
    '(keydown.backspace.stop)': '0',
  },
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FormNodeComponent extends DrawFlowBaseNode {
  readonly name = new FormControl('', {
    nonNullable: true,
    validators: [Validators.required],
  });

  protected override get invalidState(): boolean {
    const localInvalid = this.name.touched && this.name.invalid;

    return this.invalidSignal() || localInvalid;
  }
}
    

When Errors Become Visible

The editor reads touched state from its bound Angular form control or the [touched] input. Initial errors stay unhighlighted. Moving focus between fields inside the editor does not touch the editor; leaving it or completing a canvas interaction does.

Call markAllAsTouched() on submit to reveal errors. Calling markAsUntouched() or reset() hides the highlights again without suppressing validation. Editors without a form binding can control feedback through [touched] explicitly.

Graph-Level Validation

Attach validators to the same non-nullable form control bound to ng-draw-flow . They receive the complete graph value and participate in normal Angular form status and errors.

editor.component.ts

    
      readonly form = new FormControl<DfDataModel>(initialModel, {
  nonNullable: true,
  validators: [
    dfCycleDetectionValidator(),
    dfIsolatedNodesValidator(),
  ],
});
    
ValidatorChecksNode ids
dfCycleDetectionValidator()Directed cycles in the connection structure.cycleNodes
dfIsolatedNodesValidator()Nodes absent from every source and target endpoint.isolatedNodes

Built-in Error Shapes

cycle-error.ts

    
      {
  hasCycle: boolean;
  cycleNodes: DfId[]; // array of nodeIds that form a cycle
}
    

isolated-nodes-error.ts

    
      {
  hasIsolatedNodes: boolean;
  isolatedNodes: DfId[] // array of nodeIds without connections
}
    

Custom Validators

Implement a standard Angular ValidatorFn and keep it pure. Return affected ids either as a top-level string array value or in a nested {nodeIds: string[]} object so the editor can highlight those nodes.

Do not mutate nodes or connections inside a validator. Return validation metadata and let application code decide how the graph should be repaired.

required-root.validator.ts

    
      export function requiredRootValidator(): ValidatorFn {
  return (control): ValidationErrors | null => {
    const model = control.value as DfDataModel;
    const roots = model.nodes.filter((node) => node.startNode === true);

    return roots.length === 1 ? null : {requiredRoot: {nodeIds: model.nodes.map(({id}) => id)}};
  };
}
    

The built-in cycle validator caches by connection structure. Isolated-node validation also reads the node list, so replacing graph arrays and objects remains the predictable update model.

Open the editor validation example