Creating a custom decision tree in Angular/JS/TypeScript: A step-by-step guide

My current project involves designing a user interface that enables users to develop a decision tree through drag-and-drop functionality. I am considering utilizing GoJS, as showcased in this sample: GoJS IVR Tree. However, I am facing challenges in figuring out how to achieve the following tasks:

  • Enabling users to create and delete connections between nodes,
  • Adding or removing nodes,
  • Incorporating choices within the nodes.

I would greatly appreciate any guidance or examples that can help me navigate through these functionalities efficiently. Thank you in advance!

Answer №1

Check out this simple decision-tree editor created using GoJS: https://i.stack.imgur.com/5owGP.png

For the full source code, refer to:

<!DOCTYPE html>
<html>
<head>
  <title>Minimal Decision Tree Editor</title>
  <!-- Copyright 1998-2023 by Northwoods Software Corporation. -->
</head>
<body>
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:500px"></div>
  Choosing a node displays various buttons, including a button for adding a new child node and link.
  <textarea id="mySavedModel" style="width:100%;height:250px"></textarea>

  <script src="https://unpkg.com/gojs"></script>
  <script id="code">
const $ = go.GraphObject.make;

const myDiagram =
  new go.Diagram("myDiagramDiv",
    {
      // move or delete entire subtrees
      "draggingTool.dragsTree": true,
      "commandHandler.deletesTree": true,
      layout:
        $(go.TreeLayout,
          { layerSpacing: 100, setsPortSpot: false, setsChildPortSpot: false }),
      "undoManager.isEnabled": true,
      "ModelChanged": e => {
        if (e.isTransactionFinished) {
          document.getElementById("mySavedModel").textContent = e.model.toJson();
        }
      }
    });

// Define the node template
myDiagram.nodeTemplate =
  $(go.Node, "Auto",
    // prevent deletion of tree root nodes
    new go.Binding("deletable", "", node => !!node.findTreeParentNode()).ofObject(),
    $(go.Shape, "RoundedRectangle",
      { fill: "tomato", stroke: "lightgray" },
      new go.Binding("fill", "isSelected", s => s ? go.Brush.darken("tomato") : "tomato").ofObject()),
    $(go.Panel, "Table",
      { margin: 6 },
      $(go.TextBlock,
        {
          row: 0, name: "Title",
          stroke: "white", font: "bold 11pt sans-serif",
          margin: new go.Margin(0, 0, 4, 0), editable: true
        },
        new go.Binding("text", "title").makeTwoWay()),
      $(go.TextBlock,
        {
          row: 1, name: "Description",
          stroke: "white", editable: true
        },
        new go.Binding("text", "description").makeTwoWay()),
    )
  );

// Define the selection adornment template for nodes
myDiagram.nodeTemplate.selectionAdornmentTemplate =
  $(go.Adornment, "Spot",
    $(go.Placeholder, { margin: 12 }),
    $("Button",
      $(go.Shape, { geometryString: "M12 0 L3 9 M2 10 L0 12", strokeWidth: 3 }),
      {
        alignment: new go.Spot(0, 0, -5, 15),
        click: (e, button) => e.diagram.commandHandler.editTextBlock(button.part.adornedPart.findObject("Title"))
      }),
    // Additional buttons for editing description and expanding/collapsing subtree
  );

// Define the link template
myDiagram.linkTemplate =
  $(go.Link,
    {
      selectable: false,
      click: (e, link) => e.diagram.commandHandler.editTextBlock(link.findObject("Text"))
    },
    $(go.Shape,
      { stroke: "gray", strokeWidth: 2 }),
    $(go.Panel, "Auto",
      $(go.Shape, "RoundedRectangle",
        { fill: "white", strokeWidth: 0 }),
      $(go.TextBlock, "choice",
        {
          name: "Text", maxSize: new go.Size(100, NaN),
          stroke: "gray", textAlign: "center", editable: true
        },
        new go.Binding("text").makeTwoWay())
    )
  );

// Create the model with nodes and links
myDiagram.model = new go.GraphLinksModel(
[
  { key: 1, title: "Decision 1", description: "Consider a, b, and c.\nDo you want to do X?" },
  { key: 2, title: "Decision 2", description: "Do you want to do Y?" },
  { key: 3, title: "Outcome 1", description: "The end" },
  { key: 4, title: "Outcome 1", description: "one solution" },
  { key: 5, title: "Outcome 2", description: "another solution" },
],
[
  { from: 1, to: 2, text: "Yes" },
  { from: 1, to: 3, text: "No" },
  { from: 2, to: 4, text: "Yes" },
  { from: 2, to: 5, text: "No" },
]);
  </script>
</body>
</html>

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

Executing a CRM javascript button triggers a request to a JSON URL and extracts a specific value

My current task involves creating a button in JavaScript due to system limitations preventing the use of HTML. This button should navigate to a specific URL (REST API to retrieve a JSON file). Furthermore, upon clicking the button, I aim to display an aler ...

Angular - How to fix the issue of Async pipe not updating the View after AfterViewInit emits a new value

I have a straightforward component that contains a BehaviorSubject. Within my template, I utilize the async pipe to display the most recent value from the BehaviorSubject. When the value is emitted during the OnInit lifecycle hook, the view updates correc ...

Are there any risks associated with using nested setTimeout functions with the same name?

While reviewing the source code of typed.js, I noticed that the main function in this plugin utilizes a design pattern with multiple setTimeout functions nested inside one another. Here is a snippet of the code: self.timeout = setTimeout(function() { / ...

Typescript fails to recognize the imported variable within a generic type declaration

I am in the process of developing a versatile repository that can be used for every entity within the application. mongo-repository.ts import { Document, Model, Types } from 'mongoose'; type MongooseModel<T> = Model<T & Document&g ...

Trigger the change-event by hand

Hey there, I'm facing an issue with manually triggering a change event. Currently, I have a selectOneMenu (similar to a dropdown in JSF) with various values. When I select a value from this dropdown list, a datatable is supposed to be updated. This ...

Height not being applied to DIV container

I'm facing an issue with my CSS code that is causing images to break the row after reaching the end of the DIV. However, the container behind it is not adjusting its height according to the images. The height should expand along with the images. Here ...

Swap out the image backdrop by utilizing the forward and backward buttons

I am currently working on developing a Character Selection feature for Airconsole. I had the idea of implementing this using a Jquery method similar to a Gallery. In order to achieve this, I require a previous button, a next button, and the character disp ...

Ensure data is only fetched in Ngrx if the store is empty

In my application, there are a total of 3 pages: Faculties, Groups, and Specialties. Here is how the interface for these entities is structured: interface Faculty { faculty_id: number; faculty_name: string; faculty_description: string; } interface S ...

A guide on how to activate an event when a radio button is selected using jQuery

I experimented with implementing this. When I try clicking on the radio button, the code functions correctly, but the radio button does not appear to be checked and remains unchanged. Any ideas on how to resolve this issue? <p> <input id="p ...

Issue encountered when setting a background image in Angular

When trying to add a background image and set it to cover the entire browser window, I encountered an issue where the image appeared very small and did not fully cover the background. As someone new to Angular, my understanding of how this process works i ...

The focus is being lost on the ng-model when used within an ng-repeat

Within my JavaScript code, I am initiating a new question object like this: $scope.newQuestion = { answers: [] }; This is how it reflects in the HTML: <div class="row" ng-repeat="answer in newQuestion.answers"> <div class="input-field col m ...

Troubles with Custom Control Component: ControlValueAccessor and Validator Out of Sync with Form Group

Background: My custom email control component, EmailControlComponent, is designed to implement both ControlValueAccessor and Validator. The validate() method of this component takes a single parameter of type AbstractControl. As specified in Angular' ...

Rx.js struggles to access historical values

Seeking assistance with retrieving the last 3 values emitted. Despite using the provided code to populate uiOrder and invoking cancelOrderItem() multiple times, I am unable to access the last 3 revisions of the order via getHistory(). Instead, I receive th ...

Using PHP to display a message box and redirecting to a different webpage

Currently, I am working on developing a login page using PHP. When the user enters an incorrect username/password combination, I display a JavaScript alert box to notify them. However, after clicking the "OK" button on the alert box, I want to redirect t ...

Are there alternative approaches to launching processes aside from the functions found in child_process?

Having trouble with NodeJS child_process in my specific use case. Are there other methods available to start processes from a node.js application? So far, Google has only been pointing me towards child_process for all of my inquiries. Edit I am looking to ...

Is it possible to include ternary in the "homepage" section of the package.json file?

When making API calls in production, they will be on the same domain as where my app is hosted, so it's necessary to include "homepage": "." in the package.json. However, when working locally from localhost, I need to make API calls to the production ...

Incorporate a new style into several previous slides using the Slick carousel feature

I'm attempting to utilize the Slick carousel to create a timeline. My goal is for the previous slides to remain colored as you progress through the timeline, and turn grey when you go back. I've tried using onAfterChange and onBeforeChange, but I ...

The Link Element Does Not Appear Properly When Styled Using nth-of-type Selector

https://codesandbox.io/s/damp-worker-k7fj6y?file=/src/App.js Can anyone help me understand why the fourth .content <Link/> is not displaying when using the given CSS styling? Could it be a bug in the code, or am I overlooking something important? ...

Generating a list of objects from an array of strings

I am currently facing an issue in my code and I could use some help with it. Below are the details: I have a array of string values containing MAC addresses and constant min & max values. My goal is to map over these MAC values and create an array of obje ...

React SVG not displaying on page

I am facing an issue with displaying an SVG in my React application. Below is the code snippet: <svg className="svg-arrow"> <use xlinkHref="#svg-arrow" /> </svg> //styling .user-quickview .svg-arrow { fill: #fff; position: ...