BlazorDynamicFormSyncfusion is a dynamic form generator built with C# for Blazor applications, with an integration of the Syncfusion Blazor UI library. It uses data annotation attributes to automatically build forms with validation included.
- Installation
- Packages Description
- Form Usage
- Data Providers
- Create a Custom Integration
- Contributing
- License
- Built-in data annotations support for forms.
- Syncfusion Blazor components integration.
- Customization options for form fields.
- Automatic form generation and handling.
- CSS customization for each property, so you can use the boostrap grid system.
.NET CLI
dotnet add package BlazorDynamicFormDataAnnotation --version 1.0.5
dotnet add package BlazorDynamicFormGenerator --version 1.0.5
dotnet add package BlazorDynamicFormSyncfusion --version 1.0.5
Packet Manager
Install-Package BlazorDynamicFormDataAnnotation -Version 1.0.5
Install-Package BlazorDynamicFormGenerator -Version 1.0.5
Install-Package BlazorDynamicFormSyncfusion -Version 1.0.5
This project includes three essential packages that handle various aspects of dynamic form generation and management in Blazor. Here's a brief rundown of what each package offers:
-
BlazorDynamicFormDataAnnotation: This package is the heart of the form generation process in Blazor. It employs DataAnnotations for generating dynamic forms, taking the grunt work out of the process. Leveraging the framework's capabilities, it automates form creation based on your annotated model.
-
BlazorDynamicFormGenerator: This package acts as a blueprint generator. It dynamically produces class description templates, allowing the frontend to function independently of hardcoded class definitions. The generated templates cater to both fixed and fluid design requirements, offering you the flexibility to adapt to evolving needs.
-
BlazorDynamicFormSyncfusion: This extension package is a bridge between the Blazor Dynamic Form and Syncfusion Blazor components. It allows seamless integration of Syncfusion components into the generated forms, offering enhanced user interaction and richer data handling. Each component can be assigned to specific data types or attributes, promoting consistent and streamlined form structures.
Together, these packages serve as a robust toolset for handling form generation in Blazor applications, offering flexibility, ease of use, and integration with industry-leading component libraries.
In your Blazor application, include the namespace for the dynamic form generator.
@using BlazorDynamicFormDataAnnotation
Here is an example of a dynamic form in a Razor component:
<DynamicForm DataDefinition="@definition" DataObject="@data" OnValidSubmit="@OnValidResult">
<ValidationMessageTemplate Context="error">
@error
</ValidationMessageTemplate>
<FieldTemplate Context="template">
<div class="row mb-3">
<label for="@template.PropertyInfo.Name" class="col-sm-2 col-form-label">@template.PropertyInfo.DisplayName</label>
<div class="has-validation col-sm-10">
<div class="col">
@template.DynamicComponent
</div>
@if (!template.IsValid)
{
<div class="invalid-feedback d-block">
@template.ValidationComponent
</div>
}
</div>
</div>
</FieldTemplate>
<SubmitTemplate>
<div class="col-12">
<button type="submit" class="btn btn-primary">Save</button>
</div>
</SubmitTemplate>
</DynamicForm>
Refer to the included Test
class in the OnInitialized()
method for an example of a model with data annotations.
public class Test
{
[Required, Display(Name = "First Name"), DataType(DataType.Text)]
public string FirstName { get; set; }
[Display(Name = "Last Name"), DataType(DataType.Text)]
public string LastName { get; set; }
[Required, Display(Name = "Email Address"), DataType(DataType.EmailAddress), EmailAddress]
public string Email { get; set; }
[Required, Display(Name = "PhoneNumber"), DataType(DataType.PhoneNumber), Phone]
public string PhoneNumber { get; set; }
[Required, Display(Name = "Date of Birth"), DataType(DataType.DateTime)]
public DateTime? DOB { get; set; }
[Required, DataType(DataType.Duration), Display(Name = "Total Experience"), Range(0, 20, ErrorMessage = "The Experience range should be 0 to 20"), DefaultValue(10.0)]
public decimal TotalExperience { get; set; } = 22;
[Required, Display(Name = "Select a Country"), DataType("DropdownList"), LinkedAttribute(typeof(int))]
public string Country { get; set; }
[Required, DataType(DataType.MultilineText), Display(Name = "Address"), DefaultValue("piazza 24 maggio"), BlazorDynamicFormGenerator.ReadOnly]
public string Address { get; set; }
}
Generate the model description:
var definition = ModuleNodePropertyDefinitionExtensions.GetDefinition<Test>();
Get the result direclty as JSON:
void OnValidResult(string data){..}
Result:
{"FirstName":"Lorenzo","LastName":null,"Email":"l@hoy.com","PhoneNumber":"331","DOB":"2023-06-22T00:00:00+02:00","TotalExperience":10.0,"Country":"id","Address":"piazza 24 maggio"}
Showcase with Boostrap and Syncfusion
The AddDataProvider
method is used to set up a source of data for certain elements in the form, particularly for elements like dropdown lists or combo boxes that require a list of options to present to the user.
This method accepts a delegate that can retrieve or generate the data required for a given attribute and name. The method should return the data in a format that can be used to populate the dropdown list or other similar components.
Here's how it is used in the given code example:
builder.Services.AddBlazorDynamicForm()
.AddDataProvider((attribute, name) =>
{
var data = new List<FormVar>();
data.Add(new FormVar() { Id = "id", Name = "Cipolle" });
return data;
})
.SyncfusionForm();
In this example, AddDataProvider
is being used to provide a list of FormVar
objects whenever needed. In a more realistic scenario, the delegate might retrieve the data from a database or an API depending on the attribute and name provided.
This makes AddDataProvider
a powerful tool for dynamic form generation, as it allows you to tailor the data shown in each field based on the requirements of that field.
SyncfusionIntegration is a static class that serves as an example of how to extend the Dynamic Form Configuration to integrate custom data types and components.
The SyncfusionForm method of this class modifies the form's configuration, adding a series of Syncfusion Blazor components with their respective data types. The attributes of these components are set through various builder functions.
public static class SyncfusionIntegration
{
public static void SyncfusionForm(this DynamicFormConfiguration config)
{
config
.Add<SfTextBox, string>(DataType.Text, (builder, sequence, attribute) =>
{
builder.AddAttribute(sequence++, "Readonly", attribute.ReadOnly);
})
.Add<SfTextBox, string>(DataType.EmailAddress)
.Add<SfTextBox, string>(DataType.PhoneNumber)
.Add<SfTextBox, string>(DataType.MultilineText, (builder, sequence, attribute) =>
{
builder.AddAttribute(sequence++, "Multiline", true);
builder.AddAttribute(sequence++, "Readonly", attribute.ReadOnly);
})
.Add<SfDatePicker<DateTime?>, DateTime?>(DataType.DateTime)
.Add<SfNumericTextBox<decimal?>, decimal?>(DataType.Duration)
.AddCustom<SfDropDownList<string, FormVar>, string>("DropdownList", (builder, sequence, attribute) =>
{
var linked = attribute.ValidationRules.OfType<LinkedAttribute>().FirstOrDefault();
if (linked is not null)
builder.AddAttribute(sequence++, "DataSource", config.DataSource(linked, attribute.Name));
builder.AddAttribute(sequence++, "ChildContent", (RenderFragment)((builder2) =>
{
builder2.AddMarkupContent(0, "\r\n");
builder2.OpenComponent<DropDownListFieldSettings>(1);
builder2.AddAttribute(2, "Value", "Id");
builder2.AddAttribute(3, "Text", "Name");
builder2.CloseComponent();
builder2.AddMarkupContent(4, "\r\n");
}));
})
.AddError((builder, sequence, value, name, error) =>
{
builder.AddAttribute(sequence++, "Value", value);
builder.AddAttribute(sequence++, "PlaceHolder", name);
builder.AddAttribute(sequence++, "class", error ? "is-invalid" : "is-valid");
});
}
}
The Add
method is used to map a data annotation attribute to a Blazor component. This method accepts the type of the component, the DataType for which the component should be used, and an optional configuration delegate.
Here's an example of how it is used:
config.Add<SfTextBox, string>(DataType.Text, (builder, sequence, attribute) =>
{
builder.AddAttribute(sequence++, "Readonly", attribute.ReadOnly);
});
The AddCustom
method allows for more advanced configurations. It is used when you want to map a custom data annotation to a specific Blazor component. This method also accepts the type of the component, the custom data annotation name, and a configuration delegate.
Here's an example:
config.AddCustom<SfDropDownList<string, FormVar>, string>("DropdownList", (builder, sequence, attribute) =>
{
var linked = attribute.ValidationRules.OfType<LinkedAttribute>().FirstOrDefault();
if (linked is not null)
builder.AddAttribute(sequence++, "DataSource", config.DataSource(linked, attribute.Name));
builder.AddAttribute(sequence++, "ChildContent", (RenderFragment)((builder2) =>
{
builder2.AddMarkupContent(0, "\r\n");
builder2.OpenComponent<DropDownListFieldSettings>(1);
builder2.AddAttribute(2, "Value", "Id");
builder2.AddAttribute(3, "Text", "Name");
builder2.CloseComponent();
builder2.AddMarkupContent(4, "\r\n");
}));
});
In the above example, the AddCustom
method maps a custom data annotation named "DropdownList" to a SfDropDownList
component. The configuration delegate is used to configure the DataSource
and ChildContent
attributes of the SfDropDownList
component.
These two methods are very powerful as they give you the ability to customize the way your forms work with different data types and annotations, leveraging the variety of components provided by Syncfusion Blazor.
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. Please make sure to update tests as appropriate.
This project is licensed under the GPL-3.0 license. See the LICENSE file for details.