seed project in the city

Part 1- BlazorForms Low-Code Open-Source Framework: Introduction and Seed Projects

Developing applications based of Flows, Forms and Rules using type safe advantages of C#

When you need to build a working prototype for your client, or your business doesn’t have the budget for enterprise development you are left with limited options. You may need to use some shortcuts and life hacks which are usually derived from low-code or no-code approaches. In this post, I present an approach on how to develop a UI very quickly using the open-source framework BlazorForms.

Quick development is not the only advantage here, you also get low maintenance and a predictable solution, that later can be improved with custom UI and extended onto mobile versions.

BlazorForms requires minimum knowledge in UI development and helps to present data entities as a Single Page Application.

To minimize the initial effort, we have created seed projects that have been made available on GitHub. Please see the latest seed projects version 0.7.0 on my Blog repository:

Download this blog post code from GitHub –

https://github.com/euklad/BlogCode/tree/main/Story-08-BlazorForms-Intro-Seeds

BlazorForms project on GitHub –

https://github.com/ProCodersPtyLtd/BlazorForms

Seed Projects

BlazorForms was in development for a few years as an internal project of PRO CODERS PTY LTD – an Australian based software consulting business oriented on quality specialists. But I have exciting news to share with you and announce that BlazorForms will now be shared with the open-source community.

Our framework segregates an application to Flows, Forms and Rules. The approach prevents spaghetti code and reduces the amount of run-time errors whilst offering the full support of Visual Studio IntelliSense.

To demonstrate what each component means and how it looks, we need to open the seed projects, let’s do it using Visual Studio 2022.

BlazorFormsSeed

This project was created using the Visual Studio Blazor Server App template with the addion of the following NuGet packages:

  • BlazorForms 0.7.0
  • BlazorForms.Rendering.MudBlazorUI 0.7.0

It also indirectly references MudBlazor 6.1.5 – an open-source framework implementing Material Design for Blazor.

blazor seed projeect

The navigation menu and layout were also changed to use MudBlazor.

When your run the application you can see the simple form generated dynamically, it is bound to a Model, supports validations, and also is a step in a Flow that controls which Form to show and which actions to do when the Form is submitted or closed.

blazor forms

All sample code is located in the Flows\SampleFlow.cs file where we put a few classes together for simplicity

Model

At the bottom of the file, you can find a Model class which is crucial for this approach. Model consists of the properties that will be used in Flows and Forms and the compiler will check that you use existing properties and correct types in your code.

         public class MyModel1 : IFlowModel
	{
		public virtual string? Message { get; set; }
		public virtual string? Name { get; set; }
		public virtual string? Logs { get; set; }
	}

Flows

When Flow is defined, we reference to Model as a template parameter of FluentFlowBase<> generic type, allowing the compiler to know the Model type and can check that we use only existing properties.

public class SampleFlow : FluentFlowBase<MyModel1>
{
	public override void Define()
	{
		this
			.Begin()
			.NextForm(typeof(QuestionForm))
			.If(() => Model.Name?.ToLower() == "admin")
				.Next(() => Model.Logs = "Flow = 'SampleFlow'\r\nLast Form = 'QuestionForm'\r\nLast Action = 'Submit'")
				.NextForm(typeof(AdminForm))
			.Else()
				.Next(() => { Model.Message = $"Welcome {Model.Name}"; })
			.EndIf()
			.NextForm(typeof(WellcomeForm))
			.End();
	}
}

Define method specifies a sequence of steps and conditional branches that together control the presentation flow.

In the Sample Flow we defined:

  • initially show QuestionForm and the Flow will wait for the user input
  • when ‘Cancel’ button is pressed the Flow is terminated
  • when ‘Submit’ button is pressed the Flow continues to the next statement – If
  • In the If statement the Flow checks the condition (entered name equals to “admin”)
  • when “admin” is entered the Logs property populate and AdminForm is shown
  • when something else is entered, the Message property is populated and WellcomeForm is shown

Forms

In the Flow we used three different Forms that attach UI controls to the Model that is supplied as a template parameter to generic class FormEditBase<>

public class QuestionForm : FormEditBase<MyModel1>
{
	protected override void Define(FormEntityTypeBuilder<MyModel1> f)
	{
		f.DisplayName = "BlazorForms Sample";
		f.Property(p => p.Name).Label("What is your name?").IsRequired();
		f.Button("/", ButtonActionTypes.Close, "Cancel");
		f.Button("/", ButtonActionTypes.Submit);
	}
}

public class AdminForm : FormEditBase<MyModel1>
{
    protected override void Define(FormEntityTypeBuilder<MyModel1> f)
    {
		f.Property(p => p.Logs).Control(ControlType.TextArea).IsReadOnly();
		f.Button("/", ButtonActionTypes.Close);
    }
}

public class WellcomeForm : FormEditBase<MyModel1>
{
	protected override void Define(FormEntityTypeBuilder<MyModel1> f)
	{
		f.Property(p => p.Message).Control(ControlType.Header);
		f.Button("/", ButtonActionTypes.Close);
	}
}

In Define method of QuestionForm we have:

  • render Form subtitle “BlazorForms Sample”
  • for Model property Name render input control that is required
  • render button ‘Cancel’ that terminates Flow if pressed
  • render button ‘Submit’ that submits Form and continues Flow

Similarly, AdminForm renders a read-only text area that shows Logs property and only one button that terminates Flow when pressed; and WellcomeForm renders a header control that shows Message property and ‘Close’ button.

As you remember the values of Logs and Message properties are populated in Flow logic.

Basically, all code logic is contained in Flows and Rules, but Forms only define bindings between Model properties and controls that should be rendered in the UI.

Rules

Rules can be attached to every property on the Form, and it will be triggered when a value is changed. In the Rule you can place logic that can fire a validation error, hide or show a control, change Model property values and so on.

The basic seed project doesn’t have example of Rule, but I will add some small modifications to demonstrate it.

I modified QuestionForm and added NotWombatRule

public class QuestionForm : FormEditBase<MyModel1>
{
	protected override void Define(FormEntityTypeBuilder<MyModel1> f)
	{
		f.DisplayName = "BlazorForms Sample";

		f.Property(p => p.Name).Label("What is your name?")
			.IsRequired().Rule(typeof(NotWombatRule));

		f.Property(p => p.Message).Control(ControlType.Label).Label("").IsHidden();
			
		f.Button("/", ButtonActionTypes.Close, "Cancel");
		f.Button("/", ButtonActionTypes.Submit);
	}
}

public class NotWombatRule : FlowRuleBase<MyModel1>
{
    public override string RuleCode => "SFS-1";

    public override void Execute(MyModel1 model)
    {
		if (model.Name?.ToLower() == "wombat")
		{
			model.Message = "really?";
			Result.Fields[SingleField(m => m.Message)].Visible= true;
		}
		else
		{
			model.Message = "";
			Result.Fields[SingleField(m => m.Message)].Visible = false;
		}
    }
}

As you can see QuestionForm now has a hidden control for Message property and NotWombatRule is attached to property Name. When user enters “wombat” to Name field and presses Tab to move focus to the next control, the Rule will be triggered and it will check the entered Name and Message will be shown:

Rules also accept Model template parameters and support type safety, if you use non-existing Model property or mismatch its type you will see compile errors.

Blazor Page

When Flow, Forms and Rules are defined we can use them on a Blazor page. The project Pages folders contains Sample.razor file

@page "/sample"

<FlowEditForm FlowName="@typeof(BlazorFormsSeed.Flows.SampleFlow).FullName" Options="Options" NavigationSuccess="/" />

@code {
    EditFormOptions Options = new EditFormOptions { MudBlazorProvidersDefined = true, Variant=Variant.Filled };
}

We placed the FlowEditForm razor component here and supplied some parameters: the type of Flow, Options, and where to navigate when the Flow is finished.

When user navigates to “/sample” page BlazorForms framework creates an instance of SampleFlow and starts executing it step by step. When the first Form is met, it renders the Form data and waits for the user input. When data is entered and Form is submitted, the Flow execution continues until the end.

Program.cs and _Hosts.cshtml

The last thing I should mention is how to register BlazorForms framework in your Blazor Application. It is done in Program.cs file:

// BlazorForms
builder.Services.AddServerSideBlazorForms();
builder.Services.AddBlazorFormsMudBlazorUI();
builder.Services.AddBlazorFormsServerModelAssemblyTypes(typeof(SampleFlow));

var app = builder.Build();

// BlazorForms
app.BlazorFormsRun();

and references to css and javascript files should be added to the _Hosts.cshtml file:

    <!-- MudBlazor -->
    <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
    <link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
    <script src="_content/MudBlazor/MudBlazor.min.js"></script>

Summary

In this post I presented the BlazorForms framework – an open-source project that simplifies Blazor UI development and allows you to create simple and maintainable C# code, that is very useful for low-budget projects and prototyping. The main idea is to place logic to Flows and Rules, which is not dependent on UI and is unit-testable. Forms simply contain bindings between Model and UI controls.

This post is a brief presentation of BlazorForms and I have not covered different types of Flows, how to store Flow State between sessions, how to define Flows and Forms in Json, instead of C# and many other features. All that will be presented in my future blogs.

Pro Coders believes that this BlazorForms framework can also be used for large projects where hundreds of forms should be implemented and maintained with minimum effort and acceptable quality. It should be a good alternative to custom UI implementation where each page/form requires enormous effort to develop and test, and then quality degrades when modifications are added overtime.

Next – CrmLightDemoApp seed project

The more complex scenarios will be covered next and we will start looking at CrmLightDemoApp seed project.

It has several Flows connected to Repositories implementing CRUD operations, for now I will present several screenshots:

blazor forms crm light
blazor forms crm light
blazor forms crm light

Thank you for reading and remember that you can always reach out to us if you would like any help with your implementations.