<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Siarhei Huzarevich</title>
    <description>The latest articles on DEV Community by Siarhei Huzarevich (@shuzarevich).</description>
    <link>https://dev.to/shuzarevich</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1784570%2F5ee18dad-731e-46d8-b6d5-4960299227ba.jpeg</url>
      <title>DEV Community: Siarhei Huzarevich</title>
      <link>https://dev.to/shuzarevich</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shuzarevich"/>
    <language>en</language>
    <item>
      <title>Call Center Flow Editor — now updated with Angular 20 &amp; Signals 🚀</title>
      <dc:creator>Siarhei Huzarevich</dc:creator>
      <pubDate>Wed, 03 Sep 2025 21:00:37 +0000</pubDate>
      <link>https://dev.to/shuzarevich/call-center-flow-editor-now-updated-with-angular-20-signals-56hh</link>
      <guid>https://dev.to/shuzarevich/call-center-flow-editor-now-updated-with-angular-20-signals-56hh</guid>
      <description>&lt;p&gt;About a year ago I shared a small side project: a call center flow editor built on top of &lt;a href="https://github.com/Foblex/f-flow" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The idea was simple — give users a way to design call flows visually by dragging nodes and connecting them, instead of writing configuration manually.&lt;/p&gt;

&lt;p&gt;Since then, Angular has evolved, and so has this project. I wanted to take advantage of Angular 20 and its new Signals API, so I decided to give the editor a proper refresh.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔥 What’s New
&lt;/h2&gt;

&lt;p&gt;Here’s what changed in this update:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Migrated the whole project to &lt;strong&gt;Angular 20&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Rewritten state management on &lt;strong&gt;Signals&lt;/strong&gt; → no external store, simpler and more reactive&lt;/li&gt;
&lt;li&gt;Added a &lt;strong&gt;Light/Dark theme switch&lt;/strong&gt; for a nicer editing experience&lt;/li&gt;
&lt;li&gt;Added &lt;strong&gt;Undo/Redo&lt;/strong&gt; (finally you can experiment without fear)&lt;/li&gt;
&lt;li&gt;Improved overall UX (zooming, dragging and reconnecting nodes feels smoother)&lt;/li&gt;
&lt;li&gt;Using &lt;strong&gt;Angular Material components&lt;/strong&gt; for the interface&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo &amp;amp; Source
&lt;/h2&gt;

&lt;p&gt;👉 &lt;a href="https://foblex.github.io/f-flow-example" rel="noopener noreferrer"&gt;Live Demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/Foblex/f-flow-example" rel="noopener noreferrer"&gt;Source Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⭐ &lt;a href="https://github.com/Foblex/f-flow" rel="noopener noreferrer"&gt;Library: Foblex Flow&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reflections
&lt;/h2&gt;

&lt;p&gt;Moving everything to &lt;strong&gt;Signals&lt;/strong&gt; simplified the state logic a lot. Undo/redo was much easier to wire up, and persisting state in localStorage became almost trivial.&lt;/p&gt;

&lt;p&gt;This was a good reminder that Angular’s ecosystem is evolving quickly — and Signals are already powerful enough to drive fairly complex interactive UIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;This project started as a small experiment, but it keeps evolving together with Angular. Signals made the code simpler and the editor itself more responsive.&lt;/p&gt;

&lt;p&gt;I’m planning to keep polishing it, so feedback is always welcome 🙌&lt;/p&gt;

&lt;p&gt;👉 Try the demo: &lt;a href="https://foblex.github.io/f-flow-example" rel="noopener noreferrer"&gt;https://foblex.github.io/f-flow-example&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if you find this project useful, consider leaving a ⭐ on &lt;a href="https://github.com/Foblex/f-flow" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; — it really helps!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Foblex Flow 17.7 — Smarter Grouping, Copy/Paste, and Undo/Redo in Angular</title>
      <dc:creator>Siarhei Huzarevich</dc:creator>
      <pubDate>Mon, 25 Aug 2025 10:43:01 +0000</pubDate>
      <link>https://dev.to/shuzarevich/foblex-flow-177-smarter-grouping-copypaste-and-undoredo-in-angular-4af2</link>
      <guid>https://dev.to/shuzarevich/foblex-flow-177-smarter-grouping-copypaste-and-undoredo-in-angular-4af2</guid>
      <description>&lt;p&gt;Node-based editors are becoming a standard for automation, AI workflows, and low-code platforms. With Foblex Flow, we bring this power natively to Angular.&lt;/p&gt;

&lt;p&gt;The new release, v17.7.0, introduces smarter grouping, copy/paste, and undo/redo — all features that make visual editors feel much closer to production-ready tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✨ Smarter Grouping
&lt;/h2&gt;

&lt;p&gt;Working with groups is now more powerful and intuitive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Auto-resize groups&lt;/strong&gt; with fAutoSizeToFitChildren.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-expand groups&lt;/strong&gt; dynamically with fAutoExpandOnChildHit when children no longer fit inside.&lt;/li&gt;
&lt;li&gt;Refactored and improved examples:&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flow.foblex.com/examples/grouping" rel="noopener noreferrer"&gt;Grouping Example&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qjkn0gj8g25e8xocjjb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qjkn0gj8g25e8xocjjb.gif" alt="Grouping Example GIF" width="760" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://flow.foblex.com/examples/drag-to-group" rel="noopener noreferrer"&gt;Drag-to-Group Example&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚠️ Breaking Change
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;CSS class .f-parent-for-drop → renamed to .f-grouping-over-boundary.&lt;/li&gt;
&lt;li&gt;Added .f-grouping-drop-active for valid grouping targets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 If you use custom CSS for grouping, make sure to update your styles!&lt;/p&gt;

&lt;h2&gt;
  
  
  📋 Copy / Paste Example
&lt;/h2&gt;

&lt;p&gt;We all use copy, cut, and paste every day. Now your node-based editors can too.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New &lt;a href="https://flow.foblex.com/examples/copy-paste" rel="noopener noreferrer"&gt;Copy-Paste Example&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Duplicate nodes (with their connections).&lt;/li&gt;
&lt;li&gt;Remove selected elements.&lt;/li&gt;
&lt;li&gt;Paste them back while maintaining consistent IDs and connections.&lt;/li&gt;
&lt;li&gt;This example shows how easy it is to implement clipboard functionality directly with Foblex Flow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⏪ Undo / Redo Example
&lt;/h2&gt;

&lt;p&gt;No editor feels complete without Undo/Redo. With v17.7, you now have a reference implementation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk1jzwqmzy4webybjuurj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk1jzwqmzy4webybjuurj.gif" alt="Undo Redo GIF" width="800" height="601"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New &lt;a href="https://flow.foblex.com/examples/undo-redo-v2" rel="noopener noreferrer"&gt;Undo-Redo V2 Example&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Powered by &lt;a href="https://www.npmjs.com/package/@foblex/mutator" rel="noopener noreferrer"&gt;@foblex/mutator&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Tracks state changes, snapshots, and history.&lt;/li&gt;
&lt;li&gt;Enables true &lt;strong&gt;time-travel editing&lt;/strong&gt; for your diagrams.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Undo/Redo creates a smoother and more interactive editing experience, and is often a must-have for production tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 Documentation Updates
&lt;/h2&gt;

&lt;p&gt;We’ve refreshed the docs to reflect these improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://flow.foblex.com/docs/f-node-directive" rel="noopener noreferrer"&gt;FNodeDirective&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flow.foblex.com/docs/f-flow-component" rel="noopener noreferrer"&gt;FFlowComponent&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flow.foblex.com/docs/f-canvas-component" rel="noopener noreferrer"&gt;FCanvasComponent&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;With these features, &lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt; becomes even more practical for real-world editors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Grouping&lt;/strong&gt; makes complex diagrams easier to manage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy/Paste&lt;/strong&gt; is expected by every user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Undo/Redo&lt;/strong&gt; provides the safety net people need when building flows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, they bring Angular-based visual editors closer to the standard set by professional design tools.&lt;/p&gt;

&lt;p&gt;🔗 Links&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub repo: &lt;a href="https://github.com/foblex/flow" rel="noopener noreferrer"&gt;https://github.com/foblex/flow&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Live examples: &lt;a href="https://flow.foblex.com/examples" rel="noopener noreferrer"&gt;https://flow.foblex.com/examples&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ❤️ Support the Project
&lt;/h2&gt;

&lt;p&gt;This release moves Foblex Flow another step closer to being a full-fledged framework for building low-code and AI editors in Angular.&lt;/p&gt;

&lt;p&gt;If you find it useful, please ⭐ the repo on GitHub — it’s the best way to support the project and help it grow.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>angular</category>
      <category>opensource</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Inside Foblex Flow — Part 2: Drag-and-Drop Architecture in Angular Without CDK</title>
      <dc:creator>Siarhei Huzarevich</dc:creator>
      <pubDate>Sat, 23 Aug 2025 12:27:27 +0000</pubDate>
      <link>https://dev.to/shuzarevich/inside-foblex-flow-part-2-drag-and-drop-architecture-in-angular-without-cdk-34fe</link>
      <guid>https://dev.to/shuzarevich/inside-foblex-flow-part-2-drag-and-drop-architecture-in-angular-without-cdk-34fe</guid>
      <description>&lt;p&gt;When we think about node-based editors, the first thing that stands out is the interaction with on-screen elements. Dragging nodes, connecting ports, zooming the canvas — that’s what brings the interface to life.&lt;/p&gt;

&lt;p&gt;Without a well-designed drag-and-drop system, the entire editor turns into a static image. That’s why, during the development of &lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt;, we decided to build our own engine for handling events and drag sessions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🛠 &lt;a href="https://github.com/Foblex/f-flow" rel="noopener noreferrer"&gt;Sources&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🎯 Why Build a Custom Engine
&lt;/h2&gt;

&lt;p&gt;At first glance, it might seem sufficient to use &lt;strong&gt;Angular CDK DragDrop&lt;/strong&gt;. But in practice, its limitations became clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Designed for simple scenarios&lt;/strong&gt;. CDK solves the “drag a card into a list” problem. But in a node-based editor, you need much more: resize, rotate, connections, multi-selection, canvas dragging.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lacks extensibility&lt;/strong&gt;. Adding new behaviors (like dropping into a group) is difficult — there’s no built-in plugin architecture.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;So we decided:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;👉 We need a custom engine where we have full control over:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;event handling,&lt;/li&gt;
&lt;li&gt;the lifecycle of a drag session,&lt;/li&gt;
&lt;li&gt;plugin-based extensibility,&lt;/li&gt;
&lt;li&gt;and a zoneless architecture for performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚡ Unified Event Interface
&lt;/h2&gt;

&lt;p&gt;One of our core principles is that — whether it’s a mouse or touch input — the event should look the same higher up the stack.&lt;/p&gt;

&lt;p&gt;To achieve this, we introduced the following interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export abstract class IPointerEvent {

  public get originalEvent(): MouseEvent | TouchEvent {
    return this.event;
  }

  public get targetElement(): HTMLElement {
    return this.target || this.originalEvent.target as HTMLElement;
  }

  protected constructor(
    private readonly event: MouseEvent | TouchEvent | PointerEvent,
    private target?: HTMLElement
  ) {}

  public setTarget(target: HTMLElement): void {
    this.target = target;
  }

  public abstract isMouseLeftButton(): boolean;

  public abstract isMouseRightButton(): boolean;

  public preventDefault(): void {
    this.originalEvent.preventDefault();
  }

  public abstract getPosition(): { x: number, y: number };

  public get isEventInLockedContext(): boolean {
    return this.targetElement.closest('[fLockedContext]') !== null;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, all logic in the editor operates with a unified event object.&lt;/p&gt;

&lt;p&gt;Advantages of this approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A consistent API across modules (move, resize, rotate);&lt;/li&gt;
&lt;li&gt;Simplified support for new input types and devices;&lt;/li&gt;
&lt;li&gt;Fewer bugs related to platform inconsistencies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzxlks12prnnqfa5mb9zh.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzxlks12prnnqfa5mb9zh.webp" alt="Unified Event Interface" width="800" height="552"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🧩 Three-Layer Architecture
&lt;/h2&gt;

&lt;p&gt;Drag-and-drop in &lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt; is structured in layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/Foblex/f-flow" rel="noopener noreferrer"&gt;DragAndDropBase&lt;/a&gt; — a low-level class. It subscribes to the document, listens for mousedown / touchstart / pointer events, checks the movement threshold, and initiates a drag session. It also handles screen reader protection and synthetic event filtering.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/Foblex/f-flow" rel="noopener noreferrer"&gt;FDraggableBase&lt;/a&gt; — an abstraction for Angular directives. It defines the contract: which events are available (fDragStarted, fDropToGroup, fCreateConnection, etc.) and which inputs are supported (fNodeMoveTrigger, fCellSizeWhileDragging).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/Foblex/f-flow" rel="noopener noreferrer"&gt;FDraggableDirective&lt;/a&gt; — the actual directive applied to . It connects the low-level drag engine to the Angular app, uses &lt;a href="https://github.com/siarheihuzarevich/f-mediator" rel="noopener noreferrer"&gt;FMediator&lt;/a&gt; to dispatch all *Request actions and emit corresponding *Event outputs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📌 This layered structure cleanly separates concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The base layer handles raw DOM interaction and events,&lt;/li&gt;
&lt;li&gt;The Angular layer exposes them as declarative inputs and outputs,&lt;/li&gt;
&lt;li&gt;The application receives well-structured events without worrying about platform-specific details.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🛠 Drag Session Lifecycle
&lt;/h2&gt;

&lt;p&gt;Every interaction in &lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt; follows a consistent scenario:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Initialization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The user presses the mouse button or touches the screen.&lt;/p&gt;

&lt;p&gt;The directive checks the configured triggers to determine whether starting a move, resize, or connection is allowed at that moment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start Threshold&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A movement threshold (3 pixels by default) is intentionally applied to avoid triggering a drag from small cursor shakes.&lt;/p&gt;

&lt;p&gt;Only when this threshold is exceeded does the drag session officially begin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Preparation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;prepareDragSequence is invoked. This phase is triggered within mousemove/touchmove/pointermove, but only before the drag session actually starts.&lt;/p&gt;

&lt;p&gt;Plugins like NodeMove, Resize, Rotate, CanvasMove, DropToGroup, and others evaluate whether they should participate. If so, they create their own motion handler.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Movement&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once at least one motion handler is registered, there’s no need to evaluate the rest — only one action is allowed per session (move, resize, or rotate).&lt;/p&gt;

&lt;p&gt;The mousemove/touchmove/pointermove logic switches to passing delta movement data to the active handler instead of preparing new ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Completion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On mouseup/touchup/pointerup, finalization handlers are called. The result is committed — a node is moved, a connection is created, or an item is dropped into a group.&lt;/p&gt;

&lt;p&gt;👉 Each drag session has clearly defined boundaries. This makes behavior predictable and debugging significantly easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔌 Plugin System
&lt;/h2&gt;

&lt;p&gt;Dragging isn’t just about “moving a node.” In real-world applications, the use cases are far more diverse:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;resizing elements,&lt;/li&gt;
&lt;li&gt;rotating,&lt;/li&gt;
&lt;li&gt;creating connections,&lt;/li&gt;
&lt;li&gt;reattaching connections,&lt;/li&gt;
&lt;li&gt;moving the canvas,&lt;/li&gt;
&lt;li&gt;dropping external items (e.g., from a palette).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of hardcoding all of this into the core, we built a before/after plugin architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private _beforePlugins!: QueryList&amp;lt;IFDragAndDropPlugin&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plugins can subscribe to lifecycle hooks like onPointerDown, prepareDragSequence, and onPointerUp.&lt;/p&gt;

&lt;p&gt;This allows extending the functionality without touching the core logic.&lt;/p&gt;

&lt;p&gt;📌 Example: you can add a hover highlight effect using a separate plugin — no need to modify the base code.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 Architectural Decisions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Zoneless Approach&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All drag events are handled inside ngZone.runOutsideAngular, which prevents unnecessary Angular change detection cycles.&lt;/p&gt;

&lt;p&gt;This results in high performance, even with hundreds of active nodes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;OS-aware Triggers&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On macOS, multi-selection uses metaKey; on Windows and Linux — ctrlKey.&lt;/p&gt;

&lt;p&gt;This behavior is built into the fMultiSelectTrigger directive and delivers familiar UX across all platforms — right out of the box.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SSR Compatibility&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We never access window or document directly.&lt;/p&gt;

&lt;p&gt;All platform APIs are accessed through BrowserService, which can be replaced with a mock on the server.&lt;/p&gt;

&lt;p&gt;This makes drag-and-drop work even in Angular Universal, without crashes or runtime errors.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Configurable Behavior&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Any drag-and-drop behavior can be finely tuned using triggers. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;f-flow fDraggable [fNodeMoveTrigger]="e =&amp;gt; e.shiftKey"&amp;gt;&amp;lt;/f-flow&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, nodes will only move when the Shift key is held.&lt;/p&gt;

&lt;p&gt;This gives developers full control over editor behavior.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Assistive Technology Protection&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ve implemented checks to prevent drag sessions from being triggered by screen readers or other assistive technologies.&lt;/p&gt;

&lt;p&gt;This is important for accessibility and ensures that interaction remains intentional.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhq80giou3ayir69e2eb1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhq80giou3ayir69e2eb1.webp" alt="Architectural Decisions" width="800" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🔍 UX Nuances
&lt;/h2&gt;

&lt;p&gt;We’ve paid close attention to subtle details that directly affect the user experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Synthetic Event Suppression&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After a touchstart, browsers often fire a fake mousedown.&lt;/p&gt;

&lt;p&gt;To avoid double triggers, we introduced a MOUSE_EVENT_IGNORE_TIME = 800ms threshold during which such events are ignored.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Text Selection Prevention&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;During a drag session, the selectstart event is suppressed.&lt;/p&gt;

&lt;p&gt;This prevents the user from accidentally selecting text while trying to drag an element — leading to a cleaner, more intuitive experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 Strengths of the Drag-and-Drop Architecture
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Runs outside the Angular zone&lt;/strong&gt;, ensuring high performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supports all input types&lt;/strong&gt; thanks to a unified event interface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Highly configurable&lt;/strong&gt; through triggers and plugin hooks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSR-safe&lt;/strong&gt;, with no direct reliance on browser globals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accounts for UX nuances&lt;/strong&gt;, such as filtering synthetic events and preventing accidental text selection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open for extension&lt;/strong&gt; — you can implement external drag sources, dynamic reconnections, grouping behavior, and more.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🚀 Conclusion
&lt;/h2&gt;

&lt;p&gt;Drag-and-drop in &lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt; is not just about moving elements around — it’s a carefully designed architecture that offers stability, flexibility, and scalability:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Normalized events eliminate inconsistencies between mouse, touch, and pointer input.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A well-defined drag session lifecycle makes behavior predictable and easy to debug.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The plugin system enables new features without altering the core logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SSR support and zoneless execution ensure compatibility and performance across environments.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This architecture forms a solid foundation for building not just a visual editor, but a complete platform — from simple node movements to complex scenarios like external drag sources, dynamic reconnection of links, and context-aware behaviors.&lt;/p&gt;

&lt;p&gt;👉 In the next part, we’ll dive into &lt;a href="https://github.com/siarheihuzarevich/f-mediator" rel="noopener noreferrer"&gt;FMediator&lt;/a&gt; — the foundation of the command architecture in &lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt;, enabling clean, modular, and scalable logic without bloating your components.&lt;/p&gt;

&lt;p&gt;You’ll learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How we built a &lt;strong&gt;Command Pipeline&lt;/strong&gt; with built-in validation and execution stages;&lt;/li&gt;
&lt;li&gt;How we eliminated &lt;strong&gt;cyclical dependencies&lt;/strong&gt; between layers using &lt;strong&gt;Dependency Inversion&lt;/strong&gt; and &lt;strong&gt;Single Responsibility&lt;/strong&gt; principles;&lt;/li&gt;
&lt;li&gt;How &lt;a href="https://github.com/siarheihuzarevich/f-mediator" rel="noopener noreferrer"&gt;FMediator&lt;/a&gt; separates &lt;strong&gt;intent (requests)&lt;/strong&gt; from &lt;strong&gt;behavior (handlers)&lt;/strong&gt;, applying &lt;strong&gt;CQRS&lt;/strong&gt;, &lt;strong&gt;GRASP&lt;/strong&gt;, and other proven architectural practices;&lt;/li&gt;
&lt;li&gt;Why we &lt;strong&gt;avoided heavy state management tools&lt;/strong&gt; like NgRx, Akita, or NGXS — and built a lightweight solution that doesn’t dictate state structure or bloat your app;&lt;/li&gt;
&lt;li&gt;How &lt;a href="https://github.com/siarheihuzarevich/f-mediator" rel="noopener noreferrer"&gt;FMediator&lt;/a&gt; became the architectural glue layer coordinating editor behavior — from drag-and-drop to user commands and business logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📦 If you appreciate &lt;strong&gt;SOLID&lt;/strong&gt; principles, want to build a &lt;strong&gt;scalable UI without unnecessary overhead&lt;/strong&gt;, and prefer an architecture where modules are loosely coupled — the next article is definitely for you.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>architecture</category>
      <category>performance</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Inside Foblex Flow — Part 1: Library Architecture and Design Principles</title>
      <dc:creator>Siarhei Huzarevich</dc:creator>
      <pubDate>Fri, 22 Aug 2025 14:37:28 +0000</pubDate>
      <link>https://dev.to/shuzarevich/inside-foblex-flow-part-1-library-architecture-and-design-principles-2gib</link>
      <guid>https://dev.to/shuzarevich/inside-foblex-flow-part-1-library-architecture-and-design-principles-2gib</guid>
      <description>&lt;p&gt;Node-based interfaces are everywhere today: low-code platforms, workflow editors, chatbot builders, business automation systems, ETL tools. Visual logic modeling allows users to “assemble a program” without code by dragging blocks and connecting them with arrows.&lt;/p&gt;

&lt;p&gt;But if you work with Angular, you’ll quickly face a problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;most popular solutions are React-only (e.g., React Flow),&lt;/li&gt;
&lt;li&gt;others are tightly coupled to their own data model,&lt;/li&gt;
&lt;li&gt;or they feel outdated and don’t fit into modern Angular projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s why we built &lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt; — a library that fills this gap.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Written entirely in Angular.&lt;/li&gt;
&lt;li&gt;Doesn’t dictate how to store your data.&lt;/li&gt;
&lt;li&gt;Provides a flexible interaction layer.&lt;/li&gt;
&lt;li&gt;Based on a clear, event-driven API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this series, we’ll look inside the library — how it works, which principles guide its design, and which architectural decisions make it lightweight and universal.&lt;/p&gt;

&lt;p&gt;We’ll start with the foundation: architecture and design principles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Foblex/f-flow" rel="noopener noreferrer"&gt;https://github.com/Foblex/f-flow&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 The Key Idea: Separation of Responsibilities
&lt;/h2&gt;

&lt;p&gt;Most node-based libraries give you everything “out of the box”: a data store, JSON graph, APIs for saving and loading. That’s convenient at first, but becomes a limitation once you try to integrate it into a real application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Foblex/f-flow" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt; takes a different approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👉 The library stores only what’s needed for interactivity and rendering,&lt;/li&gt;
&lt;li&gt;👉 Everything else is left to the user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What the library stores&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node positions (x, y)&lt;/strong&gt; — required for rendering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canvas scale and translate&lt;/strong&gt; — zoom and panning (defaults: scale = 1, translate = {0,0}).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drag session state&lt;/strong&gt; — current node coordinates, cursor offsets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Element selection (selected / unselected)&lt;/strong&gt; — for highlighting and frames.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal UI states&lt;/strong&gt; — e.g., snap highlight when connecting nodes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What the user stores&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Graph structure&lt;/strong&gt; — which nodes exist, their types and parameters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection model&lt;/strong&gt; — which links are allowed, validation rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node semantics&lt;/strong&gt; — what a “Webhook”, “AI Generator” or “Parser” node actually does.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistence&lt;/strong&gt; — saving/loading flows (NgRx, Signals, IndexedDB, Firebase, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visual styles&lt;/strong&gt; — how nodes and connectors look.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 In short: the library handles the UI layer, while your application owns the business logic.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  ⚡ Event-Driven Model
&lt;/h2&gt;

&lt;p&gt;The golden rule of &lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt;: the &lt;a href="https://github.com/Foblex/f-flow" rel="noopener noreferrer"&gt;library&lt;/a&gt; never mutates your data silently.&lt;/p&gt;

&lt;p&gt;Every user action is emitted as an Angular event:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;fNodeMoved&lt;/strong&gt; — a node was dragged.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;fNodeSelected&lt;/strong&gt; — a node was selected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;fConnectionCreated&lt;/strong&gt; — a new connection was made.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;fConnectionRemoved&lt;/strong&gt; — a connection was deleted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;fCanvasChanged&lt;/strong&gt;— the canvas was changed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means every action is a signal to your application:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This happened. You decide what to do.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That makes the &lt;a href="https://github.com/Foblex/f-flow" rel="noopener noreferrer"&gt;library&lt;/a&gt; predictable and easy to integrate with any state management — from Signals to NgRx.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧩 Architectural Layers
&lt;/h2&gt;

&lt;p&gt;We can break down &lt;a href="https://github.com/Foblex/f-flow" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt; into four layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FFlowComponent
 └── FCanvas
      ├── FNodeDirective
      │     ├── FNodeOutputDirective
      │     └── FNodeInputDirective
      │
      └── FConnectionComponent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Flow&lt;/strong&gt;&lt;br&gt;
The root component f-flow — coordinates context, events, and canvas state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Canvas&lt;/strong&gt;&lt;br&gt;
The workspace: holds all nodes and connections. Manages zoom, pan, and selections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nodes&lt;/strong&gt;&lt;br&gt;
Nodes are not predefined components, but directives you can attach to any Angular element.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;fNode&lt;/strong&gt; — turns an element into a node.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;fNodeOutput&lt;/strong&gt; — defines an output connector.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;fNodeInput&lt;/strong&gt; — defines an input connector.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Connections&lt;/strong&gt;&lt;br&gt;
An SVG path connecting an output to an input. Supports Bezier curves, straight lines, and arrows.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔧 Minimal Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;f-flow&amp;gt;
  &amp;lt;f-canvas&amp;gt;
    &amp;lt;!-- Node 1 --&amp;gt;
    &amp;lt;div fNode fNodeId="node1" [position]="{x: 100, y: 150}"&amp;gt;
      &amp;lt;div fNodeOutput fOutputId="out1"&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;!-- Node 2 --&amp;gt;
    &amp;lt;div fNode fNodeId="node2" [position]="{x: 400, y: 150}"&amp;gt;
      &amp;lt;div fNodeInput fInputId="in1"&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;!-- Connection --&amp;gt;
    &amp;lt;f-connection fOutputId="out1" fInputId="in1"&amp;gt;&amp;lt;/f-connection&amp;gt;
  &amp;lt;/f-canvas&amp;gt;
&amp;lt;/f-flow&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Positions (x, y) are mandatory — without them a node won’t render.&lt;/p&gt;

&lt;p&gt;But where and how you store them (Signals, NgRx, Firestore, a plain service) is entirely up to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠 Design Principles
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Minimal Magic&lt;/strong&gt;&lt;br&gt;
Instead of hiding complexity behind black boxes, the API is transparent: directives and events are explicit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. SSR and Zoneless Angular&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/Foblex/f-flow" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt; works outside the browser.&lt;/p&gt;

&lt;p&gt;All window, document, and localStorage references go through DI and can be mocked for SSR.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Performance&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No “global JSON graph” that’s recalculated on each change.&lt;/li&gt;
&lt;li&gt;Renders only changed elements.&lt;/li&gt;
&lt;li&gt;Drag-and-drop is optimized for large graphs (hundreds of nodes).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Flexibility&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store flows as JSON if you like.&lt;/li&gt;
&lt;li&gt;Sync with Firebase or GitHub.&lt;/li&gt;
&lt;li&gt;Save to IndexedDB.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The library never dictates your persistence strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  📐 Visual Diagram
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    subgraph "Library stores"
        A[Node positions] --&amp;gt; B[Scale/Translate]
        B --&amp;gt; C[Selections]
        C --&amp;gt; D[Drag sessions]
    end

    subgraph "Application stores"
        E[Graph structure]
        F[Connections]
        G[Node parameters]
        H[Persistence]
    end

    A -.-&amp;gt; E
    B -.-&amp;gt; F
    C -.-&amp;gt; G
    D -.-&amp;gt; H
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The diagram illustrates the boundary: &lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt; only handles UI state, while your app owns the graph model and business logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 Philosophy: UI Handles Interactivity, Not Data
&lt;/h2&gt;

&lt;p&gt;The core philosophy: &lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt; is not a “platform inside a platform.”&lt;/p&gt;

&lt;p&gt;It focuses on what it does best — interactive UI.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The library tells you what the user did.&lt;/li&gt;
&lt;li&gt;Your app decides what it means.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation makes the library universal. You can build anything — from workflow editors to no-code chatbot builders.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt; is not a black box — it’s a transparent tool for building node-based UIs in Angular.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It manages interactivity, not your data.&lt;/li&gt;
&lt;li&gt;Gives you full control over business logic.&lt;/li&gt;
&lt;li&gt;Fits projects of any scale, from simple diagrams to complex low-code platforms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the next part, we’ll dive into the &lt;strong&gt;drag-and-drop engine&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;unifying mouse, touch, and pointer events,&lt;/li&gt;
&lt;li&gt;the internal IPointerEvent structure,&lt;/li&gt;
&lt;li&gt;how FDraggableDirective works,&lt;/li&gt;
&lt;li&gt;and how plugins enable extensions like resize, rotate, and external drop.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>typescript</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Building AI Low-Code Platform in Angular — Part 4: Styling and Handling Connections</title>
      <dc:creator>Siarhei Huzarevich</dc:creator>
      <pubDate>Fri, 08 Aug 2025 08:16:59 +0000</pubDate>
      <link>https://dev.to/shuzarevich/building-ai-low-code-platform-in-angular-part-4-styling-and-handling-connections-keo</link>
      <guid>https://dev.to/shuzarevich/building-ai-low-code-platform-in-angular-part-4-styling-and-handling-connections-keo</guid>
      <description>&lt;p&gt;In the previous part, we brought our node editor to life with custom components and a dynamic palette. But nodes alone don’t make the magic — the real power comes from how they connect. And more importantly, how those connections behave, look, and respond to users.&lt;/p&gt;

&lt;p&gt;Today, we’re upgrading our flow with some serious UX polish:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔁 Enable dynamic reattachment of connections (drag to reconnect!)&lt;/li&gt;
&lt;li&gt;🏁 Style connections with sleek SVG markers&lt;/li&gt;
&lt;li&gt;✨ Add hover highlights and interactivity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s turn static lines into smart, responsive connectors — and give our low-code UI a professional edge.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/Foblex/Building-AI-Low-Code-Platform4" rel="noopener noreferrer"&gt;🔧 View the source code on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/EpicStaff/EpicStaff" rel="noopener noreferrer"&gt;🔧 Explore the full application&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🔁 Reattaching Connections
&lt;/h2&gt;

&lt;p&gt;Connections in &lt;strong&gt;&lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt;&lt;/strong&gt; aren’t just static wires — they’re fully interactive and reconfigurable. Each end of a connection features a drag handle — an invisible area you can grab to change where the connection starts or ends.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm0qlmbwws4r6ojhjgco9.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm0qlmbwws4r6ojhjgco9.webp" alt="Connection Target Reassignable" width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default, only the target end (&lt;a href="https://flow.foblex.com/docs/f-node-input-directive" rel="noopener noreferrer"&gt;input&lt;/a&gt;) is draggable. But you can also make the source end (&lt;a href="https://flow.foblex.com/docs/f-node-output-directive" rel="noopener noreferrer"&gt;output&lt;/a&gt;) reassignable by setting the &lt;strong&gt;fReassignableStart=”true”&lt;/strong&gt; input. This gives you precise control over which ends can be modified — on a per-connection basis.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flu7fs0ujunca81fecluj.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flu7fs0ujunca81fecluj.webp" alt="Both sides reassignable" width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Want to rewire a connection? Just drag one of its ends and drop it onto another connector. &lt;strong&gt;&lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt;&lt;/strong&gt; takes care of the rest — when the drag operation finishes, it emits the &lt;a href="https://flow.foblex.com/docs/f-connection-component" rel="noopener noreferrer"&gt;fReassignConnection&lt;/a&gt; event from the &amp;lt;&lt;a href="https://flow.foblex.com/docs/f-flow-component" rel="noopener noreferrer"&gt;f-flow&lt;/a&gt;&amp;gt; component itself (not from the connection element):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;f-flow fDraggable
        (fCreateNode)="createNode($event)"
        (fCreateConnection)="createConnection($event)"
        (fReassignConnection)="reassignConnection($event)"&amp;gt; // This event is emitted from [fDraggable](https://flow.foblex.com/docs/f-draggable-directive), not from f-connection itself
&amp;lt;/f-flow&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This event contains everything you need to handle the reattachment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class FReassignConnectionEvent {
  connectionId: string;
  isSourceReassign: boolean;
  isTargetReassign: boolean;
  oldSourceId: string;
  newSourceId: string | undefined;
  oldTargetId: string;
  newTargetId: string | undefined;
  dropPoint: IPoint;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;🧠 Tip:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The event will still be triggered even if the user doesn’t drop the connection onto another connector. In that case, newTargetId (or newSourceId) will be undefined, but you’ll still receive the exact dropPoint — the canvas coordinates where the user released the connection.&lt;/p&gt;

&lt;p&gt;This is extremely useful if you want to open a context menu or create a new node on the fly at that position.&lt;/p&gt;

&lt;p&gt;Here’s how to update your connection list when the target changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected reassignConnection(event: FReassignConnectionEvent): void {
  if (!event.newTargetId) {
    return;
  }
  this.connections.update((connections) =&amp;gt; {
    const connection = connections.find(c =&amp;gt; c.id === event.connectionId);
    if(!connection) {
      throw new Error(`Connection ${event.connectionId} not found`);
    }
    connection.to = event.newTargetId;
    return [...connections];
  });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it — your users can now interactively rewire the flow by grabbing connection ends and reattaching them wherever they need. It’s a small feature, but it makes the editor feel dynamic, intuitive, and alive.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://flow.foblex.com/examples/drag-to-reassign" rel="noopener noreferrer"&gt;https://flow.foblex.com/examples/drag-to-reassign&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🧬 Connection Types and Behaviors
&lt;/h2&gt;

&lt;p&gt;Connections in &lt;strong&gt;&lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt;&lt;/strong&gt; aren’t just visual lines — they’re deeply customizable and behave exactly how you want them to.&lt;/p&gt;

&lt;p&gt;Let’s start with the three &lt;strong&gt;&lt;a href="https://flow.foblex.com/docs/f-connection-component" rel="noopener noreferrer"&gt;connection types&lt;/a&gt;&lt;/strong&gt;, each offering a different visual style:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;straight&lt;/strong&gt; — a simple straight line&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;segment&lt;/strong&gt; — a segmented line with right angles (ideal for logic-style editors)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;bezier&lt;/strong&gt; — a smooth, curved Bézier path&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffk968yxk61vekfho20md.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffk968yxk61vekfho20md.webp" alt="Connection types" width="800" height="757"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://flow.foblex.com/examples/connection-types" rel="noopener noreferrer"&gt;https://flow.foblex.com/examples/connection-types&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, you can define how the &lt;strong&gt;&lt;a href="https://flow.foblex.com/examples/connection-behaviours" rel="noopener noreferrer"&gt;connection behaves&lt;/a&gt;&lt;/strong&gt; when linking nodes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;fixed&lt;/strong&gt; — connects from a specific side (left, right, top, bottom) defined via fConnectableSide&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;fixed_center&lt;/strong&gt; — connects from the exact center of each node&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;floating&lt;/strong&gt; — auto-calculates the intersection between the node’s shape and the imaginary line between node centers (great for circular or irregular shapes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgeo1u4ngbudzr5ecxgqb.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgeo1u4ngbudzr5ecxgqb.webp" alt="Connection behaviors" width="800" height="762"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://flow.foblex.com/examples/connection-behaviours" rel="noopener noreferrer"&gt;https://flow.foblex.com/examples/connection-behaviours&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These options give you total control over both aesthetics and logic — whether you’re building a chatbot flow, a wiring diagram, or an AI pipeline.&lt;/p&gt;

&lt;p&gt;📖 Want to dive deeper into all behaviors and visuals?&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://flow.foblex.com/docs/f-connection-component" rel="noopener noreferrer"&gt;Read the full documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our low-code platform, we’ll use a Bézier curve with a fixed side behavior, which feels natural for horizontal flow layouts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;f-connection [fConnectionId]="connection.id"
              fBehavior="fixed"
              fType="bezier"
              [fOutputId]="connection.from"
              [fInputId]="connection.to" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’re now in control — not just of where your connections go, but how they feel when they get there.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏁 Adding SVG Markers to Connections
&lt;/h2&gt;

&lt;p&gt;A great UI isn’t just about layout — it’s about clarity and direction. When working with connections, nothing communicates intent better than directional markers.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;&lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt;&lt;/strong&gt;, you can attach custom SVG markers to the start or end of any connection. These can be arrows, dots, diamonds — anything you can draw in SVG.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyv5bvttblhu7cx3mgqjp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyv5bvttblhu7cx3mgqjp.webp" alt="Connection markers" width="800" height="755"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://flow.foblex.com/examples/connection-markers" rel="noopener noreferrer"&gt;https://flow.foblex.com/examples/connection-markers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s a minimal example that adds a simple circle to the start of a connection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;f-connection [fOutputId]="id1" [fInputId]="id2"&amp;gt;
  &amp;lt;svg fMarker type="f-connection-marker-start"
       [height]="10" [width]="10" [refX]="5" [refY]="5"&amp;gt;
    &amp;lt;circle cx="5" cy="5" r="2" fill="var(--connection-color)" /&amp;gt;
  &amp;lt;/svg&amp;gt;
&amp;lt;/f-connection&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;a href="https://flow.foblex.com/docs/f-connection-marker-directive" rel="noopener noreferrer"&gt;📌fMarker&lt;/a&gt; input parameters:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;type&lt;/strong&gt; — the marker type&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;width, height&lt;/strong&gt; — size of the marker viewport&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;refX, refY&lt;/strong&gt; — anchoring point (where the marker attaches to the connection)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All marker types are defined in the &lt;strong&gt;&lt;a href="https://flow.foblex.com/docs/f-connection-marker-directive" rel="noopener noreferrer"&gt;EFMarkerType&lt;/a&gt;&lt;/strong&gt; enum:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;enum EFMarkerType {
  START,
  END,
  SELECTED_START,
  SELECTED_END,
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can style markers differently for selected states — this is great for showing hover, focus, or active connections.&lt;/p&gt;

&lt;p&gt;Here’s a full example that applies all four types of markers on a connection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected readonly eMarkerType = EFMarkerType;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;f-connection [fConnectionId]="connection.id"
              fBehavior="fixed"
              fType="bezier"
              [fOutputId]="connection.from"
              [fInputId]="connection.to"&amp;gt;
  &amp;lt;svg viewBox="0 0 10 10" fMarker [type]="eMarkerType.START" [height]="10" [width]="10" [refX]="5" [refY]="5"&amp;gt;
    &amp;lt;circle cx="5" cy="5" r="2" fill="var(--connection-color)" /&amp;gt;
  &amp;lt;/svg&amp;gt;
  &amp;lt;svg viewBox="0 0 700 700" fMarker [type]="eMarkerType.END" [height]="5" [width]="5" [refX]="4" [refY]="2.5"&amp;gt;
    &amp;lt;path fill="var(--connection-color)" d="M0,0L700,350L0,700L150,350z" /&amp;gt;
  &amp;lt;/svg&amp;gt;
  &amp;lt;svg viewBox="0 0 10 10" fMarker [type]="eMarkerType.SELECTED_START" [height]="10" [width]="10" [refX]="5" [refY]="5"&amp;gt;
    &amp;lt;circle cx="5" cy="5" r="2" fill="var(--connection-color)" /&amp;gt;
  &amp;lt;/svg&amp;gt;
  &amp;lt;svg viewBox="0 0 700 700" fMarker [type]="eMarkerType.SELECTED_END" [height]="5" [width]="5" [refX]="4" [refY]="2.5"&amp;gt;
    &amp;lt;path fill="var(--connection-color)" d="M0,0L700,350L0,700L150,350z" /&amp;gt;
  &amp;lt;/svg&amp;gt;
&amp;lt;/f-connection&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With just a few lines of SVG, your connections can now clearly show direction, communicate selection, and make the entire UI feel more polished and understandable.&lt;/p&gt;

&lt;p&gt;Next, we’ll make them glow ✨&lt;/p&gt;

&lt;h2&gt;
  
  
  🎨 Styling Connections and Markers
&lt;/h2&gt;

&lt;p&gt;Let’s make our connections not just smart — but stylish.&lt;/p&gt;

&lt;p&gt;First, we’ll define a custom CSS variable — connection-color and apply it to both the stroke of the connection path and the fill of any SVG markers. This way, all visual elements stay perfectly in sync.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;::ng-deep {
  .f-connection {
    --connection-color: rgba(60, 60, 67); // default line color

    &amp;amp;.f-selected {
      --connection-color: #3451b2; // highlighted color on selection
    }

    .f-connection-drag-handle {
      fill: transparent; // invisible grab area for reattachment
    }

    .f-connection-selection {
      stroke-width: 10; // makes the selection area easier to hit
    }

    .f-connection-path {
      stroke: var(--connection-color); // applies the CSS variable
      stroke-width: 2;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;💡 Why use CSS variables?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;They allow you to easily switch themes, support dark/light mode, or dynamically update styles on interaction — without touching the DOM.&lt;/p&gt;

&lt;h2&gt;
  
  
  👆 Hover Effect
&lt;/h2&gt;

&lt;p&gt;Now let’s make our connections respond to user interaction. A subtle glow on hover helps users understand that a line is clickable or draggable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.f-connection-selection {
  stroke-width: 15;

  &amp;amp;:hover {
    stroke: rgba(52, 81, 178, 0.1);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just like that, your editor starts to feel alive — reacting to user input in a smooth, intuitive way.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✅ Done!
&lt;/h2&gt;

&lt;p&gt;At this point, your connections are no longer just lines between nodes — they’re fully interactive, visually expressive, and ready for production use.&lt;/p&gt;

&lt;p&gt;Here’s what we’ve achieved in this part:&lt;/p&gt;

&lt;p&gt;✅ Enabled reattachment of connections with intuitive drag handles&lt;/p&gt;

&lt;p&gt;✅ Supported reconnection from either side of the link&lt;/p&gt;

&lt;p&gt;✅ Styled connection paths using CSS variables for easy theming&lt;/p&gt;

&lt;p&gt;✅ Added SVG markers to visualize direction and selection&lt;/p&gt;

&lt;p&gt;✅ Enhanced the UX with hover effects and visual feedback&lt;/p&gt;

&lt;p&gt;Together, these improvements make a massive difference in how your flow editor feels to the user. Small touches like directional arrows or soft highlights can turn a technical tool into a truly enjoyable experience.&lt;/p&gt;

&lt;p&gt;But we’re not stopping here.&lt;/p&gt;

&lt;p&gt;Up next, we’ll take interactivity to the next level by adding a side panel that appears when a node is selected. This panel will allow users to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;View and edit node parameters dynamically&lt;/li&gt;
&lt;li&gt;Bind controls to schema-driven forms&lt;/li&gt;
&lt;li&gt;See changes reflected live in the flow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a huge step toward building a real low-code platform, where users don’t just connect logic — they configure and control it through a seamless UI.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>lowcode</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Building AI Low-Code Platform in Angular — Part 3: Creating Custom Nodes and a Node Palette</title>
      <dc:creator>Siarhei Huzarevich</dc:creator>
      <pubDate>Thu, 24 Jul 2025 17:24:26 +0000</pubDate>
      <link>https://dev.to/shuzarevich/building-ai-low-code-platform-in-angular-part-3-creating-custom-nodes-and-a-node-palette-g1</link>
      <guid>https://dev.to/shuzarevich/building-ai-low-code-platform-in-angular-part-3-creating-custom-nodes-and-a-node-palette-g1</guid>
      <description>&lt;p&gt;In the previous article, we built a basic editor with two nodes and a single connection. Now, let’s take the next step — we’ll add custom Angular node components, interactive connectors, and a simple node palette.&lt;/p&gt;

&lt;p&gt;It’s not a full low-code platform just yet, but we’re steadily laying the groundwork — from visual customization to dynamic interaction between elements.&lt;/p&gt;

&lt;h2&gt;
  
  
  In this part, we’ll implement:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🎨 A &lt;a href="https://flow.foblex.com/examples/add-node-from-palette" rel="noopener noreferrer"&gt;&lt;strong&gt;node palette&lt;/strong&gt;&lt;/a&gt; with icons and labels&lt;/li&gt;
&lt;li&gt;🧩 &lt;strong&gt;Custom Angular components&lt;/strong&gt; for each node&lt;/li&gt;
&lt;li&gt;🧵 &lt;strong&gt;Connectors&lt;/strong&gt; (input/output) with interactive hover effects&lt;/li&gt;
&lt;li&gt;🔌 &lt;strong&gt;Logic to create nodes and connections&lt;/strong&gt; using Foblex Flow events&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🔧 &lt;a href="https://github.com/Foblex/Building-AI-Low-Code-Platform3" rel="noopener noreferrer"&gt;View the source code on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🔧 &lt;a href="https://github.com/EpicStaff/EpicStaff" rel="noopener noreferrer"&gt;Explore the full application&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Component Architecture
&lt;/h2&gt;

&lt;p&gt;Our visual flow will be based on three main components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flow — the main Angular component containing &lt;a href="https://flow.foblex.com/docs/f-flow-component" rel="noopener noreferrer"&gt;f-flow&lt;/a&gt; and managing all nodes and connections&lt;/li&gt;
&lt;li&gt;Node — a custom Angular component that represents a single node, linked to &lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt; using the &lt;a href="https://flow.foblex.com/docs/f-node-directive" rel="noopener noreferrer"&gt;fNode&lt;/a&gt; directive&lt;/li&gt;
&lt;li&gt;FlowPalette — a sidebar panel containing draggable elements for the canvas&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Continuing from the Previous Project
&lt;/h2&gt;

&lt;p&gt;We’ll continue building on the project from the &lt;a href="https://dev.to/shuzarevich/design-node-based-interfaces-in-angular-a-beginners-guide-with-foblex-flow-356h"&gt;previous article&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add Icons and Reset Styles
&lt;/h2&gt;

&lt;p&gt;First, we need to load the required fonts and icons. Add the following to your index.html:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;link href="https://fonts.googleapis.com/css2?family=Inter&amp;amp;family=Material+Symbols+Outlined" rel="stylesheet"/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, reset default styles and apply the base font by adding this to your styles.scss:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  font-family: 'Inter', sans-serif;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Defining Node Models and Initial Data
&lt;/h2&gt;

&lt;p&gt;Let’s begin by defining the models for nodes and connections. Below are the TypeScript interfaces and an example set of 8 node types, which you can expand or customize later as needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// i-connection.ts
export interface IConnection {
  id: string;
  from: string;
  to: string;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// i-storage-node.ts
export interface IStorageNode {
  name: string;
  icon: string;
  shortDescription: string;
  inputs: string[];
  outputs: string[];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// i-node.ts
import { IStorageNode } from './i-storage-node';

export interface INode extends IStorageNode {
  id: string;
  position: { x: number; y: number };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s define a list of available nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// data.ts
export const DATA: IStorageNode[] = [
  {
    name: "Start",
    icon: "link",
    shortDescription: "Start: incoming request",
    inputs: [],
    outputs: ["any"]
  },
  {
    name: "AI Parser",
    icon: "code_blocks",
    shortDescription: "Extract JSON fields",
    inputs: ["any"],
    outputs: ["success", "failure"]
  },
  {
    name: "Retry Loop",
    icon: "repeat",
    shortDescription: "Repeat for each item",
    inputs: ["any"],
    outputs: ["any"]
  },
  {
    name: "AI Validator",
    icon: "check_circle",
    shortDescription: "AI data checker",
    inputs: ["parsed", "retry"],
    outputs: ["valid", "invalid"]
  },
  {
    name: "AI Executor",
    icon: "psychology",
    shortDescription: "AI-powered action",
    inputs: ["valid"],
    outputs: ["done"]
  },
  {
    name: "If-Else",
    icon: "fork_right",
    shortDescription: "Conditional branching",
    inputs: ["any"],
    outputs: ["true", "false"]
  },
  {
    name: "Error Handler",
    icon: "error",
    shortDescription: "Error management",
    inputs: ["any"],
    outputs: []
  },
  {
    name: "Logger",
    icon: "terminal",
    shortDescription: "Flow logging",
    inputs: ["any"],
    outputs: []
  }
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating the Node Palette
&lt;/h2&gt;

&lt;p&gt;Next, we’ll create the flow-palette component — a vertical sidebar from which we can drag nodes into the canvas.&lt;/p&gt;

&lt;p&gt;Each palette item is a div using the fExternalItem directive, which enables external drag-and-drop support with &lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Template (flow-palette.html)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@for (node of nodes; track node.name) {
  &amp;lt;div class="palette-node" fExternalItem [fData]="node" [fPreviewMatchSize]="true"&amp;gt;
    &amp;lt;span class="icon"&amp;gt;{{ node.icon }}&amp;lt;/span&amp;gt;
    &amp;lt;span&amp;gt;{{ node.name }}&amp;lt;/span&amp;gt;
  &amp;lt;/div&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Styles (flow-palette.scss)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:host {
  position: absolute;
  display: flex;
  flex-direction: column;
  width: fit-content;
  gap: 8px;
  left: 20px;
  top: 50%;
  transform: translateY(-50%);
}

::ng-deep .palette-node {
  display: flex;
  align-items: center;
  gap: 4px;
  border: 1px solid rgba(0, 0, 0, 0.06);
  border-radius: 4px;
  padding: 5px;
  background-color: white;

  .icon {
    font-family: 'Material Symbols Outlined';
    font-size: 20px;
    background: linear-gradient(to right, #4b91f1, #5c2ae8);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;We use ::ng-deep here because drag preview elements are appended to the document.body. Without it, styles wouldn’t apply to them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Component (flow-palette.ts)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Component, ChangeDetectionStrategy } from '@angular/core';
import { FFlowModule } from '@foblex/flow';
import { DATA } from './data';

@Component({
  selector: 'flow-palette',
  imports: [FFlowModule],
  templateUrl: './flow-palette.html',
  styleUrl: './flow-palette.scss',
  changeDetection: ChangeDetectionStrategy.OnPush,
  standalone: true,
})
export class FlowPalette {
  protected nodes = DATA;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can add nodes from the palette directly into the flow by handling the following event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;f-flow (fCreateNode)="createNode($event)"&amp;gt;
&amp;lt;/f-flow&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📦 Custom Nodes
&lt;/h2&gt;

&lt;p&gt;Each node in the editor will be represented by a dedicated Angular component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component({
  selector: 'node',
  ...
})
export class Node {
  public data = input.required&amp;lt;INode&amp;gt;();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Node Template:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="connectors inputs"&amp;gt;
  @for (connector of data().inputs; track $index) {
    &amp;lt;connector fNodeInput [fInputId]="connector + ' ' + data().id" fInputConnectableSide="left" /&amp;gt;
  }
&amp;lt;/div&amp;gt;
&amp;lt;div class="connectors outputs"&amp;gt;
  @for (connector of data().outputs; track $index) {
    &amp;lt;connector fNodeOutput [fOutputId]="connector + ' ' + data().id" fOutputConnectableSide="right" /&amp;gt;
  }
&amp;lt;/div&amp;gt;

&amp;lt;div class="header"&amp;gt;
  &amp;lt;span class="icon"&amp;gt;{{ data().icon }}&amp;lt;/span&amp;gt;
  &amp;lt;span&amp;gt;{{ data().name }}&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;div class="description"&amp;gt;{{ data().shortDescription }}&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Styles (node.scss):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:host {
  position: relative;
  background: #ffffff;
  border-radius: 6px;
  padding: 20px 24px;
  min-width: 180px;
  max-width: 180px;
  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.04);
  border: 1px solid rgba(0, 0, 0, 0.06);
  display: flex;
  flex-direction: column;
  gap: 8px;
  cursor: default;

  &amp;amp;:hover {
    box-shadow: 0 6px 14px rgba(0, 0, 0, 0.08);
  }

  &amp;amp;.f-selected {
    outline: 2px solid #4b91f1;
    outline-offset: -2px;
    box-shadow: 0 0 0 4px rgba(75, 145, 241, 0.12);
  }

  .header {
    display: flex;
    align-items: center;
    gap: 10px;
    font-weight: 600;
    font-size: 15px;
  }

  .icon {
    font-family: 'Material Symbols Outlined';
    font-size: 20px;
    background: linear-gradient(to right, #4b91f1, #5c2ae8);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
  }

  .description {
    font-size: 13px;
    opacity: 0.7;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
    width: fit-content;
  }

  .connectors {
    position: absolute;
    display: flex;
    flex-direction: column;
    gap: 10px;
    z-index: 1;

    &amp;amp;.inputs {
      left: -6px;
      top: 8px;
      bottom: 8px;
      justify-content: start;
    }

    &amp;amp;.outputs {
      right: -6px;
      top: 8px;
      bottom: 8px;
      justify-content: end;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each node has inputs (&lt;a href="https://flow.foblex.com/docs/f-node-input-directive" rel="noopener noreferrer"&gt;fNodeInput&lt;/a&gt;) and outputs (&lt;a href="https://flow.foblex.com/docs/f-node-output-directive" rel="noopener noreferrer"&gt;fNodeOutput&lt;/a&gt;) — these are small circular components with hover animation and styles for the connected state.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔘 Connector Component
&lt;/h2&gt;

&lt;p&gt;While we could have kept the connector inline inside the node, we’re moving it to a standalone component for better style encapsulation and code separation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Component (connector.ts):
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { ChangeDetectionStrategy, Component } from '@angular/core';
import { FFlowModule } from '@foblex/flow';

@Component({
  selector: 'connector',
  imports: [FFlowModule],
  template: '',
  styleUrl: './connector.scss',
  changeDetection: ChangeDetectionStrategy.OnPush,
  standalone: true,
})
export class Connector {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Styles (connector.scss):
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:host {
  position: relative;
  width: 12px;
  height: 12px;
  min-height: 12px;
  border-radius: 50%;
  background-color: #fff;
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
  cursor: pointer;

  &amp;amp;.connected {
    background-color: #4b91f1;
    box-shadow: 0 0 0 2px rgba(75, 145, 241, 0.2);
  }

  &amp;amp;:hover {
    transform: scale(1.4);
    box-shadow: 0 0 6px rgba(75, 145, 241, 0.4);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🚀 Dynamically Adding Nodes from the Palette
&lt;/h2&gt;

&lt;p&gt;Up to this point, we’ve been rendering nodes manually — statically defined in the template. Now it’s time to take the next step: we’ll enable users to dynamically add nodes by dragging them from the side palette into the canvas.&lt;br&gt;
Here’s how the flow works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user drags a node from the palette.&lt;/li&gt;
&lt;li&gt;The fCreateNode event is triggered, giving us information about what type of node was added and where it should appear.&lt;/li&gt;
&lt;li&gt;We push the new node into a reactive array — and thanks to &lt;a class="mentioned-user" href="https://dev.to/for"&gt;@for&lt;/a&gt;, it immediately appears on the canvas.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  1. Storing nodes in a signal
&lt;/h3&gt;

&lt;p&gt;We start by creating a signal to hold the list of all current nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected nodes = signal&amp;lt;INode[]&amp;gt;([]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Signals make the UI reactive — whenever the list updates, the DOM reflects the changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Handling the fCreateNode event
&lt;/h3&gt;

&lt;p&gt;Now let’s define the method that will run when a new node is added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected createNode(event: FCreateNodeEvent&amp;lt;IStorageNode&amp;gt;): void {
  this.nodes.update((nodes) =&amp;gt; {
    const newNode: INode = {
      ...event.data,
      id: generateGuid(),
      position: event.rect || { x: 0, y: 0 }
    };
    return [...nodes, newNode];
  });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s what’s happening:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We extract the node’s metadata from event.data.&lt;/li&gt;
&lt;li&gt;We generate a unique id using generateGuid().&lt;/li&gt;
&lt;li&gt;We use the position passed from the event — this is usually the cursor position on drop.&lt;/li&gt;
&lt;li&gt;We return a new list of nodes with the new one added.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Updating the template
&lt;/h3&gt;

&lt;p&gt;Let’s replace the static nodes with a dynamic list rendered using &lt;a class="mentioned-user" href="https://dev.to/for"&gt;@for&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;f-flow fDraggable
        (fCreateNode)="createNode($event)"&amp;gt;
  &amp;lt;f-canvas&amp;gt;
    @for (node of nodes(); track node.id) {
      &amp;lt;node
        fNode
        fDragHandle
        [fNodePosition]="node.position"
        [fNodeId]="node.id"
        [data]="node"&amp;gt;
      &amp;lt;/node&amp;gt;
    }
  &amp;lt;/f-canvas&amp;gt;
&amp;lt;/f-flow&amp;gt;

&amp;lt;flow-palette /&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fDraggable enables node movement inside the canvas.&lt;/li&gt;
&lt;li&gt;fNode links the element to Foblex Flow.&lt;/li&gt;
&lt;li&gt;fDragHandle defines the draggable area inside the node.&lt;/li&gt;
&lt;li&gt;[fNodePosition], [fNodeId], and [data] pass in the necessary parameters for rendering.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔁 How the flow comes together
&lt;/h2&gt;

&lt;p&gt;When a user drags a node from flow-palette, the fExternalItem directive fires an fCreateNode event. We handle that event in the createNode method, construct a new node object, and append it to the list. Thanks to &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/for"&gt;@for&lt;/a&gt;&lt;/strong&gt;, the UI instantly reflects the change.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠 Generating Unique IDs
&lt;/h2&gt;

&lt;p&gt;To ensure each node has a unique identifier, we use a small utility function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function generateGuid(): string {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    const r = Math.random() * 16 | 0;
    const v = c === 'x' ? r : (r &amp;amp; 0x3 | 0x8);
    return v.toString(16);
  });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ⚙️ Logic for Creating and Rendering Connections
&lt;/h2&gt;

&lt;p&gt;Now that we can add nodes from the palette, it’s time to implement the next key step: connecting nodes together — and rendering those connections inside the canvas.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧩 What &amp;lt;&lt;a href="https://flow.foblex.com/docs/f-connection-for-create-component" rel="noopener noreferrer"&gt;f-connection-for-create&lt;/a&gt;&amp;gt; Does
&lt;/h2&gt;

&lt;p&gt;This component goes inside  and renders a temporary line when the user starts dragging a connection from an output. It also triggers the fCreateConnection event once the drag ends — either over a valid input or in empty space.&lt;/p&gt;

&lt;h2&gt;
  
  
  📌 Listening to fCreateConnection and Displaying Connections
&lt;/h2&gt;

&lt;p&gt;Let’s update our Flow component template to handle the connection event and also render all created connections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;f-flow fDraggable
        (fCreateNode)="createNode($event)"
        (fCreateConnection)="createConnection($event)"&amp;gt;
  &amp;lt;f-canvas&amp;gt;
    &amp;lt;!-- Visual feedback during dragging --&amp;gt;
    &amp;lt;f-connection-for-create&amp;gt;&amp;lt;/f-connection-for-create&amp;gt;

    &amp;lt;!-- Render all established connections --&amp;gt;
    @for (connection of connections(); track connection.id) {
      &amp;lt;f-connection [fConnectionId]="connection.id"
                    [fOutputId]="connection.from"
                    [fInputId]="connection.to" /&amp;gt;
    }

    &amp;lt;!-- Render all nodes --&amp;gt;
    @for (node of nodes(); track node.id) {
      &amp;lt;node
        fNode
        fDragHandle
        [fNodePosition]="node.position"
        [fNodeId]="node.id"
        [data]="node"&amp;gt;
      &amp;lt;/node&amp;gt;
    }
  &amp;lt;/f-canvas&amp;gt;
&amp;lt;/f-flow&amp;gt;

&amp;lt;flow-palette /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔁 Handling the Connection Event
&lt;/h2&gt;

&lt;p&gt;Here’s the method that handles fCreateConnection and stores the connection in a reactive signal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected connections = signal&amp;lt;IConnection[]&amp;gt;([]);

protected createConnection(event: FCreateConnectionEvent): void {
  if (!event.fInputId) return;

  this.connections.update((connections) =&amp;gt; [
    ...connections,
    {
      id: generateGuid(),
      from: event.fOutputId,
      to: event.fInputId
    }
  ]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s what’s happening:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;event.fOutputId is the ID of the output connector the user dragged from.&lt;/li&gt;
&lt;li&gt;event.fInputId is the ID of the input the user dropped onto.&lt;/li&gt;
&lt;li&gt;If fInputId is missing (the user released the mouse in empty space), we ignore the event.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🖇 Putting It All Together
&lt;/h2&gt;

&lt;p&gt;Now we have a complete cycle in place:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Nodes are added from the palette.&lt;/li&gt;
&lt;li&gt;The user connects outputs to inputs.&lt;/li&gt;
&lt;li&gt;All valid connections are stored in connections() and automatically rendered in the flow via .&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🧩 Conclusion
&lt;/h2&gt;

&lt;p&gt;At this stage, our visual editor is starting to look much more like a real low-code platform. We’ve learned how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create custom Angular-based nodes;&lt;/li&gt;
&lt;li&gt;render them inside the canvas;&lt;/li&gt;
&lt;li&gt;drag items from a palette;&lt;/li&gt;
&lt;li&gt;connect nodes visually using interactive connectors;&lt;/li&gt;
&lt;li&gt;store and display the list of all connections.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key idea in this part is the separation of logic from visual behavior. The Foblex Flow library doesn’t manage your state directly — it simply tells you what happened: a node was added, a connection started, or a drag event completed. Everything else remains under your control.&lt;/p&gt;

&lt;p&gt;In the next part, we’ll focus on styling and connection behavior. We’ll add curved lines, color schemes, arrows, custom states, and interactions like hover, click, and delete. These enhancements will give your flow editor a polished and professional feel.&lt;/p&gt;

&lt;p&gt;🧠 Series: Building AI Low-Code Platform in Angular&lt;br&gt;
✅ &lt;a href="https://dev.to/shuzarevich/building-visual-interfaces-in-angular-introducing-foblex-flow-49cj"&gt;Part 1: Introduction to Foblex Flow&lt;/a&gt;&lt;br&gt;
✅ &lt;a href="https://dev.to/shuzarevich/design-node-based-interfaces-in-angular-a-beginners-guide-with-foblex-flow-356h"&gt;Part 2: Creating Your First Flow&lt;/a&gt;&lt;br&gt;
✅ Part 3: Creating Custom Nodes and a Node Palette&lt;br&gt;
🔄 Part 4: Styling and Handling Connections (soon)&lt;br&gt;
🔄 Part 5: Creating a Node Configuration Panel (soon)&lt;br&gt;
🔄 Part 6: Flow Execution and Playback Animation (soon)&lt;br&gt;
🔄 Part 7: Minimap, Multi-Select and Smart Alignment (soon)&lt;/p&gt;

</description>
      <category>angular</category>
      <category>opensource</category>
      <category>lowcode</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building AI Low-Code Platform in Angular — Part 2: Creating Your First Flow</title>
      <dc:creator>Siarhei Huzarevich</dc:creator>
      <pubDate>Tue, 24 Jun 2025 14:33:09 +0000</pubDate>
      <link>https://dev.to/shuzarevich/design-node-based-interfaces-in-angular-a-beginners-guide-with-foblex-flow-356h</link>
      <guid>https://dev.to/shuzarevich/design-node-based-interfaces-in-angular-a-beginners-guide-with-foblex-flow-356h</guid>
      <description>&lt;p&gt;Learn how to render a flow, create basic draggable nodes, and connect them. This is the foundation for your AI Low-code platform.&lt;/p&gt;

&lt;p&gt;In this article, we’ll go through the minimal setup needed to build an interactive flow with draggable nodes and dynamic connections — all using &lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt;, a native Angular library for visual interfaces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Foblex/Building-AI-Low-Code-Platform2" rel="noopener noreferrer"&gt;🔧 View the source code on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/EpicStaff/EpicStaff" rel="noopener noreferrer"&gt;🔧 Explore the full application&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Installation
&lt;/h2&gt;

&lt;p&gt;To add &lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt; to your Angular project, simply use the schematics command, which will automatically install all dependencies and perform the initial setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng add @foblex/flow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔧 Creating a Basic Flow
&lt;/h2&gt;

&lt;p&gt;Let’s now create the simplest example — an interface with two nodes and a connection between their connectors. This is the minimal setup that’s ideal for getting started:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;f-flow&lt;/span&gt; &lt;span class="na"&gt;fDraggable&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;f-canvas&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;f-connection&lt;/span&gt; &lt;span class="na"&gt;fOutputId=&lt;/span&gt;&lt;span class="s"&gt;"output1"&lt;/span&gt; &lt;span class="na"&gt;fInputId=&lt;/span&gt;&lt;span class="s"&gt;"input1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/f-connection&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;
      &lt;span class="na"&gt;fNode&lt;/span&gt;
      &lt;span class="na"&gt;fDragHandle&lt;/span&gt;
      &lt;span class="na"&gt;fNodeOutput&lt;/span&gt;
      &lt;span class="na"&gt;[fNodePosition]=&lt;/span&gt;&lt;span class="s"&gt;"{ x: 32, y: 32 }"&lt;/span&gt;
      &lt;span class="na"&gt;fOutputId=&lt;/span&gt;&lt;span class="s"&gt;"output1"&lt;/span&gt;
      &lt;span class="na"&gt;fOutputConnectableSide=&lt;/span&gt;&lt;span class="s"&gt;"right"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      Node 1
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;
      &lt;span class="na"&gt;fNode&lt;/span&gt;
      &lt;span class="na"&gt;fDragHandle&lt;/span&gt;
      &lt;span class="na"&gt;fNodeInput&lt;/span&gt;
      &lt;span class="na"&gt;[fNodePosition]=&lt;/span&gt;&lt;span class="s"&gt;"{ x: 240, y: 32 }"&lt;/span&gt;
      &lt;span class="na"&gt;fInputId=&lt;/span&gt;&lt;span class="s"&gt;"input1"&lt;/span&gt;
      &lt;span class="na"&gt;fInputConnectableSide=&lt;/span&gt;&lt;span class="s"&gt;"left"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      Node 2
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/f-canvas&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/f-flow&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🎨 Styling
&lt;/h2&gt;

&lt;p&gt;The library &lt;strong&gt;does not include any default styles&lt;/strong&gt;, giving you full freedom in design. Below is a basic style set to get started quickly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.f-flow&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;400px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.f-node&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;24px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;67&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;67&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="err"&gt;&amp;amp;.f-selected&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#3451b2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;Highlights&lt;/span&gt; &lt;span class="err"&gt;the&lt;/span&gt; &lt;span class="err"&gt;border&lt;/span&gt; &lt;span class="err"&gt;when&lt;/span&gt; &lt;span class="err"&gt;the&lt;/span&gt; &lt;span class="err"&gt;node&lt;/span&gt; &lt;span class="err"&gt;is&lt;/span&gt; &lt;span class="err"&gt;selected&lt;/span&gt;
    &lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;The&lt;/span&gt; &lt;span class="err"&gt;f-selected&lt;/span&gt; &lt;span class="err"&gt;class&lt;/span&gt; &lt;span class="err"&gt;is&lt;/span&gt; &lt;span class="err"&gt;automatically&lt;/span&gt; &lt;span class="err"&gt;added&lt;/span&gt; &lt;span class="err"&gt;by&lt;/span&gt; &lt;span class="err"&gt;the&lt;/span&gt; &lt;span class="err"&gt;library&lt;/span&gt; &lt;span class="err"&gt;when&lt;/span&gt; &lt;span class="err"&gt;a&lt;/span&gt; &lt;span class="err"&gt;node&lt;/span&gt; &lt;span class="err"&gt;or&lt;/span&gt; &lt;span class="err"&gt;connection&lt;/span&gt; &lt;span class="err"&gt;is&lt;/span&gt; &lt;span class="err"&gt;selected.&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.f-drag-handle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;::ng-deep&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;.f-connection&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;.f-connection-drag-handle&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
      &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;By&lt;/span&gt; &lt;span class="err"&gt;default,&lt;/span&gt; &lt;span class="err"&gt;this&lt;/span&gt; &lt;span class="err"&gt;element&lt;/span&gt; &lt;span class="err"&gt;has&lt;/span&gt; &lt;span class="err"&gt;a&lt;/span&gt; &lt;span class="err"&gt;black&lt;/span&gt; &lt;span class="err"&gt;fill&lt;/span&gt; &lt;span class="err"&gt;and&lt;/span&gt; &lt;span class="err"&gt;is&lt;/span&gt; &lt;span class="err"&gt;used&lt;/span&gt; &lt;span class="err"&gt;to&lt;/span&gt; &lt;span class="err"&gt;detect&lt;/span&gt; &lt;span class="err"&gt;the&lt;/span&gt; &lt;span class="err"&gt;start&lt;/span&gt; &lt;span class="err"&gt;of&lt;/span&gt; &lt;span class="err"&gt;dragging&lt;/span&gt; &lt;span class="err"&gt;(e.g.,&lt;/span&gt; &lt;span class="err"&gt;onmousedown).&lt;/span&gt;
      &lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;We&lt;/span&gt; &lt;span class="err"&gt;make&lt;/span&gt; &lt;span class="err"&gt;it&lt;/span&gt; &lt;span class="err"&gt;transparent&lt;/span&gt; &lt;span class="err"&gt;to&lt;/span&gt; &lt;span class="err"&gt;avoid&lt;/span&gt; &lt;span class="err"&gt;visual&lt;/span&gt; &lt;span class="err"&gt;clutter,&lt;/span&gt; &lt;span class="err"&gt;while&lt;/span&gt; &lt;span class="err"&gt;keeping&lt;/span&gt; &lt;span class="err"&gt;it&lt;/span&gt; &lt;span class="err"&gt;functional.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nc"&gt;.f-connection-selection&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="py"&gt;stroke-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;This&lt;/span&gt; &lt;span class="err"&gt;is&lt;/span&gt; &lt;span class="err"&gt;a&lt;/span&gt; &lt;span class="err"&gt;pseudo-connection&lt;/span&gt; &lt;span class="err"&gt;(a&lt;/span&gt; &lt;span class="err"&gt;copy&lt;/span&gt; &lt;span class="err"&gt;of&lt;/span&gt; &lt;span class="err"&gt;the&lt;/span&gt; &lt;span class="err"&gt;main&lt;/span&gt; &lt;span class="err"&gt;path)&lt;/span&gt; &lt;span class="err"&gt;used&lt;/span&gt; &lt;span class="err"&gt;to&lt;/span&gt; &lt;span class="err"&gt;make&lt;/span&gt; &lt;span class="err"&gt;it&lt;/span&gt; &lt;span class="err"&gt;easier&lt;/span&gt; &lt;span class="err"&gt;to&lt;/span&gt; &lt;span class="err"&gt;select&lt;/span&gt; &lt;span class="err"&gt;the&lt;/span&gt; &lt;span class="err"&gt;connection.&lt;/span&gt;
      &lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;It's&lt;/span&gt; &lt;span class="err"&gt;slightly&lt;/span&gt; &lt;span class="err"&gt;thicker&lt;/span&gt; &lt;span class="err"&gt;than&lt;/span&gt; &lt;span class="err"&gt;the&lt;/span&gt; &lt;span class="err"&gt;actual&lt;/span&gt; &lt;span class="err"&gt;path&lt;/span&gt; &lt;span class="err"&gt;(which&lt;/span&gt; &lt;span class="err"&gt;is&lt;/span&gt; &lt;span class="err"&gt;often&lt;/span&gt; &lt;span class="err"&gt;only&lt;/span&gt; &lt;span class="err"&gt;1px),&lt;/span&gt; &lt;span class="err"&gt;making&lt;/span&gt; &lt;span class="err"&gt;it&lt;/span&gt; &lt;span class="err"&gt;easier&lt;/span&gt; &lt;span class="err"&gt;to&lt;/span&gt; &lt;span class="err"&gt;interact&lt;/span&gt; &lt;span class="err"&gt;with.&lt;/span&gt;
      &lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;It&lt;/span&gt; &lt;span class="err"&gt;remains&lt;/span&gt; &lt;span class="err"&gt;invisible&lt;/span&gt; &lt;span class="err"&gt;to&lt;/span&gt; &lt;span class="err"&gt;avoid&lt;/span&gt; &lt;span class="err"&gt;affecting&lt;/span&gt; &lt;span class="err"&gt;visual&lt;/span&gt; &lt;span class="err"&gt;clarity&lt;/span&gt; &lt;span class="err"&gt;but&lt;/span&gt; &lt;span class="err"&gt;stays&lt;/span&gt; &lt;span class="err"&gt;active&lt;/span&gt; &lt;span class="err"&gt;for&lt;/span&gt; &lt;span class="err"&gt;user&lt;/span&gt; &lt;span class="err"&gt;interaction.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nc"&gt;.f-connection-path&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="py"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;67&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="py"&gt;stroke-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;.f-selected&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="err"&gt;.f-connection-path&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#3451b2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="err"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmfnsilo4t21hu3vzr80u.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmfnsilo4t21hu3vzr80u.webp" alt=" " width="766" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🔍 Explanation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://flow.foblex.com/docs/f-flow-component" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;f-flow&amp;gt;&lt;/code&gt;&lt;/a&gt; — the root component that manages the flow state.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://flow.foblex.com/docs/f-canvas-component" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;f-canvas&amp;gt;&lt;/code&gt;&lt;/a&gt; — the layer where nodes and connections are placed.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://flow.foblex.com/docs/f-node-directive" rel="noopener noreferrer"&gt;&lt;code&gt;fNode&lt;/code&gt;&lt;/a&gt; — directive representing a node.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://flow.foblex.com/docs/f-node-output-directive" rel="noopener noreferrer"&gt;&lt;code&gt;fNodeOutput&lt;/code&gt;&lt;/a&gt; / &lt;a href="https://flow.foblex.com/docs/f-node-input-directive" rel="noopener noreferrer"&gt;&lt;code&gt;fNodeInput&lt;/code&gt;&lt;/a&gt; — connectors for connections. &lt;a href="https://flow.foblex.com/docs/f-node-output-directive" rel="noopener noreferrer"&gt;&lt;code&gt;fNodeOutput&lt;/code&gt;&lt;/a&gt; is the source, and &lt;a href="https://flow.foblex.com/docs/f-node-input-directive" rel="noopener noreferrer"&gt;&lt;code&gt;fNodeInput&lt;/code&gt;&lt;/a&gt; is the target.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://flow.foblex.com/docs/f-connection-component" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;f-connection&amp;gt;&lt;/code&gt;&lt;/a&gt; — the component that renders a connection between two connectors by their &lt;code&gt;fOutputId&lt;/code&gt; and &lt;code&gt;fInputId&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Note&lt;/strong&gt;: &lt;code&gt;fOutputId&lt;/code&gt; and &lt;code&gt;fInputId&lt;/code&gt; may technically match, since they belong to different connector collections. However, this is not recommended, as future versions may unify these into a single &lt;code&gt;fConnector&lt;/code&gt; directive where matching IDs would cause conflicts.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧪 Try This
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Change the &lt;code&gt;[fNodePosition]&lt;/code&gt; coordinates&lt;/li&gt;
&lt;li&gt;Add more &lt;a href="https://flow.foblex.com/docs/f-node-directive" rel="noopener noreferrer"&gt;&lt;code&gt;fNode&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://flow.foblex.com/docs/f-connection-component" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;f-connection&amp;gt;&lt;/code&gt;&lt;/a&gt; elements&lt;/li&gt;
&lt;li&gt;Experiment with connection sides: &lt;code&gt;fOutputConnectableSide&lt;/code&gt;, &lt;code&gt;fInputConnectableSide&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Modify the connection type or behavior using the &lt;code&gt;fType&lt;/code&gt; and &lt;code&gt;fBehaviour&lt;/code&gt; inputs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fType&lt;/code&gt;: defines the visual style of the connection. Acceptable values from the &lt;code&gt;EFConnectionType&lt;/code&gt; enum include: &lt;code&gt;straight&lt;/code&gt;, &lt;code&gt;bezier&lt;/code&gt;, &lt;code&gt;segment&lt;/code&gt;. You can also pass a string for a custom connection type.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To create a custom connection type, see &lt;a href="https://flow.foblex.com/docs/f-connection-component" rel="noopener noreferrer"&gt;documentation here&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fBehavior&lt;/code&gt;: defines the &lt;strong&gt;connection behavior&lt;/strong&gt;, including positioning and flexibility. Acceptable values from &lt;code&gt;EFConnectionBehavior&lt;/code&gt; include: &lt;code&gt;fixed&lt;/code&gt;, &lt;code&gt;fixed_center&lt;/code&gt;, &lt;code&gt;floating&lt;/code&gt;. Default: &lt;code&gt;EFConnectionBehavior.FIXED&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚙️ Customize It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full styling freedom&lt;/strong&gt; — you decide how the nodes and UI will look.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Any Angular components&lt;/strong&gt; can be used inside &lt;strong&gt;fNode&lt;/strong&gt; — not limited to &lt;code&gt;div&lt;/code&gt;s.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📌 &lt;em&gt;Supports SSR, Standalone Components, Signals, and Zoneless Angular.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🐞 Common Mistakes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;❌ Missing &lt;code&gt;[fNodePosition]&lt;/code&gt; — the node won’t appear on screen.&lt;/li&gt;
&lt;li&gt;❌ &lt;code&gt;fOutputId&lt;/code&gt; or &lt;code&gt;fInputId&lt;/code&gt; don’t match the ones set in connectors — the connection won’t render.&lt;/li&gt;
&lt;li&gt;❌ Components are placed outside &lt;a href="https://flow.foblex.com/docs/f-canvas-component" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;f-canvas&amp;gt;&lt;/code&gt;&lt;/a&gt; — &lt;a href="https://flow.foblex.com/docs/f-node-directive" rel="noopener noreferrer"&gt;&lt;code&gt;fNode&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://flow.foblex.com/docs/f-connection-component" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;f-connection&amp;gt;&lt;/code&gt;&lt;/a&gt; must be within it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔍 Under the Hood
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;All &lt;code&gt;fNode&lt;/code&gt; elements are registered and stored with their coordinates.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;f-connection&amp;gt;&lt;/code&gt; finds the correct fNodeOutput and fNodeInput by ID.&lt;/li&gt;
&lt;li&gt;Connection points are calculated and an SVG line is drawn.&lt;/li&gt;
&lt;li&gt;When nodes are moved, connections update automatically.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  ⏭ What’s Next?
&lt;/h2&gt;

&lt;p&gt;In the next article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We’ll create custom Angular components as nodes&lt;/li&gt;
&lt;li&gt;Add drag and drop events&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📘 Jump To Series
&lt;/h2&gt;

&lt;p&gt;✅ &lt;a href="https://dev.to/shuzarevich/building-visual-interfaces-in-angular-introducing-foblex-flow-49cj"&gt;Part 1: Introduction to Foblex Flow&lt;/a&gt;&lt;br&gt;
✅ Part 2: Creating Your First Node Flow (you are here)&lt;br&gt;
🔄 Part 3: Creating Custom Nodes and a Node Palette (coming soon)&lt;/p&gt;

&lt;p&gt;👉 In the meantime, explore the official documentation: &lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;flow.foblex.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>lowcode</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Building AI Low-Code Platform in Angular— Part 1: Introduction to Foblex Flow</title>
      <dc:creator>Siarhei Huzarevich</dc:creator>
      <pubDate>Tue, 24 Jun 2025 14:10:18 +0000</pubDate>
      <link>https://dev.to/shuzarevich/building-visual-interfaces-in-angular-introducing-foblex-flow-49cj</link>
      <guid>https://dev.to/shuzarevich/building-visual-interfaces-in-angular-introducing-foblex-flow-49cj</guid>
      <description>&lt;p&gt;Introduction to the series and the Foblex Flow library — a flexible tool for creating flow-based UIs in Angular. Learn the core concepts behind low-code architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✍ Introduction
&lt;/h2&gt;

&lt;p&gt;Modern web applications increasingly require visual logic and process editors — from call flow builders to no-code tools or internal dashboards.&lt;/p&gt;

&lt;p&gt;If you work with Angular, you know how difficult it is to find a truly native solution for creating such flow interfaces.&lt;/p&gt;

&lt;p&gt;That’s why I created &lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt; — a library designed specifically for Angular to build flexible and dynamic node-based UIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔧 What is Foblex Flow?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt; is an Angular library for building interactive node-and-connection-based interfaces — fully written in Angular.&lt;/p&gt;

&lt;p&gt;With it, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Render and connect nodes dynamically&lt;/li&gt;
&lt;li&gt;Customize node templates freely&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;drag’n’drop&lt;/code&gt;, &lt;code&gt;zooming&lt;/code&gt;, &lt;code&gt;snapping&lt;/code&gt;, and &lt;code&gt;grouping&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Integrate with &lt;code&gt;SSR&lt;/code&gt;, &lt;code&gt;standalone components&lt;/code&gt;, &lt;code&gt;Angular Signals&lt;/code&gt;, and &lt;code&gt;zoneless&lt;/code&gt; Angular&lt;/li&gt;
&lt;li&gt;Build complete editors with state saving and custom logic&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧩 Where You Might Use It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Workflow editors&lt;/li&gt;
&lt;li&gt;Logic configurators (&lt;code&gt;low-code&lt;/code&gt; / &lt;code&gt;no-code&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Call routing editors&lt;/li&gt;
&lt;li&gt;Visual back-office or CRM tools&lt;/li&gt;
&lt;li&gt;Visual programming UIs (&lt;code&gt;block logic&lt;/code&gt;, &lt;code&gt;AI chains&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔍 What You’ll Learn in This Series
&lt;/h2&gt;

&lt;p&gt;In this series of tutorials, we’ll cover:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How to install and render a basic flow&lt;/li&gt;
&lt;li&gt;How to use custom components as nodes&lt;/li&gt;
&lt;li&gt;How to configure and customize connections&lt;/li&gt;
&lt;li&gt;How to enable snapping, resize, and rotate&lt;/li&gt;
&lt;li&gt;How to save and restore flow state&lt;/li&gt;
&lt;li&gt;How to build a real-world editor with the library&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  📚 Live Examples
&lt;/h2&gt;

&lt;p&gt;Here are a few interactive examples built with &lt;a href="https://flow.foblex.com/" rel="noopener noreferrer"&gt;Foblex Flow&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📞 &lt;a href="https://github.com/Foblex/f-flow-example" rel="noopener noreferrer"&gt;Call Flow Editor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🧱 &lt;a href="https://flow.foblex.com/examples/f-visual-programming-flow" rel="noopener noreferrer"&gt;Logic Configuration Tool&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🗄 &lt;a href="https://flow.foblex.com/examples/f-db-management-flow" rel="noopener noreferrer"&gt;Database Flow Manager&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🚀 Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🔌 Dynamic node and connection management&lt;/li&gt;
&lt;li&gt;🧠 Event-driven architecture for nodes and links&lt;/li&gt;
&lt;li&gt;✏️ Full customization of templates and visuals&lt;/li&gt;
&lt;li&gt;🔍 Zooming, panning, snapping, and guide support&lt;/li&gt;
&lt;li&gt;🧱 Grouping and alignment with grid snapping&lt;/li&gt;
&lt;li&gt;📦 Compatible with SSR, standalone components, Angular Signals, and zoneless Angular&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📘 Jump To Series
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ Part 1: Introduction to Foblex Flow (you are here)&lt;/li&gt;
&lt;li&gt;✅ &lt;a href="https://dev.to/shuzarevich/design-node-based-interfaces-in-angular-a-beginners-guide-with-foblex-flow-356h"&gt;Part 2: Creating Your First Node Flow&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the meantime, check out the official documentation or explore the examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  🙌 Thanks for Your Interest
&lt;/h2&gt;

&lt;p&gt;If you like the project — leave a ⭐ on &lt;a href="https://github.com/Foblex/f-flow" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, join the discussions, and share your feedback!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>lowcode</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Creating workflow editor with Angular</title>
      <dc:creator>Siarhei Huzarevich</dc:creator>
      <pubDate>Mon, 15 Jul 2024 12:22:43 +0000</pubDate>
      <link>https://dev.to/shuzarevich/creating-workflow-editor-with-angular-5dkn</link>
      <guid>https://dev.to/shuzarevich/creating-workflow-editor-with-angular-5dkn</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/Foblex/f-flow-example" rel="noopener noreferrer"&gt;Demo&lt;/a&gt; &lt;br&gt;
&lt;a href="https://flow.foblex.com" rel="noopener noreferrer"&gt;Library&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial, we will look at the process of creating a visual flow call editor using Angular and the &lt;a href="https://flow.foblex.com" rel="noopener noreferrer"&gt;@foblex/flow&lt;/a&gt; library.&lt;/p&gt;

&lt;p&gt;The first step is to create a new Angular project:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

ng new call-workflow-editor


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To work with a graph, install &lt;a href="https://flow.foblex.com" rel="noopener noreferrer"&gt;@foblex/flow&lt;/a&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npm &lt;span class="nb"&gt;install&lt;/span&gt; @foblex/flow


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Defining Models
&lt;/h2&gt;

&lt;p&gt;To manage the components of a workflow, it is necessary to define models:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IFlowModel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;INodeModel&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IConnectionModel&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;INodeModel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;input&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ENodeType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IConnectionModel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Workflow elements configuration
&lt;/h2&gt;

&lt;p&gt;To control the behaviour of our visual workflow call editor, we introduce an enumeration of node types (ENodeType). Our application will use five main types of nodes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Incoming Call&lt;/strong&gt;: designed for incoming calls. This is the first node that is activated when a call is received.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Play Text&lt;/strong&gt;: Allows you to play a preset text to the caller. Can be used for autoresponders or informational messages.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User Input&lt;/strong&gt;: Waits for input from the user, such as pressing buttons on a phone to navigate through a menu.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;To Operator&lt;/strong&gt;: forwards the call to an operator. Used to contact a live operator if automated options are not suitable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disconnect&lt;/strong&gt;: Ends the call. Can be used after all necessary actions have been completed or if the caller has decided to end the call.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each node type is assigned unique settings, including name, icon, color, and number of outputs. This allows you to visually distinguish nodes from each other and configure their functionality:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;ENodeType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;IncomingCall&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;incoming-call&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;PlayText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;play-text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;UserInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ToOperator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;to-operator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Disconnect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disconnect&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NODE_MAP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;ENodeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;IncomingCall&lt;/span&gt; &lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Incoming call&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add_call&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#39b372&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;ENodeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UserInput&lt;/span&gt; &lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User Input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;call_log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#2676ff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;ENodeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PlayText&lt;/span&gt; &lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Play text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wifi_calling_3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#AF94FF&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;ENodeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ToOperator&lt;/span&gt; &lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;To operator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wifi_calling_3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ffb62a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;ENodeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Disconnect&lt;/span&gt; &lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Disconnect&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;phone_disabled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ff859b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Editor component
&lt;/h2&gt;

&lt;p&gt;Let's create a component for the visual editor, which will be responsible for visualization and interaction with the workflow:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

ng generate component workflow-editor


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In this component we will define the workflow display logic and user interaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flow visualization
&lt;/h2&gt;

&lt;p&gt;Displaying call flows is a key part of the editor. To do this, we use the components of the &lt;a href="https://github.com/Foblex/f-flow" rel="noopener noreferrer"&gt;@foblex/flow&lt;/a&gt; library:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;workflow-editor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./workflow-editor.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./workflow-editor.component.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WorkflowEditorComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;flow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IFlowModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="na"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;eConnectableSide&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;EFConnectableSide&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;cBehavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EFConnectionBehavior&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;EFConnectionBehavior&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FIXED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;cType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EFConnectionType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;EFConnectionType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SEGMENT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

@if(flow) {
  &lt;span class="nt"&gt;&amp;lt;f-flow&lt;/span&gt; &lt;span class="na"&gt;fDraggable&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;f-canvas&lt;/span&gt; &lt;span class="na"&gt;fZoom&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          @for (connection of flow.connections;track connection.key) {
            &lt;span class="nt"&gt;&amp;lt;f-connection&lt;/span&gt; &lt;span class="na"&gt;[fBehavior]=&lt;/span&gt;&lt;span class="s"&gt;"cBehavior"&lt;/span&gt;
                          &lt;span class="na"&gt;[fType]=&lt;/span&gt;&lt;span class="s"&gt;"cType"&lt;/span&gt;
                          &lt;span class="na"&gt;[fOutputId]=&lt;/span&gt;&lt;span class="s"&gt;"connection.from"&lt;/span&gt; &lt;span class="na"&gt;[fInputId]=&lt;/span&gt;&lt;span class="s"&gt;"connection.to"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/f-connection&amp;gt;&lt;/span&gt;
          }
          @for (node of flow.nodes;track node.key) {
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;fNode&lt;/span&gt; &lt;span class="na"&gt;fNodeInput&lt;/span&gt; &lt;span class="na"&gt;[fInputId]=&lt;/span&gt;&lt;span class="s"&gt;"node.input"&lt;/span&gt;
                 &lt;span class="na"&gt;[fInputDisabled]=&lt;/span&gt;&lt;span class="s"&gt;"!node.input"&lt;/span&gt;
                 &lt;span class="na"&gt;[fInputConnectableSide]=&lt;/span&gt;&lt;span class="s"&gt;"eConnectableSide.TOP"&lt;/span&gt;
                 &lt;span class="na"&gt;[fNodePosition]=&lt;/span&gt;&lt;span class="s"&gt;"node.position"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;{{ node.name }}&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
              @for (output of node.outputs;track output) {
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;fNodeOutput&lt;/span&gt; &lt;span class="na"&gt;[fOutputId]=&lt;/span&gt;&lt;span class="s"&gt;"output"&lt;/span&gt; &lt;span class="na"&gt;[fOutputConnectableSide]=&lt;/span&gt;&lt;span class="s"&gt;"eConnectableSide.BOTTOM"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
              }
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
          }
      &lt;span class="nt"&gt;&amp;lt;/f-canvas&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/f-flow&amp;gt;&lt;/span&gt;
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will allow us to display the flow, but we need to add the ability to add nodes and connections, since we have nothing to display yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding new nodes
&lt;/h2&gt;

&lt;p&gt;To make our visual flow call editor more interactive and functional, we provide the user with the ability to add new nodes. This allows you to create more complex and varied call processing scenarios.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;possibleNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;NODE_MAP&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;NODE_MAP&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;onCreateNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FCreateNodeEvent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputsCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NODE_MAP&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;outputsCount&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NODE_MAP&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;generateId&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's add this next to the flow component and add an event to the flow component:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

@for (item of possibleNodes;track item) {
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;fExternalItem&lt;/span&gt;
          &lt;span class="na"&gt;[style.color]=&lt;/span&gt;&lt;span class="s"&gt;"item.color"&lt;/span&gt;
          &lt;span class="na"&gt;[fData]=&lt;/span&gt;&lt;span class="s"&gt;"item.type"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    {{ item.icon }}
  &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
}
@if(flow) {
   &lt;span class="nt"&gt;&amp;lt;f-flow&lt;/span&gt; &lt;span class="na"&gt;(fCreateNode)=&lt;/span&gt;&lt;span class="s"&gt;"onCreateNode($event)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            ...flow content
   &lt;span class="nt"&gt;&amp;lt;/f-flow&amp;gt;&lt;/span&gt;
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Users can select nodes from a list and drag them onto the editor workspace using the fExternalItem directive. This action initiates the creation of a new node in the flow with the appropriate visualization and functionality settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkk9m25l5z188d5hhiktu.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkk9m25l5z188d5hhiktu.gif" alt="Create nodes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Editing connections
&lt;/h2&gt;

&lt;p&gt;After adding nodes to the flow, the next important step is creating connections between them. Connections determine the logic of transition from one node to another and form the final flow of call processing.&lt;/p&gt;

&lt;p&gt;To edit and create new connections, we have provided the ability to visually interact with node components:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;f-flow&lt;/span&gt; &lt;span class="na"&gt;(fCreateConnection)=&lt;/span&gt;&lt;span class="s"&gt;"onCreateConnection($event)"&lt;/span&gt;
        &lt;span class="na"&gt;(fReassignConnection)=&lt;/span&gt;&lt;span class="s"&gt;"onReassignConnection($event)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;f-canvas&lt;/span&gt; &lt;span class="na"&gt;fZoom&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;f-connection-for-create&amp;gt;&amp;lt;/f-connection-for-create&amp;gt;&lt;/span&gt;
    ...flow content
  &lt;span class="nt"&gt;&amp;lt;/f-canvas&amp;gt;&lt;/span&gt;     
&lt;span class="nt"&gt;&amp;lt;/f-flow&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;onCreateConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FCreateConnectionEvent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IConnectionViewModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fOutputId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fInputId&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;onReassignConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FReassignConnectionEvent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fOutputId&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;oldFInputId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newFInputId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This allows users to easily modify an existing flow by adding new logical connections or changing existing ones. This approach makes the process of setting up flow calls flexible and intuitive.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1v3a97xqx0ujq2xh2jt9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1v3a97xqx0ujq2xh2jt9.gif" alt="Connect nodes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Foblex/f-flow-example" rel="noopener noreferrer"&gt;Example&lt;/a&gt; &lt;a href="https://github.com/Foblex/f-flow" rel="noopener noreferrer"&gt;Library&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>foblex</category>
      <category>flow</category>
      <category>grapheditor</category>
    </item>
  </channel>
</rss>
