Intro
Before we dive into the main guide on integrating Workflow Designer with Blazor, it makes sense to convey to readers the practical benefits of using workflow engines in applications. If you haven't considered this before, or if you have but lacked the motivation to implement a Workflow Engine, then this YouTube video by Jeff Fritz will surely captivate you. Jeff Fritz a principal program manager in Microsoft’s Developer Division working on the .NET Community Team. We are confident that this video will help you understand the benefits of implementing a Workflow Engine with a visual Workflow editor and dispel any doubts you may have.
Jeff Fritz explains in a simple and accessible manner, using a delivery case example, how using a workflow engine helps simplify development and reduces the time and effort spent on refactoring and testing
Overview
Not all .NET backend developers have expertise in popular frontend frameworks like React or Angular. However, .NET backend developers often possess skills in Microsoft Blazor. We have frequently received requests from our technical support team to create an example of integrating the community version of Optimajet WorkflowEngine (which includes a visual designer) with Microsoft Blazor. We have accomplished this and are sharing an example and guide with you on GitHub repository).
Let's start by creating an application using the blazorserver
template.
mkdir workflow-designer-blazor
cd workflow-designer-blazor
dotnet new blazorserver
We can run this application using the dotnet watch
command and modify its code on the fly. This is a great feature of the dotnet
CLI! If
you run the application and open it in a browser, you will see something like this:
Empty Blazor application
What we are going to add:
- CSS and JS files for Workflow Designer from CDN.
- Designer navigation element on the left panel with the corresponding page.
- Workflow Designer on a new Blazor page.
Adding CSS and JS Workflow for Workflow Designer
First, we need to add CSS and JS from Workflow Designer to our application so that we can connect the Designer to a new page. We will also
need to connect jQuery, since Workflow Designer uses it in its work.
We will use CDN, just to avoid copying Designer files to the project. Of course, you can use local files instead of CDN, this is especially
important when you work in an environment with limited Internet access.
info important
Due to the way Blazor works with JavaScript code, external JavaScript code must be included after the script blazor.server.js
.
Open the Pages/_Host.cshtml
file and add the highlighted lines as shown below. Styles are added inside the <head>
tag, scripts are added to the end of the page.
Pages/_Host.cshtml
@page "/"
@using Microsoft.AspNetCore.Components.Web
@namespace workflow_designer_blazor.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<base href="~/"/>
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css"/>
<link href="css/site.css" rel="stylesheet"/>
<link href="workflow-designer-blazor.styles.css" rel="stylesheet"/>
<link rel="stylesheet" href="https://unpkg.com/@@optimajet/workflow-designer@12.5.1/dist/workflowdesigner.min.css">
<link rel="icon" type="image/png" href="favicon.png"/>
<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered"/>
</head>
<body>
<component type="typeof(App)" render-mode="ServerPrerendered"/>
<div id="blazor-error-ui">
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.server.js"></script>
// &new>
<script
src="https://code.jquery.com/jquery-3.7.1.min.js"
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="
crossorigin="anonymous"></script>
<script src="https://unpkg.com/@@optimajet/workflow-designer@12.5.1/dist/workflowdesignerfull.min.js"
async defer>
</script>
<script src="js/designerInterop.js"></script>
// <&new
</body>
</html>
Now the Workflow Designer with its styles will be loaded in our application.
Pay attention to the file js/designerInterop.js
. This is a file that will contain auxiliary functions for working with the Designer.
Let's create it in the wwwroot/js/designerInterop.js
path:
wwwroot/js/designerInterop.js
function renderWorkflowDesigner(options) {
var wfdesigner = new WorkflowDesigner({
apiurl: options.apiUrl,
name: 'wfe',
language: 'en',
renderTo: options.elementId,
graphwidth: window.innerWidth - 400,
graphheight: window.innerHeight - 100,
showSaveButton: true,
})
const data = {
schemecode: options.schemeCode,
processid: options.processId
}
if (wfdesigner.exists(data)) {
wfdesigner.load(data)
} else {
wfdesigner.create(data.schemecode)
}
}
function waitForJsAndRender(options) {
if (typeof window.WorkflowDesigner !== 'undefined') {
renderWorkflowDesigner(options)
return
}
// the interval here is only needed to wait for the javascript to load with the designer
const interval = setInterval(() => {
// if the designer hasn't been uploaded yet, we'll wait a little longer
if (typeof window.WorkflowDesigner === 'undefined') return
clearInterval(interval)
renderWorkflowDesigner(options)
}, 30)
}
There are only two functions in the file:
-
renderWorkflowDesigner
- renders the Designer with the specified options. -
waitForJsAndRender
- waits for JavaScript to load with the Designer and calls the Designer's render. We need this function because the Designer loads asynchronously after the page loads.
Adding a new page
Open the Shared/NavMenu.razor
file and add the highlighted lines after the Fetch data navigation link.
Shared/NavMenu.razor
<div class="@NavMenuCssClass nav-scrollable" @onclick="ToggleNavMenu">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-plus" aria-hidden="true"></span> Counter
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="fetchdata">
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
</NavLink>
</div>
// &new>
<div class="nav-item px-3">
<NavLink class="nav-link" href="designer">
<span class="oi oi-copywriting" aria-hidden="true"></span> Designer
</NavLink>
</div>
// <&new
</nav>
</div>
Now add a new file Pages/Designer.razor
and paste the following content there:
Pages/Designer.razor
@page "/designer"
@inject IJSRuntime JSRuntime
<PageTitle>Workflow designer</PageTitle>
<div id="root"></div>
@code
{
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var options = new
{
apiUrl = "https://demo.workflowengine.io/Designer/API",
elementId = "root",
schemeCode = "SimpleWF"
};
await JSRuntime.InvokeAsync<Task>("waitForJsAndRender", options);
}
}
}
Everything is quite simple here. There is a div
element on the page with the id root
. When the page is first rendered, in the
OnAfterRenderAsync
method, we call a JavaScript function called waitForJsAndRender
, passing in options
as parameters.
In the parameters we pass:
-
elementId
- the identifier of the HTML element in which Designer should be drawn. -
apiUrl
- the URL where Designer's API is located. -
schemeCode
- the scheme code.
Launching the application
Now you can run your application using the dotnet run
or dotnet watch
command. After that, open your browser and navigate to a new page where you should see Workflow Designer.
Workflow Designer in Blazor application
Conclusion
We have added Workflow Designer to our Blazor application by including a script from a content delivery network (CDN).
To work with the designer, we used the JavaScript interop mechanism.
It was quite easy!
Your feedback is very important to us
It helps us understand whether this guide was useful to you, how clearly it was written, and what else you would like to learn about. Please ask your questions in the comments or start discussions on GitHub.
Top comments (0)