DEV Community

Cover image for Vue Flow Quickstart and Best Practices
MonsterPi13
MonsterPi13

Posted on

Vue Flow Quickstart and Best Practices

workflow demo

📝 Preface

The immense popularity of large language models (LLMs) in 2023 looks set to continue this year. Following OpenAI's groundbreaking releases of GPT-3 and subsequent iterations, LLMs have become the cutting edge in natural language processing. Numerous tech firms and developers are racing to bring their own LLM offerings to market, capitalizing on intense interest in this burgeoning field. Harnessing the power of LLMs appears to be the wise course forward for those seeking to remain at the forefront of AI capabilities. 🤖

My employer has opted to develop its own product in this sphere, integrating proprietary APIs across the organization. The workflow chart constitutes the crux of this endeavor, empowering users to tailor personalized processes. In the following article, I will illuminate the pivotal steps to constructing such workflow diagrams within web interfaces. It is my sincere aspiration that these insights provide some modicum of assistance to readers pursuing similar objectives. 🙏

📚 Finding the Right Workflow Library

At my present employer, the predominant front-end technology stack comprises Vue3 and TypeScript. Consequently, I sought out a workflow library grounded in Vue3 and ideally authored in TypeScript. Despite utilizing LogicFlow previously, its integration with Vue3 proved insufficiently seamless.Therefore, for this demonstration, I opted for VueFlow, which boasts exceptional support for Vue3.

⚙️ Core Capabilities to Implement

  1. 🖱 Drag & Drop Elements to Create New Nodes
  2. 🎨 Customizing Nodes
  3. ➡️ Connect Nodes with Drag-Based Edges
  4. 📤 Pass Data Between Nodes
  5. 💾 Save/Load Workflow State

🖱 Drag & Drop Elements to Create New Nodes

Set the element draggable and add dragstart event handler:

<script setup>
function handleDragStart(event, nodeType) {
  event.dataTransfer.setData("node", nodeType);
}
</script>

<template>
  <div 
    draggable 
    @dragstart="handleDragStart($event, 'someNode')">
    ...
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Define drop zone with ondragover and ondrop handlers:

<template>
  <div
    @drop="handleDrop"
    @dragover="handleDragOver">

    <VueFlow>
      ...
    </VueFlow>

  </div>  
</template>

<script>
function handleDrop(event) {
  // Create node from dragged data
  const nodeType = event.dataTransfer.getData('node') 
  const newNode = {
    type: nodeType,
    ...
  }

  addNode(newNode)
}

function handleOnDragOver(event) {
  event.preventDefault();

  if (event.dataTransfer) {
    event.dataTransfer.dropEffect = "move";
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

This enables dragging elements into the flowchart canvas to create new nodes programmatically. 💡

As the code above shows, we can do our business logic in the drop event handler. In this demo, we just add our custom node to the workflow canvas with addNodes. By the way, we can use the useVueFlow hook provided by VueFlow to conveniently add/delete nodes & edges in our app.

🎨 Customizing Nodes

Two approaches exist for defining customized nodes. The first employs template slots, whereby dynamic resolution furnishes a slot denominated #node-custom when the custom type is anticipated. The second leverages the node-types prop. In my estimation, the latter approach promotes superior organization. Thus, I opted to utilize the node-types technique for node definition.

Constructing a bespoke node is remarkably straightforward - simply codify the node UI as an ordinary Vue component. This convenience is quite remarkable.

<script setup lang="ts">
import StartNode from '@/components/vue-flow/nodes/start-node.vue'
import EndNode from '@/components/vue-flow/nodes/end-node.vue'
import LLMNode from '@/components/vue-flow/nodes/LLM-node.vue'
import CodeNode from '@/components/vue-flow/nodes/code-node.vue'
import KnowledgeNode from '@/components/vue-flow/nodes/knowledge-node.vue'
import ApiNode from '@/components/vue-flow/nodes/api-node.vue'

  const nodeTypes = {
    start: markRaw(StartNode),
    end: markRaw(EndNode),
    LLM: markRaw(LLMNode),
    code: markRaw(CodeNode),
    knowledge: markRaw(KnowledgeNode),
    api: markRaw(ApiNode)
  }
</script>

<template>
  <div class="relative h-full w-full">
    <VueFlow :node-types="nodeTypes">
    </VueFlow>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

➡️ Connect Nodes with Drag-Based Edges

  1. Employ the onConnect hook, invoked upon establishing a connection between two nodes.
  2. Within the callback, call addEdges to forge an edge linking the nodes.
  3. When instantiating the novel node, the isValidTargetPos attribute can be defined to constrain permissible connections to specific target nodes.
const { onConnect, addEdges } = useVueFlow()
onConnect((params) => {
  addEdges(params)
})
Enter fullscreen mode Exit fullscreen mode

Automatically connect nodes on drag.

📤 Pass Data Between Nodes

Our envisioned workflow platform necessitates obtanining data from preceding nodes and replaying it to subsequent nodes. This imposes the requirement of devising a technique to retrieve a node's antecedent data.

The current node's attributes incorporate a connectedEdges field. By filtering for connected nodes where the current node constitutes the target node type, we can identify the direct predecessor. The findNode function can then be invoked upon that node to extract its bound data for passage downstream.

This leverages existing node properties and methods to fulfill the prerequisite of data sharing between ajacent nodes in the workflows.

import { useNode, useVueFlow } from "@vue-flow/core";

const node = useNode();
const { findNode } = useVueFlow();
watchEffect(() => {
  if (node.connectedEdges && node.connectedEdges.value.length > 0) {
    const filteredEdges = node.connectedEdges.value.filter(
      (item) => item.target === node.id
    );
    referenceOptions.value = filteredEdges.map((edge) => {
      const node = findNode(edge.source);
      const currentItem: Option = {
        groupName: node?.data.title ?? node?.label,
        options: [],
      };

      if (node?.data.output) {
        node?.data.output
          .filter((item: any) => Boolean(item.name))
          .forEach((option: any) => {
            currentItem.options.push({
              label: option.name,
              value: option.name,
            });
          });
      } else {
        currentItem.options = [];
      }
      return currentItem;
    });
  }
});
Enter fullscreen mode Exit fullscreen mode

💾 Save/Load Workflow State

To enable integration with back-end APIs, persisting the current workflow state is requisite. Retrieving the diagram data proves straightforward - simply invoke the toObject method form the useVueFlow hook. Similarly, restoring a saved workflow is readily achievable by applying the appropriate utilities furnished by the hook.

Leveraging the capabilities surface by useVueFlow provides a clean and convenient approach to serializing and deserializing the workflow for server communication and storage.

For illustration, the workflow data can be obtained, persisted, and restored as follows:

// Retrieve workflow data
const data = toObject(workflow); 

// Save data to backend
saveWorkflow(data);

// Reconstruct workflow from data
setNodes(data.nodes);
setEdges(data.edges);
Enter fullscreen mode Exit fullscreen mode

Through this approach, the current workflow state can be serialized and sent to the backend for storage. Later, it can be fetched and deserialized to recreate the diagram programmatically by re-instantiating the nodes and edges.

This enables full persistence of workflows to facilitate saving progress and resuming from previous sessions. The useVueFlow hook provides convenient utility methods to capture and restore the diagram state.

Additional Beneficial Capabilities

  • Copying, deleting, and otherwise modifying nodes through handy utility functions.

VueFlow furnishes abundant helper methods to effortlessly copy, remove, or otherwise alter nodes within the workflow diagram. This greatly simplifies tasks like duplicating portions of the workflow.

  • Supplementary plugins for expanded functionality.

A range of optional VueFlow plugins exist that introduce additional capabilities including minimaps, grid backgrounds, node resizer, and more. These afford further ways to customize the environment to suit specific needs.

In summary, VueFlow comes well-equipped with built-in utilities and an ecosystem of plugins to enable advanced workflows and streamlined customization beyond core dragging and connecting of nodes.

🏁 Conclusion

At its core, constructing a web-based flowchart entails thoughtfully organizing components into a cohesive application. The crux lies with the components themselves, irrespective of the framework such as React or Vue. The process simply involves adhering to documentation, logically structuring components, and conveniently adding/removing them as needed. I hope these insights offer useful pointers on building flowchart tools.

Through creating this demo, I've gained significant clarity around implementing web apps centered on flowcharts. The heart of such applications remains the components - whether using React, Vue, or any other JavaScript framework. It's a matter of fitting components into the framework's architecture. This principle persists unchanged; the steps are to comprehend documentation, keep the framework tidy, and seamlessly inject or remove components. I hope this article📚 provides helpful tips on flowchart apps. Thank you for reading - have an excellent day!😃


🚀 Live Demo: https://chat-bot-workflow.vercel.app/
📚 Original Article: https://www.monsterpi13.dev/writing/vue-flow-quickstart-and-best-practices
📂 GitHub Repo: https://github.com/MonsterPi13/chatBot-workflow

Top comments (2)

Collapse
 
helel1905 profile image
Tom Ellis

你好博主,我是国内的开发者,很高兴能在此看到你的文章,整篇文章对我收获颇多。我目前的公司也是从事AI领域相关应用的开发,我本身也是要一名web开发者。希望再次看到你的文章和项目,祝君好。

Collapse
 
monsterpi13 profile image
MonsterPi13

谢谢鼓励,很开心能对你有帮助。