Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / Blazor

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

4.71/5 (13 votes)
29 Jan 2023CPOL6 min read 25.6K  
Developing applications based on Flows, Forms, and Rules using type-safe advantages of C#
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.

Image 1

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.

Image 2

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.

C#
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.

C#
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 Forms that attach UI controls to the Model that is supplied as a template parameter to generic class FormEditBase<>.

C#
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.

C#
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:

Image 3

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.

Razor
@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:

C#
// 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 _Hosts.cshtml file:

HTML
<!-- 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 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:

Image 4

Picture 4

Image 5

Picture 5

Image 6

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

License

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