Custom nodes cannot be connected via drag and drop at this time

Struggling with connecting custom nodes while utilizing the vueflow package and its example functionalities like drag-and-drop & custom nodes. Some components are from Vuetify.

The StartNode renders as expected, and dragging the InputNode into VueFlow functions correctly. However, issues arise when trying to connect these nodes. One specific error occurs when dragging from StartNode to InputNode:

Error: <path> attribute d: Expected number

No error is displayed when dragging from InputNode to StartNode, but it results in a new "default node" being rendered instead.

I am seeking guidance on how to properly connect these custom nodes.

Main file:

<template>
    <v-row>
        <v-col cols="9" style="height: 500px" @drop="onDrop">
            <VueFlow
                v-model:nodes="nodes"
                v-model:edges="edges"
                :node-types="types"
                :connection-mode="ConnectionMode.Strict"
                @dragover="onDragOver"
                @dragleave="onDragLeave"
            />
        </v-col>

        <v-col cols="3">
            <InputNode
                :draggable="true"
                @dragstart="onDragStart( $event, 'input' )"
            />
        </v-col>
    </v-row>
</template>

<script setup>
import { ref, markRaw } from 'vue';
import { VueFlow, useVueFlow, ConnectionMode } from '@vue-flow/core';
import useDragAndDrop from './drag-n-drop.js';
import StartNode from './nodes/StartNode.vue';
import InputNode from './nodes/InputNode.vue';

const { onConnect, addEdges } = useVueFlow();

const { onDragOver, onDrop, onDragLeave, onDragStart } = useDragAndDrop()

const nodes = ref( [
    {
        id: 'start-node',
        type: 'start',
        position: { x: 0, y: 50 },
        dimensions: { width: '150px', height: '50px' },
    },
] );

const edges = ref( [] );

const types = {
    start: markRaw( StartNode ),
    input: markRaw( InputNode ),
};

onConnect( addEdges );
</script>

<style>
@import '@vue-flow/core/dist/style.css';
@import '@vue-flow/core/dist/theme-default.css';
</style>

The StartNode:

<template>
    <div>
        <v-card color="green" :width="props.dimensions?.width ?? '150px'" :height="props.dimensions?.height ?? '50px'">
            <v-card-text>Start</v-card-text>

        </v-card>
        <Handle id="start" type="source" :position="Position.Right" :connectable="handleConnectable" />
    </div>
</template>

<script setup>
import { Position, Handle } from '@vue-flow/core';

const props = defineProps( {
    id: {
        type: String,
        required: true,
    },
    data: {
        type: Object,
        required: true,
    },

    dimensions: {
        type: Object,
        required: false,
    },
} );

function handleConnectable( node, connectedEdges )
{
    return connectedEdges.length <= 1;
}
</script>

The InputNode:

<template>
    <div
    >
        <Handle v-if="data" id="input-target" type="target" :position="Position.Right" :connectable="handleConnectable" />

        <v-card
            :draggable="props.draggable"
            :width="'300px'"
            :height="'104px'"
        >

            <v-card-title>Input</v-card-title>

            <v-card-text>
                <v-text-field
                    label="Input"
                    outlined
                    hide-details
                    density="compact"
                />
            </v-card-text>

        </v-card>

        <Handle v-if="data" id="input-source" type="source" :position="Position.Left" :connectable="handleConnectable" />
    </div>
</template>

<script setup>
import { Handle, Position } from '@vue-flow/core';

const props = defineProps( {
    id: {
        type: String,
        required: false,
    },

    data: {
        type: Object,
        required: false,
    },

    dimensions: {
        type: Object,
        required: false,
    },

    draggable: {
        type: Boolean,
        required: false,
        default: true,
    },
} );

function handleConnectable( node, connectedEdges )
{
    return connectedEdges.length <= 1;
}
</script>

The drag-n-drop.js (retrieved from the example):

import { useVueFlow } from '@vue-flow/core'
import { ref, watch } from 'vue'

let id = 0

/**
 * @returns {string} - A unique id.
 */
function getId() {
  return `dndnode_${id++}`
}

/**
 * In a real world scenario you'd want to avoid creating refs in a global scope like this as they might not be cleaned up properly.
 * @type {{draggedType: Ref<string|null>, isDragOver: Ref<boolean>, isDragging: Ref<boolean>}}
 */
const state = {
  /**
   * The type of the node being dragged.
   */
  draggedType: ref(null),
  isDragOver: ref(false),
  isDragging: ref(false),
}

export default function useDragAndDrop() {
  const { draggedType, isDragOver, isDragging } = state

  const { addNodes, screenToFlowCoordinate, onNodesInitialized, updateNode } = useVueFlow()

  watch(isDragging, (dragging) => {
    document.body.style.userSelect = dragging ? 'none' : ''
  })

  function onDragStart(event, type) {
    if (event.dataTransfer) {
      event.dataTransfer.setData('application/vueflow', type)
      event.dataTransfer.effectAllowed = 'move'
    }

    draggedType.value = type
    isDragging.value = true

    document.addEventListener('drop', onDragEnd)
  }

  /**
   * Handles the drag over event.
   *
   * @param {DragEvent} event
   */
  function onDragOver(event) {
    event.preventDefault()

    if (draggedType.value) {
      isDragOver.value = true

      if (event.dataTransfer) {
        event.dataTransfer.dropEffect = 'move'
      }
    }
  }

  function onDragLeave() {
    isDragOver.value = false
  }

  function onDragEnd() {
    isDragging.value = false
    isDragOver.value = false
    draggedType.value = null
    document.removeEventListener('drop', onDragEnd)
  }

  /**
   * Handles the drop event.
   *
   * @param {DragEvent} event
   */
  function onDrop(event) {
    const position = screenToFlowCoordinate({
      x: event.clientX,
      y: event.clientY,
    })

    const nodeId = getId()

    const newNode = {
      id: nodeId,
      type: draggedType.value,
      position,
      data: { label: nodeId },
    }

    /**
     * Align node position after drop, so it's centered to the mouse
     *
     * We can hook into events even in a callback, and we can remove the event listener after it's been called.
     */
    const { off } = onNodesInitialized(() => {
      updateNode(nodeId, (node) => ({
        position: { x: node.position.x - node.dimensions.width / 2, y: node.position.y - node.dimensions.height / 2 },
      }))

      off()
    })

    addNodes(newNode)
  }

  return {
    draggedType,
    isDragOver,
    isDragging,
    onDragStart,
    onDragLeave,
    onDragOver,
    onDrop,
  }
}

Answer №1

One of the key problems identified was a mix-up in the source and target locations for the handle.

An additional issue found within the StartNode component was related to the dynamic sizing of width and height.

Last but not least, an error detected on the InputNode was caused by the incorrect usage of

:draggable="props.draggable"
on the v-card, resulting in unexpected default node additions.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Determining the height of dynamically rendered child elements in a React application

Looking for a way to dynamically adjust the heights of elements based on other element heights? Struggling with getting references to the "source" objects without ending up in an infinite loop? Here's what I've attempted so far. TimelineData cons ...

Tips for moving a texture horizontally across a sphere using threejs

I have a 360 degree viewer tool. Is there a way to load a texture in a specific position that will rotate the original image by a certain number of degrees or units around the Y-axis without altering anything else about how the picture is displayed? If s ...

JavaScript files and the power of jQuery AJAX

If I have a .js file with the code snippet below, how can I use a jQuery AJAX method to call it? This file resembles a JSON file in terms of syntax, which is causing some confusion for me. Is the JSON.stringify() method necessary for this task? Although ...

The mvc controller encountered a problem as it was unable to find the functionality for res

I encountered an error in my controller file with the following code snippet. It keeps saying that res.status is not a function. Has anyone else faced this issue before? Interestingly, the function works fine when I include it directly in my routes file. ...

After modifying the select option, the input field remains disabled

I successfully developed a self-contained code snippet that toggles the enable/disable state of input fields. It works flawlessly on my HTML page. Check it out below: Identification Type: <select name="Identification-Type" id="Identification-Type"& ...

Utilize Array.filter to retrieve specific values based on a defined number of elements

I have a function that currently retrieves the url value for each item in an array up to a specified maximum number. Below is the code snippet: const myArray = [ { url: "example.com/1", other: "foo" }, { url: "example.com/s ...

Submitting multiple forms with one button can produce unexpected results

As I work on my app development, I encountered an issue when trying to submit multiple forms using a single button. I applied the same class to all the forms and looped through them to submit. While this approach worked smoothly on my localhost, it seems ...

Exploring the exciting possibilities with Bootstrap-vue-next and Vite

I am facing an issue with the build process. I have a modal component called b-modal that is used in Vuejs 3. When I run npm run dev for development, everything works fine. However, when I run npm run prod to build assets for production, the modal stops wo ...

Adjusting the size of objects in Three.js programaticallyAnswer: "Mod

Currently, I am utilizing the dat.gui library with three.js to allow users to interact with an object and adjust the dimensions of its mesh. While many online examples use scale to modify mesh dimensions like this: mesh.onChange(function(value){mesh.scale ...

Determine the associated value for a given key within a TypeScript object

I have a structure like this: type newsItem = { img: string; slug: newsSlug; text: newsText; }; derived from an enum like this: export const newsEnum = { interesting: "Interesting", regions: "Regions", contradictory: " ...

Merge the properties of two class instances deeply

What is the best method for deep merging two instances of ES6 classes similar to lodash? The end result should be an instance of the same class with a deep merge of both instances' properties. ...

Utilize Vue.js to selectively display or manipulate objects with a

I am currently working on a Vue.js component page that receives data from my backend. The page consists of three different filters/states that users can choose from: Unlabeled, Completed, and Skipped. Unlablled Completed Skipped My approach involves usin ...

There seems to be an issue with the functionality of the `Nav` component in React-bootstrap when used within a `NavBar` with the

Desiring my NavBar to occupy the entire available width, I included a fill flag within the Nav section of the NavBar component. <Navbar bg="light" expand="lg"> <Navbar.Toggle aria-controls="basic-navbar-nav" /&g ...

Ensuring User Information Persistence in React Upon Successful Login

I am developing a small application using React with PHP as the back-end. Within my database, I have two types of users - admin and student. Upon user login, I store their information in session storage like this ( user: { username:'abcxyz123', r ...

How can you determine if an asynchronous function is actively running at the beginning of the "code"?

I am currently working on a project that requires access to a global context which can identify the function running at any given moment. While this task is straightforward with single-threaded synchronous functions that run from start to finish, async fun ...

Retrieve the current URL upon page load

I'm currently attempting to parse the URL in document.ready() so I can retrieve the id of the current page and dynamically populate it with content from an AJAX call. However, I've encountered an issue where 'document.URL' seems to refe ...

The value of the checkbox model in AngularJS is not defined

I'm encountering an issue where I am trying to send the value of a checkbox from my model to the server. Since the checkbox has not been interacted with on the form, Angular does not assign it a value and returns undefined when I request the value. B ...

Enhance User Experience by Implementing Event Listeners for Changing Link Visibility Using mouseover and getElementsBy

I am new to JavaScript and struggling to find a solution. Despite searching online for fixes, none of the solutions I've found seem to work for me. I have spent hours troubleshooting the issue but I must be missing something crucial. Can someone plea ...

Efficiently uploading multiple files using AJAX in conjunction with Codeigniter

Greetings! I'm attempting to utilize ajax and codeigniter to upload multiple images. As a beginner in ajax and jquery, I would greatly appreciate any assistance in identifying where I might be going wrong or missing something. Below is my controller ...

Saving drag and drop positions in Angular to the database and troubleshooting screen resizing problems

Currently, I'm working on an application that involves dragging and dropping a DIV onto an image. My goal is to save the X and Y coordinates of the dropped DIV to the database so that when the user returns, the position will remain the same. The chal ...