One of the most helpful aspects for contemporary workplace apps is the creation of a browser-based workflow simulator. Custom workflows are frequently required in industries like manufacturing, aviation, banking, insurance, and IT operations. Before releasing the workflow to the real environment, users want to graphically build steps, connect nodes, establish rules, conduct simulations, and observe how the workflow works.

This article describes how to use Angular to create a Workflow Simulator that is suitable for production. The system facilitates:

The system supports:

  • Drag-and-drop node creation
  • Graph-based workflow builder
  • Branching paths and decision nodes
  • Node configuration panels
  • Execution simulation with event logs
  • Validation of loops, unreachable nodes, and broken paths
  • Pluggable rule engine
  • JSON-based storage and replay
  • Real-time visualisation of execution path

The goal is to build an Angular-based workflow editor that functions like lightweight versions of Camunda Modeler, Node-RED, or n8n, but fully customisable for your product.

Why Build a Workflow Simulator?
Typical enterprise systems contain long-running or multi-step business processes. Before executing workflows in production, developers and business users need a safe sandbox to test logic.

A browser-based simulator allows:

  • Quick iteration
  • Visual understanding of flows
  • Troubleshooting of decision logic
  • Validation before saving workflows
  • Replaying actual data
  • Training and demonstration


Most importantly, it reduces rework and production issues.

High-Level Features Required
A workflow simulator must include:

  • Node palette (Start, Task, Decision, API Call, Script, End).
  • Drag-and-drop canvas.
  • Connectors between nodes.
  • Node configuration (name, rules, conditions, inputs).
  • JSON export/import of the workflow.
  • Execution engine to simulate the workflow step-by-step.
  • Visual highlight of execution path.
  • Validation engine.
  • Undo/redo support.
  • Event logs and breakpoints.

This article focuses on the Angular implementation, core architecture, and execution simulation.

High-Level Architecture
The system is divided into:

  • Canvas Module
  • Nodes Module
  • Connection Engine
  • Workflow Engine (simulation)
  • Persistence Module
  • Validation Module
  • UI Components (palette, config panel)
Designing the Workflow Data Model
A workflow is a directed graph. Each node represents a step, and each edge represents the flow.

Node Structure
export interface WorkflowNode {
  id: string;
  type: 'start' | 'task' | 'decision' | 'api' | 'script' | 'end';
  name: string;
  position: { x: number; y: number };
  config?: any;
}
Edge Structure
export interface WorkflowEdge {
  id: string;
  from: string;
  to: string;
  condition?: string; // expression or rule
}
Workflow Definition
export interface WorkflowDefinition {
  nodes: WorkflowNode[];
  edges: WorkflowEdge[];
}

Building the Canvas in Angular

You will use these libraries/tools:
  • Angular CDK DragDrop Module
  • HTML canvas or SVG for rendering
  • RxJS for state management
  • A lightweight graph calculation library (optional)
Basic Canvas Component
@Component({
  selector: 'app-workflow-canvas',
  templateUrl: './workflow-canvas.component.html',
  styleUrls: ['./workflow-canvas.component.css']
})
export class WorkflowCanvasComponent {
  @Input() workflow!: WorkflowDefinition;

  onNodeDrag(event: CdkDragMove, node: WorkflowNode) {
    node.position.x = event.pointerPosition.x;
    node.position.y = event.pointerPosition.y;
  }
}

Canvas Template

<div class="canvas-area">
  <div *ngFor="let node of workflow.nodes"
       class="node"
       cdkDrag
       [style.left.px]="node.position.x"
       [style.top.px]="node.position.y">
       {{ node.name }}
  </div>

  <svg class="edge-layer">
    <ng-container *ngFor="let edge of workflow.edges">
      <line
        [attr.x1]="getNode(edge.from).position.x + 50"
        [attr.y1]="getNode(edge.from).position.y + 20"
        [attr.x2]="getNode(edge.to).position.x"
        [attr.y2]="getNode(edge.to).position.y"
        stroke="black"
        stroke-width="2" />
    </ng-container>
  </svg>
</div>

Implementing Drag-and-Drop Node Palette

A simple palette:
<div class="palette">
  <button (click)="addNode('task')">Add Task</button>
  <button (click)="addNode('decision')">Add Decision</button>
  <button (click)="addNode('api')">Add API Call</button>
</div>
Add Node Logic
addNode(type: string) {
  const newNode: WorkflowNode = {
    id: crypto.randomUUID(),
    type,
    name: `${type} node`,
    position: { x: 100, y: 100 }
  };

  this.workflow.nodes.push(newNode);
}

Adding Connectors Between Nodes

Common approach:
  • Click first node.
  • Click second node.
  • Create edge.
Add Edge Logic
addEdge(fromId: string, toId: string) {
  this.workflow.edges.push({
    id: crypto.randomUUID(),
    from: fromId,
    to: toId
  });
}

Adding Node Configuration Panel

This allows editing:
  • Name
  • Inputs
  • Conditions
  • Output fields
  • API parameters
  • Decision expressions
Sample Configuration Panel
<div class="config-panel" *ngIf="selectedNode">
  <h3>Node Configuration</h3>
  <label>Name:
    <input [(ngModel)]="selectedNode.name" />
  </label>

  <label *ngIf="selectedNode.type === 'decision'">
    Condition:
    <input [(ngModel)]="selectedNode.config.condition" />
  </label>
</div>

The Workflow Execution Engine

The Workflow Engine runs the workflow.
This includes:
  • Loading workflow JSON
  • Finding the start node
  • Executing each node
  • Evaluating rules
  • Logging events
  • Highlighting visited nodes
Engine Structure
export class WorkflowEngine {
  constructor(private workflow: WorkflowDefinition) {}

  private logs: string[] = [];

  execute() {
    let currentNode = this.findStartNode();

    while (currentNode && currentNode.type !== 'end') {
      this.logs.push(`Executing: ${currentNode.name}`);
      currentNode = this.getNextNode(currentNode);
    }

    this.logs.push('Reached end node');
    return this.logs;
  }
}


Rule Evaluation for Decision Nodes
Rules may be:
  • JavaScript expressions
  • JSON-based rule definitions
  • Condition strings
Example Rule Evaluation
evaluateCondition(condition: string, context: any): boolean {
  const fn = new Function("context", `return ${condition}`);
  return fn(context);
}

For large enterprise systems, use a safe rule engine instead of dynamic functions.

Simulating Execution Step-by-Step

Support:
  • Automatic play
  • Manual step-by-step
  • Breakpoints
Step Mode Example
step() {
  if (!this.currentNode) {
    this.currentNode = this.findStartNode();
    return this.currentNode;
  }

  this.currentNode = this.getNextNode(this.currentNode);
  return this.currentNode;
}


Visualising Execution Path
When the simulator runs:
  • Highlight the active node
  • Highlight the traversed edges
  • Dim inactive nodes
Canvas Highlighting Example
.node.active {
  border: 2px solid blue;
}

.edge.active {
  stroke: blue;
}

You will toggle CSS classes based on execution logs.

Validations Before Simulation
Essential validations:
  • Workflow must contain exactly one start node.
  • All nodes must be reachable from start.
  • No cycles unless allowed.
  • Decision nodes must contain valid conditions.
  • All edges must reference valid node IDs.
Example Validation Function
validateWorkflow(workflow: WorkflowDefinition): string[] {
  const errors: string[] = [];

  const startNodes = workflow.nodes.filter(n => n.type === 'start');
  if (startNodes.length !== 1) {
    errors.push('Workflow must contain exactly one start node.');
  }

  // Additional validations…

  return errors;
}

JSON Export and Import
The workflow editor must support saving and loading.

Export
downloadJSON() {
  const json = JSON.stringify(this.workflow);
  // download logic
}
Import
uploadWorkflow(json: string) {
  this.workflow = JSON.parse(json);
}

Undo / Redo Support

Use an RxJS BehaviorSubject to push state snapshots.

Example
history: WorkflowDefinition[] = [];
historyIndex = -1;

pushState() {
  this.history.push(JSON.parse(JSON.stringify(this.workflow)));
  this.historyIndex++;
}

Undo
undo() {
  if (this.historyIndex > 0) {
    this.historyIndex--;
    this.workflow = JSON.parse(JSON.stringify(this.history[this.historyIndex]));
  }
}

Real-World Challenges and Best Practices

Performance drops when rendering more than 200 nodes.
Use canvas or WebGL for large workflows.

Users need zooming and panning.
Implement with CSS transforms.

Complex connectors require bezier curves.
Use SVG path elements.

Decision rules get messy.
Integrate a rules engine (Nools, JSON-rules-engine).

Autosaving is essential.
Persist state in localStorage every few seconds.

Multi-user collaboration.
Use WebSockets and operational transforms.

Prevent invalid node drops.
Add guards and context checks.

Production-Level Recommendations
Use a state management library (NgRx or Akita) for storing workflow state.

Use RxJS Subjects for canvas events.

Debounce drag events to reduce change detection load.

Generate node IDs using crypto APIs for uniqueness.

Allow theming and custom node templates.

Include snap-to-grid behaviour for cleaner diagrams.

Create extension hooks for future custom nodes.

Conclusion

A browser-based workflow simulator is a powerful and reusable tool for any enterprise Angular application. By combining a graph-based canvas, configurable nodes, a robust execution engine, and a validation system, you can build a complete workflow modelling and testing system inside the browser. What makes this solution truly valuable is its flexibility. It doesn’t depend on any external engine and can be adapted for HR approvals, manufacturing processes, IT automation, financial rules, document processing, and more.