Creating dynamic HTML nodes in Vuejs diagram with HTML Button element using the Syncfusion

Hello Team,

I am a bit new to Syncfusion + Vue component, I am trying to implement the dynamic creation of the `HTML Nodes` within the `EJS Diagram` within my `Vuejs/Nuxtjs` application. All I want to do is create the `HTML Nodes` within the `EJS Diagram` when the user clicks on `Add Node` button. Each of the `HTML Node` should consist of a `Button` within it so I can perform some things when it is clicked.


I have done it in `Angular` but am unable to do the same within my `Vuejs/Nuxtjs` application. I have added my code to the `CodeSandbox`: https://codesandbox.io/s/infallible-meadow-prmp5?file=/pages/index.vue


Can you please help me out with this issue and let me know how can I create the nodes dynamically with `HTML Button` inside it within my `Vuejs/Nuxtjs` application?




I want to achieve something like this within my application:


Attachment: CPT21101213301920x930.gif_fe299607.zip

12 Replies 1 reply marked as answer

GG Gowtham Gunashekar Syncfusion Team October 13, 2021 02:25 PM UTC

 
 
Hi AB, 
 
On the further analysis of the shared sample, we found you have set the shape type of the “Html” and we have set the type as “HTML” and we need to set the HTML content into “content” property of the shape. We have added the suggested changes in the shared sample added below for your references. In the sample we have set the HTML content as the string template, and we can draw the HTML node using the drawing tool. We have added the documentation for the HTML shape for your references.  
 
Code snippet: 
  addNode () { 
      const ports = [ 
        { id: 'port1', offset: { x: 0, y: 0.5 }}, 
        { id: 'port2', offset: { x: 1, y: 0.5 }}, 
        { id: 'port3', offset: { x: 0.5, y: 0 } }, 
        { id: 'port4', offset: { x: 0.5, y: 1 } } 
      ] 
      const name = 'Event' + this.nodeCounter 
      const label = [{ text: name, offset: { x: 0.5, y: 0.06 } }] 
      const drawingshape = { 
        type: 'HTML', 
        content: `<div style="background-color: red;width: 100%;height: 100%;" class="text-center"> 
      <button class="btn btn-info btn-sm" @click='ShowFormDataModal($event)'> Event Info </button> 
      <div style="margin-left:45%">annotation</div> 
      </div>`, 
        value: { 
          select: this.nodeCounter 
        } 
      } 
      const node = { 
        shape: drawingshape, 
        ports:ports, 
        annotations:[{ content:"ssss", offset: { x: 0.5, y: 0.06 } }] 
      } 
      diagramInstance.drawingObject = node 
 
      // To draw an object once, activate draw once 
      diagramInstance.tool = DiagramTools.DrawOnce 
      diagramInstance.dataBind() 
      this.nodeCounter++ 
    }, 
 
 
Online sample for Multiple HTML template into diagram: https://ej2.syncfusion.com/vue/demos/#/bootstrap5/diagram/html-node.html 
 
Regards, 
Gowtham 



AB AB October 14, 2021 02:11 PM UTC

@Gowtham

Thanks a lot for your response. I have some more doubts regarding the HTML Node creation and usage. I would like to add a button in each of the created HTML Nodes (this is working correctly now) but when I click the button I want to trigger the method/function to perform some actions which it is unable to do so. I have created the method but when I click on HTML Node button nothing is performed.


Also, I would like to add some beautification aspects to my HTML Node, I do not want the Node to be transparent and it should have a border something like as shown in this main question. 


Can you please guide me through the documentation or provide some explanation using an example? 


Following is the code I have currently:

<template>

  <div>
    <div class="container-fluid">
      <div class="row">
        <div class="col-md-6">
          <button class="btn btn-primary btn-sm" @click="addNode()">
            Add Event
          </button> 
        </div>
      </div>
      <div class="row">
        <div class="col-sm-12">
          <div id="app">
            <ejs-diagram
              id="diagram"
              ref="diagramObj"
              :width="width"
              :height="height"
              style="display: block"
            />
          </div>
        </div>
      </div>
    </div>
  </div>
</template>


<script>
import Vue from 'vue'
import { DiagramPlugin, DiagramTools } from '@syncfusion/ej2-vue-diagrams'


Vue.use(DiagramPlugin)


let diagramInstance


export default {
  name: 'App',
  data () {
    return {
      width: '100%',
      height: '500px',
      nodes: [],
      connectorArray: [],
      nodeCounter: 0,
      connectorCounter: 0
    }
  },
  mounted () {
    diagramInstance = this.$refs.diagramObj.ej2Instances
  },
  methods: {
    // On click of the add node button add the node to diagram
    addNode () {
      const ports = [
        { name: 'port1', offset: { x: 0, y: 0.5 }, shape: 'circle' },
        { name: 'port2', offset: { x: 1, y: 0.5 }, shape: 'circle' },
        { name: 'port3', offset: { x: 0.5, y: 0 }, shape: 'circle' },
        { name: 'port4', offset: { x: 0.5, y: 1 }, shape: 'circle' }
      ]
      const name = 'Event' + this.nodeCounter
      // const label = [{ text: name, offset: { x: 0.5, y: 0.06 } }]
      const drawingshape = {
        type: 'HTML',
        content: `<div style="margin-top:15%;" class="text-center">
                    <button class="btn btn-info btn-sm" @click='showEventInfoModal($event)'> Event Info </button>
                  </div>`,
        value: {
          select: this.nodeCounter
        }
      }


      const node = {
        shape: drawingshape,
        ports,
        style: {
          strokeColor: 'black',
          strokeWidth: 3
        },
        annotations: [
          {
            content: name,
            offset: { x: 0.5, y: 0.06 }
          }
        ]
      }
      diagramInstance.drawingObject = node


      // To draw an object once, activate draw once
      diagramInstance.tool = DiagramTools.DrawOnce
      diagramInstance.dataBind()
      this.nodeCounter++
    },
    // On click of the EventInfo button within each node call the method to display modal and get info
    showEventInfoModal (event) {
      console.log('DISPLAY MODAL FOR INFO')
      console.log(event)
    }
  }
}
</script>
<style>
@import "@/node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>


SS Sivakumar Sekar Syncfusion Team October 15, 2021 02:10 PM UTC

Hi AB, 

We have added the sample link and a video link to demonstrate how to add a button that can perform some custom actions. While rendering the HTML content, we have to apply the style to the HTML content on the template itself. If we need to add any text to the HTML shapes means, we have to add it to the HTML content itself. In the added sample we have added the text inside the div and position the text using the CSS and render the HTML template inside the node. 



Regards,  
Sivakumar 



AB AB October 18, 2021 04:27 PM UTC

@Sivakumar


Thanks a lot for the sample code and video link. This is working as expected for me.


I have two small questions. 

  1. I would like to keep track of each Node that is added to the Diagram so I am creating a unique number and assigning it to each Node which will be added as an Annotation but they are not visible on these Nodes because maybe the template is coming from another Vuejs Component. Can you please let me know how can I add the Annotation to each of the created Nodes?
  2. I would like to keep track of which Node button the user has clicked. Is there a way to identify it? For example, if there are 10 Nodes then I would like to know which Nodes HTML button was clicked by the user. I am already assigning the name to each of the Nodes but when the button is clicked then how can I know which Node was clicked?


As you can see from the main question image I have the Node assigned with the Label -> Event1, similarly, I want to have for these Nodes. Currently I am using the Annotations but they are not visible on the HTML Node.


Following is the current code I have:

<ejs-diagram
id="diagram"
ref="diagramObj"
:width="width"
:height="height"
style="display: block"
:node-template="nodeTemplate"
:collection-change="diagramCollectionChange"
:source-point-change="connectorSourcePointChange"
:target-point-change="connectorTargetPointChange"
:text-edit="connectorTextChange"
/>

My Javascript:
// On click of the add node button add the node to diagram
addNode () {
const ports = [
{ name: 'port1', offset: { x: 0, y: 0.5 }, shape: 'circle', visibility: PortVisibility.Visible },
{ name: 'port2', offset: { x: 1, y: 0.5 }, shape: 'circle', visibility: PortVisibility.Visible },
{ name: 'port3', offset: { x: 0.5, y: 0 }, shape: 'circle', visibility: PortVisibility.Visible },
{ name: 'port4', offset: { x: 0.5, y: 1 }, shape: 'circle', visibility: PortVisibility.Visible }
]
const name = 'node' + this.nodeCounter
// const label = [{ text: name, offset: { x: 0.5, y: 0.06 } }]
const drawingshape = {
type: 'HTML',
value: {
select: this.nodeCounter
}
}

const node = {
addInfo: { name },
name,
shape: drawingshape,
ports,
style: {
fill: 'white',
strokeColor: 'black'
},
annotations: [
{
content: name,
offset: { x: 0.5, y: 0.06 }
}
],
constraints: NodeConstraints.Default & ~(NodeConstraints.OutConnect | NodeConstraints.InConnect)
}
diagramInstance.drawingObject = node

// To draw an object once, activate draw once
diagramInstance.tool = DiagramTools.DrawOnce
diagramInstance.dataBind()

// Store Node information in Node Array
this.nodeCounter++
const nodeObj = {}
nodeObj.name = name
this.nodeArray.push(nodeObj)
},


AB AB October 19, 2021 10:18 AM UTC

@Sivakumar


I am really hoping to get some suggestions from your side as I am struck at the above-mentioned 2 issues. Any input or workaround from your side will be really useful for me. Thanks a lot in advance.


Thanks and Best regards,



SS Sivakumar Sekar Syncfusion Team October 19, 2021 12:31 PM UTC

Hi AB, 
 
Please find the response for queries in below table 
I would like to keep track of each Node that is added to the Diagram so I am creating a unique number and assigning it to each Node which will be added as an Annotation but they are not visible on these Nodes because maybe the template is coming from another Vuejs Component. Can you please let me know how can I add the Annotation to each of the created Nodes? 
As we suggested before, if we need to apply annotations to the HTML nodes we have to add it to the template itself as shown in the below code.  
<div id="app" class="container"> 
  <button ref="Btn" class="btnhtml" :value= "$data.data.annotations[0].content" @click="logClicked">EventInfo</button> 
  <p> {{$data.data.annotations[0].content}}</p> 
</div> 
 
 
I would like to keep track of which Node button the user has clicked. Is there a way to identify it? For example, if there are 10 Nodes then I would like to know which Nodes HTML button was clicked by the user. I am already assigning the name to each of the Nodes but when the button is clicked then how can I know which Node was clicked? 
We can assign the unique value to the button and can able to trigger the specified action based on the unique value of the button. Please refer to the below code to add nodes value to the template.  
<button ref="Btn" class="btnhtml" :value= "$data.data.annotations[0].content" @click="logClicked">EventInfo</button> 
 
 
methods: { 
    logClicked (value) { 
      alert(value.target.value); 
    } 
  }, 
 
 
Please find the sample link below to add the annotation to the HTML template and custom click events  
 
 
Regards, 
Sivakumar 
 



AB AB October 19, 2021 01:00 PM UTC

@Sivakumar


Thanks a lot for the detailed explanation and for providing the sample code. I am able to understand how we are achieving this. 


I am just curious isn't there a way to include the NodeTemplate within the EJSDiagram component that will make managing some of these things a bit easier I feel. As of now, we are using the external NodeTemplate and including it for every Node. Just wanted to know if there is a way to avoid the NodeTemplate.


Because when I implemented something similar within the AngularJS application we were using something like this, which was adding the Button inside each created Node due to which were able to know which Node being clicked using the DiagramInstance:


<script ng-model="NodeEvents" id="htmlTemplate" type="text/ng-template">
<div style="margin-top:15%;" class="text-center">
<button class="btn btn-info btn-sm" ng-click='$parent.ShowFormDataModal($event);'> Event Info </button>
</div>
</script>

Just wanted to know if there is something similar even in Vuejs?



SS Sivakumar Sekar Syncfusion Team October 20, 2021 04:43 PM UTC

Hi AB, 

On further validation of your query. We suspect that you want to use an HTML template inside the App.vue component itself. As shown in the below code snippet, we can render the HTML content inside the App component itself, but we are specifying the HTML content as a string, so we couldn`t able to hook events with it. But if we need to achieve events for the HTML template we need to render it as NodeTemplate to achieve the events associated with it. 
const drawingshape = {  
        type: 'HTML',  
        content: `<div style="background-color: red;width: 100%;height: 100%;" class="text-center">  
      <button class="btn btn-info btn-sm"> Event Info </button>  
      <div style="margin-left:45%">annotation</div>  
      </div>`,  
        value: {  
          select: this.nodeCounter  
        }  
     


Regards, 
Sivakumar 



AB AB October 22, 2021 09:14 AM UTC

@Sivakumar

Thanks a lot for the answer. You have understood my query correctly. Sure, I will continue to use the NodeTemplate. I have one small query regarding the addition of the Annotation at runtime.

I have the HTML Node and each HTML Node consists of Button. Upon clicking the Button I am opening the Bootstrap Modal where users can provide some information. After submitting the Modal information I would like to obtain the information from this Modal and add it to the HTML Node. Since I am using the HTML Node you were saying we need to add it to the NodeTemplate directly so I am a bit confused on how to add it.


Previously when using AngularJS I was doing something like this:

Initially, when I was creating the Node I was adding the Label then I was updating them something like this:



//Find the nodeID
var diagram = angular.element("#diagram").ejDiagram("instance");
var node = diagram.selectionList[0];
$scope.NodeEventId = node.name;

//Add Label to the Node to display relevent information
var label1 = {name: "label1", text: "", offset: {x: 0.5, y: 0.55}};
var label2 = {name: "label2", text: "", offset: {x: 0.5, y: 0.65}};
var label3 = {name: "label3", text: "", offset: {x: 0.5, y: 0.75}};
diagram.addLabel($scope.NodeEventId, label1,2);
diagram.addLabel($scope.NodeEventId, label2,2);
diagram.addLabel($scope.NodeEventId, label3,2);

//Update the Labels to the Particular Event with relevent information
var diagram = angular.element("#diagram").ejDiagram("instance");
var selectedObject = diagram.model.selectedItems.children[0];
diagram.updateLabel(selectedObject.name, selectedObject.labels[1], { text: EventTypeLabel});
diagram.updateLabel(selectedObject.name, selectedObject.labels[2], { text: "No. of events: "+ $scope.formdata.eventcount});
diagram.updateLabel(selectedObject.name, selectedObject.labels[3], { text: $scope.formdata.businessStep});


SS Sivakumar Sekar Syncfusion Team October 22, 2021 02:40 PM UTC

Hi AB, 

On the further analysis of the shared details, we found that you have to add an annotation to the HTML nodes at runtime, while adding the annotation, it renders behind the HTML node because the normal nodes and connectors take place in the diagram layer and HTML nodes render in the HTML layer and Native node takes place on the Native layer. So, the HTML layer takes place above the diagram layer. In your case, the annotation takes place in the Diagram layer and the HTML node takes place in the HTML layer, it is above the diagram layer. so the annotation takes place behind the HTML nodes. we cannot move the annotation above the HTML layer, it’s our implementational behavior we don’t have any option to change this behavior. But you can add the input text to your HTML template to achieve your requirements. 

Regards, 
Sivakumar 


Marked as answer

AB AB October 25, 2021 02:04 PM UTC

@Sivakumar


Thanks a lot for your response. I implemented it according to your suggestion and everything seems to be working fine. Thanks for all the help :)



SS Sivakumar Sekar Syncfusion Team October 26, 2021 05:33 AM UTC

Hi AB, 

Thanks for your update. 

Regards, 
Sivakumar 


Loader.
Up arrow icon