Click here to Skip to main content
15,867,834 members
Articles / Web Development / HTML

Javascript Flow Chart and Workflow with C# ASP.NET MVC

Rate me:
Please Sign up or sign in to vote.
4.95/5 (24 votes)
8 Nov 2017CPOL12 min read 47.6K   2.4K   42   3
Using JSPlumb with ASP MVC to create flow-chart and work-flow applications
This article is about a very useful Javascript library called 'JS plumb', and how it can help us build flow-chart or work-flow style capabilities into our .net web based applications. Let's have a look at the basics of how things hang together visually with flow-charts, and JSPlumb in particular. We look at: shapes (the building blocks of our flowchart), setting the anchors property, endpoints (the intersection of connectors and anchors), and connectors (the lines that allow us to create the links between shapes).

Introduction

As soon as we wake in the morning, we start to make decisions - do we stay in bed? (weekend, yea!), or get out of the bed? (get to work, must catch the train...). Once we make that first decision, we might take an action (grab a coffee!), or ponder another decision (do I need to visit the bathroom first?). As the day progresses, our life is one decision after another. Its the same in every organization. In one way, we can say that life, and how it goes, is based on infinite possibilities of flow-states :)

This article is about a very useful Javascript library called 'JS plumb', and how it can help us build flow-chart or work-flow style capabilities into our .net web based applications. As you might expect these days, its not restricted to .net, and not restricted to MVC, but for this article, that's what I've chosen to focus on.

Image 1

Background

I have frequently come across situations where I wish to give the user the some form of decision making in how a program executes. This may be based on a simple series of hard-coded states (yes/no, if X then choose from a list of Y actions), or may be more complex with an underlying automation system such as that provided by Windows workflow foundation. I always loved the visual aspect of workflow foundation, and missed the ability to be able to provide that wonderful drag and drop work-flow-builder experience we have in visual studio to the end user. 

Image 2

When I came across JSPlumb, I was immediately excited by the possibilities. It's a very flexible library that manages objects on a canvas and the connections between them - its focus is on providing a solution for the 'plumbing' part of a flow-chart or work-flow style user interface. As with a lot of these libraries, there is an open source community edition, and one that's more advanced. The latter is known as JSPlumb Toolkit and comes with commercial support. JSPlumb uses SVG and runs on all browsers from IE9 and up.

The basics

Before we start jumping into code, lets have a look at the basics of how things hang together visually with flow-charts, and JSPlumb in particular.

Image 3

There are a lot of common things you will be aware of in the above diagram:

  • a workfow or flow-chart usually has some kind of starting point
  • there are lines that connect things together
  • we have shapes that represent decisions that have to be made, and actions to be taken
  • the direction of the connection between shapes is usually represented by arrows, and annotated with text of some kind.

If we zoom in on that picture a little bit closer, JSPlumb defines things as follows:

Image 4

The visual objects are split into two groups. There are the objects of interest which are called the 'source' and 'target', and then the things that connect these objects. Each source or target can have 0:M anchor positions. The anchors are points on the object that the connectors can hook onto. Connectors allow objects to be linked together. Connectors link to an end-point at an anchor at either end on the source or target - as they are bidirectional, there is no start or end 'end-point', simply, endpoints!

Getting started

The first thing to understand about the library is that works using a series of DIV objects that you define. Unlike other JS libraries that get you to define a DIV as a boundry and use this as a canvas to draw on, JSPlumb simply says - "tell me what DIVs are your flow-chart shapes, and I will connect them". This makes sense, given the name of the library - its not flowchartJS or WorkFlowJS, its about joining things together, its literally the 'plumbing' !

To demonstrate this, lets define two DIVs, and then join them together with JSPlumb. First, the divs...

HTML
<div class="shape" id="shape1"></div>
<div class="shape" id="shape2"></div>

and some styling

HTML
  .shape {
  float:left;
  margin:10px;
  width: 100px;
  height: 100px;
  border: solid 1px;
}

And now, the magic....

JavaScript
jsPlumb.ready(function() {
    jsPlumb.connect({
        source:"shape1",
        target:"shape2"
    });
});

In the same way we can direct Javascript to trigger when the DOM is ready, in this case we tell jsPlumb to execute the 'connect' method for our first two shapes onces its loaded and ready to go. Heres how it looks once rendered ... all smiley-face like!

Image 5

We didn't have to define the position for the connection or the anchor or endpoint shape - it used its own defaults, 'bottom' and 'round'.

Shapes

The building blocks of our flowchart are shapes - we connect them together, but the shape tiself usually signifies something. Diamonds or triangles mean decisions, circles convergance points, etc. It all depends on the application (think flow-chart versus entity relationship diagram versus web based work-flow). JSPlumb is not responsible for styling the shapes, thats up to you, and your old friend CSS. Whatever you stlye, JSPlumb will hook up. Here are some examples you may commonly see in a flow-chart application.

NB: You can get some great CSS Shape examples at CSS Tricks, for fine tuning of colours you could do worse than refer to HTMLColorCodes.

Image 6

HTML Declaring the CLASS to style each DIV

HTML
<div id="shape1" class="shape square"></div>
<div id="shape2" class="shape circle"></div>
<div id="shape3" class="shape triangle-down"></div>
<div id="shape4" class="shape parallelogram"></div>

CSS styling for each example

CSS
.shape {
  float:left;
  margin:80px;
  width: 100px;
  height: 100px;
  border: solid 1px;
}

.square {
    width: 100px;
    height: 100px;
    background: #76D7C4;
}

.circle {
    width: 100px;
    height: 100px;
    background: red;
    -moz-border-radius: 50px;
    -webkit-border-radius: 50px;
    border-radius: 50px;
  background: #F7DC6F
}

.triangle-down {
    width: 0;
    height: 0;
    border-left: 50px solid transparent;
    border-right: 50px solid transparent;
    border-top: 100px solid red;
}

.parallelogram {
    width: 150px;
    height: 100px;
    -webkit-transform: skew(20deg);
       -moz-transform: skew(20deg);
         -o-transform: skew(20deg);
    background: blue;
}


Lets see what else we can do to tweak the endpoints and connections. First, lets look at the anchors, the points on the shape itself that the connectors hook onto.

Anchors

Changing where the connectors anchor is done by setting the anchors property

JavaScript
jsPlumb.ready(function() {
    jsPlumb.connect({
        source:"shape1",
        target:"shape2",
        anchor:"Top"
    });
});

Valid options are:

  • Top (also aliased as TopCenter)
  • TopRight
  • Right (also aliased as RightMiddle)
  • BottomRight
  • Bottom (also aliased as BottomCenter)
  • BottomLeft
  • Left (also aliased as LeftMiddle)
  • TopLeft
  • Center

Some examples:

Image 7

In addition to the static anchors listed above, there are other options available to you including Dynamic and Peripheral. The latter are interesting, as they are dynamically created depending on the type of shape you define the object source or target DIV to be. For example, if you have a decision object, you might choose a diamond shape - in this case the library will automatically put the anchors in the most logical position which would be the corners of the diamond. You get similar options using for example the circle/oval - the documentation for jsplumb anchors outlines what you need to know should you need this level of flexibility. 

Endpoints

Endpoints are the intersection of connectors and anchors. They can be styled using some built-in shapes, images or custom CSS (yes, good ole Bob finally made it as an end-point .. success at last!)

Image 8

Different endpoint types you can use are as follows

  • Dot This Endpoint draws a dot on the screen. It supports three constructor parameters:

    • radius - Optional; defaults to 10 pixels. Defines the radius of the dot.
    • cssClass - Optional. A CSS class to attach to the element the Endpoint creates.
    • hoverClass - Optional. A CSS class to attach to the element the Endpoint creates whenever the mouse is hovering over the element or an attached Connection.
  • Rectangle Draws a rectangle. Supported constructor parameters are:

    • width Optional; defaults to 20 pixels. Defines the width of the rectangle.
    • height Optional; defaults to 20 pixels. Defines the height of the rectangle.
    • cssClass Optional. A CSS class to attach to the element the Endpoint creates.
    • hoverClass Optional. A CSS class to attach to the element the Endpoint creates whenever the mouse is hovering over the element or an attached Connection.
  • Image Draws an image from a given URL. This Endpoint supports three constructor parameters:

    • src Required. Specifies the URL of the image to use
    • cssClass Optional. A CSS class to attach to the element the Endpoint creates.
    • hoverClass Optional. A CSS class to attach to the element the Endpoint creates whenever the mouse is hovering over the element or an attached Connection.
  • Blank Does not draw anything visible to the user. This Endpoint is probably not what you want if you need your users to be able to drag existing Connections - for that, use a Rectangle or Dot Endpoint and assign to it a CSS class that causes it to be transparent.

Connectors

The lines that allow us to create the links between shapes are connectors. Like endpoints and anchors, they have different options that we can use. The connector carries with it some of the other concepts:

  • Anchors are the places on the shapes that connectors can hook onto
  • Endpoints are a visual representation of one end of a connection, which are mostly but not always attached visibly to shapes, giving an indication of where connectors can hook onto

JSPlumb has four standard connectors - a bezier curve (this is the default), a straight line, a 'flowchart' mode, and a 'state machine' mode. 

Connectors and their options are generally specified using the connector property when we call any of the following methods

  • jsPlumb.connect
  • jsPlumb.addEndpoint(s)
  • jsPlumb.makeSource
  • jsPlumb.makeTarget

Here are some examples of building a connection, and setting the type of connection that is rendered

Example 1 - JSPlumb Connector - Bezier with different curve radius

JavaScript
  jsPlumb.connect({
  source:"shape1",
  target:"shape2",
  connector:[ "Bezier", { curviness:150 } ]
});

  jsPlumb.connect({
  source:"shape3",
  target:"shape4",
  connector:[ "Bezier", { curviness:50 } ]
});

Image 9
 

Example 2 - JSPlumb Connector - Straight and flowchart connectors with options

JavaScript
  jsPlumb.connect({
  source:"shape1",
  target:"shape2",
  connector:[ "Straight" ],
  anchors:["Top", "Bottom"],
  endpoint:[ "Rectangle", { width:20, height:40 }]
});

  jsPlumb.connect({
  source:"shape3",
  target:"shape4",
  connector:[ "Flowchart", {cornerRadius:15}]
});

  jsPlumb.connect({
  source:"shape5",
  target:"shape6",
  connector:[ "Flowchart", {stub:10, gap:20}]
});

Image 10

The options used above are 'cornerRadius', which puts a curve on the corner of the connector as it connects, 'gap' which introduces a space between the connector and the endpoint, and 'stub' which gives a squared edge to the connection. 

In addition to talking about the connector options, there we have two other concepts we have still to talk about:

  • Overlays are UI elements that can be used to decorate a connector - for example an arrow indicating direction of the connector, or a text overlay on the connector line itself describing the connection.
  • Groups allow you to have a collection of elements 'grouped' inside another element, which can be collapsed and expanded - this could be very useful in a work-flow chart to allow the user to drill down into detail on a particular workflow process.

Overlays

The lines that make up the connectors have options available to them as well. Amongst these are overlays. These are elements like display text along the conneciton line and arrowheads. In the following example code we are going to do a few things with the overlay, add some text and style it, and add a number of different arrows.

FIrst, lets see the end result, then we'll look at how its built up

Image 11
As I want to offload the styling for the 'Sample text' overlay label onto a CSS class, we'll define this first

CSS
.labelClass {
    background-color:yellow; 
    padding:0.4em; 
    font:16px sans-serif; 
    color:#444;
    z-index:21;
    border:1px solid red;
}

Now lets look at the JSPlumb setup code 

JavaScript
  jsPlumb.connect({
  source:"shape1",
  target:"shape2",
  paintStyle: { strokeStyle: "orange", lineWidth: 5 },
  connector:[ "Bezier", { curviness:100 } ],
  overlays:[ 
     [ "Arrow", { width:20, length:15, location:[.1], foldback: 1,
                paintStyle: { strokeStyle: "red", lineWidth: 2 } } 
                ],
     [ "Arrow", { width:20, length:15, location:[.2], 
                paintStyle: { strokeStyle: "green", lineWidth: 2 } } 
                ],
     [ "Arrow", { width:20, length:15, location:[.3], direction:-1,
                paintStyle: { strokeStyle: "blue", lineWidth: 2 } } 
                ],                
     [ "Arrow", { width:50, length:30, location:[.9] } ],
     [ "Label", { 
                   label:"Sample text",
                        location:0.5,
                        id:"label",
                        cssClass:"labelClass"
                    }],
    ]   
});

As before, we start by declaring the source and target shapes.

JavaScript
source:"shape1", target:"shape2"

next, we define how we want the main paint style to look

JavaScript
paintStyle: { strokeStyle: "orange", lineWidth: 5 }

our connector is a bezier curve

JavaScript
connector:[ "Bezier", { curviness:100 } ]

and here come the overlays....

The first one is an arrow. Its location is 10% from the source '.1'. The location works either as a proportional value from 0 to 1 inclusive (so in this case, '.1' = 10%), or as an an absolute value, where negative values mean distance from target, and positive values (greater than 1) mean the distance from source. Foldback refers to the fold at the base of the arrow. Negative numbers cause it to float out, like a diamond shape, positive number make for a skinny pointy arrow. We'll see an example of this shortly.

JavaScript
"Arrow", { width:20, length:15, location:[.1], foldback: 1,
     paintStyle: { strokeStyle: "red", lineWidth: 2 } }

The main feature of the next arrow is it uses the default foldback, and we have changed the stroke color.

JavaScript
[ "Arrow", { width:20, length:15, location:[.2],
paintStyle: { strokeStyle: "green", lineWidth: 2 } } 

Coming hot on the heels of Mr. Foldback, comes Ms.Reverse! ... here we set the 'Direction' property to be '-1'. The default is '1'.

JavaScript
[ "Arrow", { width:20, length:15, location:[.3], direction:-1,
                paintStyle: { strokeStyle: "blue", lineWidth: 2 } } 
                ], 

The last arrow of this example is a biggie - we change the dimensions of it by adjusting the width and length.

JavaScript
[ "Arrow", { width:50, length:30, location:[.9] } ]


Getting text in is very simple. You will recall that earlier we defined some CSS to style our label - here is where we call it.

JavaScript
[ "Label", {
              label:"Sample text",
                   location:0.5,
                   id:"label",
                   cssClass:"labelClass"
               }]

The key bits from the above code are the 'label' which feeds the text you wish to display, and the cssClass we wish to use for styling.

Heres a closer look at how the arrows work with another example, you can follow the code underneath to see how each one is designed.

Image 12

JavaScript
  jsPlumb.connect({
  source:"shape3",
  target:"shape4",
  connector:[ "Straight"],
  anchors: ["Right", "Left"],
  paintStyle: { strokeStyle: "orange", lineWidth: 1 },
  overlays:[ 
     [ "Arrow", { width:30, length:25, location:[.2], foldback: 1,
                paintStyle: { strokeStyle: "red", lineWidth: 2 } } 
                ],
     [ "Arrow", { width:20, length:15, location:[.4], 
                paintStyle: { strokeStyle: "green", lineWidth: 1 } } 
                ],
     [ "Arrow", { width:20, length:15, location:[.65],  foldback: 2,
                paintStyle: { strokeStyle: "red", lineWidth: 8 } } 
                ],                
     [ "Arrow", { width:20, length:15, location:[.8], direction:-1,
                paintStyle: { strokeStyle: "blue", lineWidth: 2 } } 
                ]
    ]   
});

 

Groups

The library has a concept called 'Groups' that works pretty much as you would expect, with an additional bonus. Grouping allows you to take a set of shapes, and group them together within the scope of a parent container. Any connectors from these child shapes to other shapes on your UI are retained. Now there are two cool things to note:

  • when you drag/move the parent/group container shape, all of the child shapes within it move with it.
  • you can collapse and expand the 'parent' group container, and as you do, any connectors that were previously linked visually to other shapes, are 'proxied' up onto the groups collapsed element.

Heres an screenshot that shows grouping:

Image 13

Ref: documentation page about grouping in JSPlumb.

Other useful things:

Common options

A lot of the time you may find you are repeating yourself with setting endpoint styles, connectors, etc. It may also be the case that in a complex applicaiton you want to have one set of styles for one type of element, and another set for the next. To help with this, we can pass in a plain old Javascript object as a 'common option' into Endpoint, Connector, Anchor and Overlay methods.

JavaScript
var commonEndpoint = {
      stub:"90",
      gap:"15"
};
  
  jsPlumb.connect({
  source:"shape5",
  target:"shape6",
  connector:[ "Flowchart", commonEndpoint]
});

Drag and drop

Another useful, and I think critical feature of a good flowchart/diagram utility is the ability to drag and drop elements onto the canvas. Drag/drop is initiated by introducing the 'draggable' method to an element:

<code data-lang="javascript">jsPlumbInstance.draggable($(".someClass"));</code>

More documentation about drag and drop here: https://jsplumbtoolkit.com/community/doc/dragging.html

Snap to grid

For a better, more organised layout, its handy to be able to 'snap' your elements on the canvas to a grid if you need. 

Image 14

There is an extremly useful site that demonstrates a good technique for doing this amongst other things at 'Free developer tutorials'

Saving and loading

Being able to save the users work and load it back again for them is rather critical, there are a number of solutions available for this (bingle is your friend!) ... this solution from stack-overflow works as it says on the tin....

 

Bringing it all together - Flowchart for MVC

I have put together (attached to the top of this article), a very basic demo (based on the stack code referred to above). It allows us to click a button to add different types of elements for our flowchart, and save the chart both to a local textarea and also to our MVC controller. It should be sufficent together with the walk-through of different features above to allow you to get started!

Steps

(1) Add chart elements by clicking buttons, or load simple example chart by clicking 'load from server'

Image 15


(2) result of loading from server
 

Image 16



Happy charting, and dont forget, if you liked the article, please give it a vote!
 

History

08/Nov/2017 - Version 1

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer SocialVoice.AI
Ireland Ireland
Allen is CTO of SocialVoice (https://www.socialvoice.ai), where his company analyses video data at scale and gives Global Brands Knowledge, Insights and Actions never seen before! Allen is a chartered engineer, a Fellow of the British Computing Society, a Microsoft mvp and Regional Director, and C-Sharp Corner Community Adviser and MVP. His core technology interests are BigData, IoT and Machine Learning.

When not chained to his desk he can be found fixing broken things, playing music very badly or trying to shape things out of wood. He currently completing a PhD in AI and is also a ball throwing slave for his dogs.

Comments and Discussions

 
QuestionThe solution does not run Pin
AndyHo28-Jul-22 10:05
professionalAndyHo28-Jul-22 10:05 
Questioncan we integrate this solution in desktop application Pin
Member 1450548922-Dec-19 18:09
Member 1450548922-Dec-19 18:09 
Questioncan we integrate this solution in desktop application. Pin
Member 1450548919-Dec-19 20:02
Member 1450548919-Dec-19 20:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.