This forge component currently manages data on flowchart with Json as fixed value but I want to manage data in an entity.
I mean, I show entity data on the lest side as list (red mark) and save the data you created on flow in database(green mark).
I wonder if we can do this.
If possible, I'd like to exactly know about how to do it.
Best,
Hi Tsubasa-san,
Yes, you can. Here's how to do it, exactly:
Make sure you have the latest Drawflow and Drawflow demo app installed from Forge. Open the DrawflowDemo module, and clone it. Rename the module to EntityDrivenDrawflowDemo and publish. Create a new application, ‘Drawflow - Entity Driven Demo’ and move the EntityDrivenDrawflowDemo module to the new application.
There are two parts of this demo that we want to convert; the side menu and the diagram. Let's take a look at the side menu which contains the different types of Nodes for the diagram. To start, open the Main screen and take a look at the widget tree. We can see that the side menu consists of a Container and ListItems, which we can easily replace with a dynamic List.
Container with List items
Every List item has a unique icon, title and data-node attribute. We will make these as our Entity attributes.
List item with icon, title and data-node
The data-node attribute is being used as a unique identifier, which decides what will be added to the diagram when the list item is dragged over. In the screen action ‘OnDrop_DrawflowEditor’ you can find a switch statement that handles this logic.
Switch statement that handles the OnDrop logic
The ‘addNode’ function requires additional parameters like number of inputs and outputs, css class, data and html content. We can add these as our Node entity attribute. To summarize, here’s how we’re going to map the Node attributes.
Node attributes mapping
Create a new module called Drawflow_CS in the same application. In this core module, create an entity called Node with the attributes above and set it to Public. Remember to give Data and HtmlContent sufficient length as it could be a long text. Since we have all the information for the side menu, we can create an Excel sheet with all the data to bootstrap the Node Entity.
Excel file for bootstrap
To bootstrap, go back to the Drawflow_CS module, right click on the Node entity and select Advanced > Create Action to bootstrap data from Excel. Publish the module and you should be able to see the Node Entity now populated.
Get back to the EntityDrivenDrawflowDemo module and open the Main screen. We can begin replacing the side menu. Add the Node entity from the core module to the screen and set it to fetch on demand. We want to make sure that Drawflow has been initialized before adding the nodes. Next, set the on after fetch event to a new screen action called ‘OnAfterFetch_GetNodes’. We’ll get back to this action later. Follow these steps to replace the side menu:
Replace the Container in Column 1 with a List
Set source to GetNodes.List
Set the style = “min-height: 100vh;”. When the screen first loads, the menu won’t be loaded yet, and the height is going to be zero, hence the minimum height.
Add ListItem to List
Set draggable = “true” to enable drag and drop
Set data-node = GetNodes.List.Current.NodeEntity.Id. This will be our unique identifier for the node.
Add a HTML Element into ListItem content. We use this instead of the Icon because we need it to be dynamic.
Set the Tag = “i” which is what the OS UI Icon translates to
Set class = "icon margin-right-s fa-1x fa " + GetNodes.List.Current.NodeEntity.IconCssClass
Add an Expression and set the Value to GetNodes.List.Current.NodeEntity.Title.
To get drag and drop working, we need to add the dragstart event listener to every ListItem. We also need to add additional information for the node, like the number of inputs, outputs and data.
Add data-node, data-inputs, data-outputs, data-node-data, data-css-class and data-html to ListItem
In the Main screen, delete ‘OnReady’ screen action, as we will register the dragstart event listener after GetNodes response. Open ‘OnAfterFetch_GetNodes’ and add the following JavaScrtipt. I added a 300ms delay to allow the side menu to be rendered.
<code>
setTimeout(function() {
const draggables = document.querySelectorAll('.list-item[draggable="true"]');
draggables.forEach(function(d) {
d.addEventListener('dragstart', function(ev) {
ev.dataTransfer.setData("node", ev.target.dataset.node);
ev.dataTransfer.setData("inputs", ev.target.dataset.inputs);
ev.dataTransfer.setData("outputs", ev.target.dataset.outputs);
ev.dataTransfer.setData("data", ev.target.dataset.nodeData);
ev.dataTransfer.setData("css", ev.target.dataset.cssClass);
ev.dataTransfer.setData("html", ev.target.dataset.html);
});
}, 300);
</code>
Open ‘OnInitialize_DrawflowEditor’, get Refresh Data action and set the source to GetNodes. Now we need to handle the drop event. Open ‘OnDrop_DrawflowEditor’ and replace the JavaScript code with this:
$parameters.Event.preventDefault();
const node = $parameters.Event.dataTransfer.getData("node");
const inputs = $parameters.Event.dataTransfer.getData("inputs");
const outputs = $parameters.Event.dataTransfer.getData("outputs");
const data = $parameters.Event.dataTransfer.getData("data");
const css = $parameters.Event.dataTransfer.getData("css");
const html = $parameters.Event.dataTransfer.getData("html");
let pos_x = $parameters.Event.clientX;
let pos_y = $parameters.Event.clientY;
const editor = $parameters.Editor;
pos_x = pos_x * ( editor.precanvas.clientWidth / (editor.precanvas.clientWidth * editor.zoom)) - (editor.precanvas.getBoundingClientRect().x * ( editor.precanvas.clientWidth / (editor.precanvas.clientWidth * editor.zoom)));
pos_y = pos_y * ( editor.precanvas.clientHeight / (editor.precanvas.clientHeight * editor.zoom)) - (editor.precanvas.getBoundingClientRect().y * ( editor.precanvas.clientHeight / (editor.precanvas.clientHeight * editor.zoom)));
editor.addNode(node, inputs, outputs, pos_x, pos_y, css, JSON.parse(data), html);
Publish and we have a working drag and drop! Replacing the diagram will be a much easier process. To start, check the Structure under Drawflow. We need to create Entities corresponding to these.
Drawflow JSON structure
Open the Drawflow_CS module and create Entities with the same attributes. Keep in mind the relation between these Entities; Module should have DrawflowId as foreign key, Node2 (I renamed this to ModuleNode) has ModuleId, InputOutput has ModuleNodeId and Connection has InputOutputId. Set the Delete rule to Delete to every foreign key for simplicity. We also need to differentiate between Input and Output, so create a static Entity called ConnectionType with two records, “Input” and “Output”. Set all the entities to Public and Read only.
Entities diagram
Now, we can add a Server action to save everything all at once, given a Drawflow object. To start, add the structure from the Drawflow module, shown before. I renamed all with *Struc suffix. Create a new server action called ‘Drawflow_CreateOrUpdate’. In the server action, add input parameters DrawflowId, Name and Drawflow of type DrawflowStruc. Add output parameter SavedDrawflowId of type Drawflow identifier. Next we need to save every object, starting with Drawflow. Get the action CreateOrUpdate Drawflow and pass DrawflowId and Name input parameters. Again for simplicity, let's get rid of all Modules related to the DrawflowId. We can use advanced query to delete all, in one fell swoop. The last thing to do is to loop through the objects and save each item. Set this server action to Public, and that’s it. You should have something resembles the following:
Drawflow_CreateOrUpdate server action
Publish and add don’t forget to add the entities and the CreateOrUpdate_Drawflow action to the EntityDrivenDrawflowDemo module. I renamed all the Entities with *Entity suffix, so I don’t get confused with the structures. There is already a Server Action called SaveToDB. We could change the input parameter to Drawflow, call the action CreateOrUpdate_Drawflow and that would be the end of this tutorial. Why stop there? Rename ‘SaveToDB’ to ‘ExportNodesToExcel’ and leave that as is.
Open the ‘Main’ screen and add Drawflow Entity as a new data source. Add an input parameter called DataflowId OF type DrawflowEtity Identifier. Add a dropdown to show saved Drawflow and a ‘Save as’ button by following these steps:
I want to show a Popup dialog with an input to name the Drawflow diagram, when the user clicks on the ‘Save as’ button. Take a peek at the Widget tree of the Main screen. At the bottom you can see a Popup. We want to follow the same style as this excellent Popup. Create a new Variable called ‘IsShowSaveAsPopup’ and set it to True in ‘OnClick_SaveAs’.
Rename Popup to Popup_Import
Drag a new Popup below it and name it Popup_SaveAs. Set the Show Popup to ‘IsShowSaveAsPopup’
Add a Form and a PopupSection in Popup_SaveAs. Click on the Form and set Style class = “display-contents”. Click on the PopupSection and set the event ‘BackdropOnClick’ to call a new screen action ‘OnClick_CancelSave’. Set ‘IsShowSaveAsPopup’ to False in this screen action.
Add a title and set the Style classes to “heading5”.
Add Input field and set the Variable to ‘Name’ and Mandatory = True
Drag a container and set the Style classes to “text-align-right”. Drag two buttons into the container, and set the Text to Cancel and Save. For the Cancel button, use css class "btn btn-small btn-cancel border-size-none" while for the Save button, "btn btn-small". Set the OnClick action of the Cancel button to ‘OnClick_CancelSave’ that we created in step 3. The save button should be set as the Form default to trigger the form validation. Create a new screen action ‘OnClick_Save’ and set it to the save button OnClick event.
Open the ‘OnClick_Save’ action, and it should have the Form.Valid check before proceeding. We need to export the diagram, and convert it to DrawflowStruc before calling the Drawflow_CreateOrUpdate server action. Add a JavaScript action, set the Editor as input parameter, Data (Text) as output parameter and add the following script:
var editor = $parameters.Editor;
var data = editor.export();
$parameters.Data = JSON.stringify(DrawflowToOutsystemsJson(data));
Publish and preview it in the browser to test the new Save feature. If all goes well, you’ll be able to see the data in your database. Well done, you made it this far!
The last part is to be able to load the saved data and import it to the editor. Right click on the Main screen and choose ‘Fetch data from other sources’ and name it GetDrawflowById. Set it to Fetch Only On Demand. Create an Output parameter called ‘Drawflow’ of type ‘Drawflow structure’. There are a few ways to get data from the DB to be in the expected structure, like making multiple queries to get the related records would be the most straightforward. I opted to make a single query, sorted and have a single loop to append unique records. The resulting action looks like below:
Remove the ‘Name’ local variable and create another output parameter called ‘Name’ in GetDrawflowById to fetch the Drawflow name from DB. Add an On After Fetch event for the ‘GetDrawflowById’ called ‘OnAfterFetch_GetDrawflowById’ to import the data to the Drawflow editor. I also added the default JSON if DrawflowId is empty.
let JsonString = $parameters.JsonString;
const editor = $parameters.DrawflowEditor;
JsonString = JSON.parse(JsonString);
if (JsonString && editor) {
editor.import(JsonString);
}
Open ‘OnInitialize_DrawflowEditor’ action and add Refresh ‘GetDrawflowById’. Click on the <Drawflow> component on the Main screen and remove the Data input parameter. Click on the ‘DrawflowDropdown’ at the top of the Main screen and add the OnChange event ‘OnChange_DrawflowDropdown’. We want to refresh GetDrawflowId data, or clear the data if the user picks “Create new”.
OnChange_DrawflowDropdown
editor.clear();
There you have it, a fully functioning, Entity driven Drawflow demo. For extra marks, try the following:
I've uploaded the completed app to Forge as a holiday gift to the community. I hope this will inspire and guide you to your solution and future success!
Kind regards,
Izam