When you need to build a working prototype for your client or your company doesn't have a budget for enterprise development, you have no choice and you need to use some shortcuts and life hacks, usually 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.
All BlazorForms articles in this series
Introduction
When you need to build a working prototype for your client or your company doesn't have a budget for enterprise development, you have no choice and you need to use some shortcuts and life hacks, usually 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 by mobile versions.
BlazorForms requires a minimum knowledge in UI development and helps to present data entities as a Single Page Application.
To minimize the initial effort, we created seed projects, that are available on GitHub, and I copied the latest seed projects version 0.7.0 to my blog repository.
Download this blog post code from GitHub:
BlazorForms project on GitHub:
Seed Projects
BlazorForms was in development for a few years as an internal project of PRO CODERS PTY LTD - Australian based consulting oriented on quality specialists, and now we decided to share it for the open-source community.
Our framework segregates an application to Flows, Forms and Rules. That prevents spaghetti coding and reduces the amount of run-time errors, it also has full support of Visual Studio intellisence.
To demonstrate what which component means and how it looks like, it is better to open seed projects, let’s do it using Visual Studio 2022.
BlazorFormsSeed
This project is created using Visual Studio Blazor Server App template, and then we added NuGet packages:
BlazorForms
0.7.0 BlazorForms.Rendering.MudBlazorUI
0.7.0
It also references indirectly to MudBlazor
6.1.5 – open-source framework implementing Material Design for Blazor.
Picture 1
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 it is a step in a Flow that controls which Form to show and which actions to do when Form is submitted or closed.
Picture 2
All sample code is located in 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, that is crucial for this approach. Model
consists of the properties that will be used in Flows and Forms and 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, thus compiler knows 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 all together control the presentation flow.
In the Sample Flow, we defined:
- initially show
QuestionForm
and the Flow
will wait 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
If
statement, the Flow checks the condition (entered name equals to “admin
”) - when “
admin
” is entered, the Logs
property populated 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 Form
s 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 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 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 validation error, hide or show control, change Model
property values and so on.
The basic seed project doesn’t have example of Rule
, but I will add a small modification 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:
Picture 3
Rules also accept Model template parameter 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 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 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:
builder.Services.AddServerSideBlazorForms();
builder.Services.AddBlazorFormsMudBlazorUI();
builder.Services.AddBlazorFormsServerModelAssemblyTypes(typeof(SampleFlow));
var app = builder.Build();
app.BlazorFormsRun();
and references to CSS and JavaScript files should be added to _Hosts.cshtml file:
<!--
<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 BlazorForms framework – open-source project that simplifies Blazor UI development and allows 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 UI-depended and is unit-testable; and 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 Blog later.
PRO CODERS team believes that BlazorForms framework can be used even 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 later 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:
Picture 4
Picture 5
Picture 6
You can find the full solution code on my GitHub, folder Story-08-BlazorForms-Intro-Seeds:
Thank you for reading and remember that you can always reach out to us if you would like any help with your implementations.
https://procoders.com.au/contact-us/
History
- 9th January, 2023: Initial version