Unlock the Power of Microsoft Blazor: Seamless Integration with Workflow Designer — Check Out Guide Now!
Tags: frontend, dotnet, csharp, tutorial
Published: 2024-05-26 15:42:37

## 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](https://devblogs.microsoft.com/dotnet/author/jeffreytfritzyahoo-com/) 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

{% youtube vpMzm6TsdDY %} 

## 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](https://github.com/optimajet/workflow-designer-blazor-sample) repository).

Let's start by creating an application using the `blazorserver` template.

```bash
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**

![Empty Blazor application](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zbtbityig1blc8vmr3hx.png)

What we are going to add:

1. CSS and JS files for Workflow Designer from CDN.
2. _Designer_ navigation element on the left panel with the corresponding page.
3. 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

```html 
@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

```javascript 
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:

1. `renderWorkflowDesigner` - renders the Designer with the specified options.
2. `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
```html 

<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
```html 
@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:
1. `elementId` - the identifier of the HTML element in which Designer should be drawn.
2. `apiUrl` - the URL where Designer's API is located.
3. `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**

![Workflow Designer in Blazor application](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qs6628m4zrb812w0t0gw.png)

## 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]( https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/?view=aspnetcore-8.0). 
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](https://github.com/optimajet/WorkflowEngine.NET/discussions).