Skip to content

AutoForm is a model-driven UI control generation tool for .Net.

License

Notifications You must be signed in to change notification settings

PaulBraetz/AutoForm

Repository files navigation

AutoForm

Note: this readme was generated on 30.01.2023 17:29:23 +00:00 using README_template.ls


1. Table of Contents

  1. Table of Contents

  2. Description

  3. Versioning

  4. Features

    4.1. Attribute-based model discovery

    4.2. Debug json generator

    4.3. Blazor component generator

  5. Installation

  6. How To Use

    6.1. A Note on Attributes

    6.2. Creating Models

    6.3. Creating Templates

    6.4. Creating Controls

    6.5. First Results

    6.6. Complex Models

    6.7. Complex Models Result

    6.8. Submodels

    6.9. Submodels Result

  7. Planned Features

  8. License

  9. Contributors


2. Description

AutoForm is a model-driven UI control (input component) generation tool for .Net. Its aim is to decouple UI controls from each other.

Similar to dependency injection, using controls managed by AutoForm forces decoupling from implementations, so callers need only know the model for which they require a control.


3. Versioning

AutoForm uses Semantic Versioning 2.0.0.


4. Features

  • Attribute-based model discovery
  • Debug json generator
  • Blazor component generator

4.1. Attribute-based model discovery

TODO: describe attribute discovery here.


4.2. Debug json generator

TODO: describe json generator here


4.3. Blazor component generator

TODO: describe blazor generator here


5. Installation

Currently, there are multiple packages published for generating code based on models:

  • For model discovery, an attribute collection is provided in the AutoForm.Attributes package.
  • For debug purposes, a json generator is provided in the AutoForm.Json.Analysis package.
  • For use in Blazor, a blazor component generator is provided in the AutoForm.Blazor.Analysis package.
  • For accessing controls generated by AutoForm.Blazor.Analysis, an entry point control and other default controls and templates are provided in the AutoForm.Blazor package.

In order to use the generators, models have to be annotated using the attributes found in AutoForm.Attributes. Since the generators rely on code semantics in order to discover models, you may implement your own attributes using the same namespace and attribute names as in AutoForm.Attributes. This is currently not reccomended, as the underlying processes are not hardened against faulty attribute declarations.

Installing AutoForm.Attributes

Nuget Gallery: https://www.nuget.org/packages/RhoMicro.AutoForm.Attributes

Package Manager: Install-Package RhoMicro.AutoForm.Attributes -Version 3.0.0

.Net CLI: dotnet add package RhoMicro.AutoForm.Attributes --version 3.0.0

Installing AutoForm.Blazor

Nuget Gallery: https://www.nuget.org/packages/RhoMicro.AutoForm.Blazor

Package Manager: Install-Package RhoMicro.AutoForm.Blazor -Version 2.1.0

.Net CLI: dotnet add package RhoMicro.AutoForm.Blazor --version 2.1.0

Installing AutoForm.Blazor.Analysis

Nuget Gallery: https://www.nuget.org/packages/RhoMicro.AutoForm.Blazor.Analysis

Package Manager: Install-Package RhoMicro.AutoForm.Blazor.Analysis -Version 3.0.1

.Net CLI: dotnet add package RhoMicro.AutoForm.Blazor.Analysis --version 3.0.1

Installing AutoForm.Json.Analysis

Nuget Gallery: https://www.nuget.org/packages/RhoMicro.AutoForm.Json.Analysis

Package Manager: Install-Package RhoMicro.AutoForm.Json.Analysis -Version 3.0.1

.Net CLI: dotnet add package RhoMicro.AutoForm.Json.Analysis --version 3.0.1


6. How To Use

The following samples use the provided blazor generator.


6.1. A Note on Attributes

While templates and controls used for the blazor generator are required to provide an Attributes property, generated controls will ignore any attributes passed to them. Default controls found in AutoForm.Blazor.Controls will honor attributes passed via the Attributes property.


6.2. Creating Models

Register a model by annotating at least one of its properties with the ModelPropertyAttribute:

using AutoForm.Attributes;

namespace TestApp.Models;

public class MyModel
{
	[ModelProperty]
	public String? Name {
		get;
		set;
	}
}

Source: TestApp.Models.MyModel

Access the generated control using the AutoControl:

@page "/"
@using TestApp.Models

<AutoForm.Blazor.AutoControl @bind-Value="Model" />

<span>
	@(nameof(MyModel.Name)) : @(Model.Name)
</span>

@code {
	private MyModel Model { get; set; } = new MyModel();
}

Source: TestApp.Pages.Index.razor

Note that default controls provided by AutoForm.Blazor, found in AutoForm.Blazor.Controls, will bind to the oninput event. This means that when using these controls, models will be updated with every keystroke instead of when a control loses focus, as is default.


6.3. Creating Templates

Templates may be created in order surround a control with another component. They must semantically implement the following interface:

using Microsoft.AspNetCore.Components;

namespace AutoForm.Blazor.Templates.Abstractions
{
	public abstract class TemplateBase<TModel> : ComponentBase
	{
		[Parameter]
		public virtual TModel? Value { get; set; }
		[Parameter]
		public virtual IEnumerable<KeyValuePair<String, Object>>? Attributes { get; set; }
		[Parameter]
		public virtual RenderFragment? ChildContent { get; set; }
	}
}

Source: AutoForm.Blazor.Templates.Abstractions.TemplateBase

Here, TModel is the model type whose control this template should be applied to. These properties enable the template to access the controls attributes as well as the models current value. The control will be passed to ChildContent.

Note that templates must only provide properties that are semantically identical to those found in TemplateBase, as well as inherit from ComponentBase.

For uniform bootstrap template styling, a primitive base template may be created like so:

@typeparam TModel
@inherits AutoForm.Blazor.Templates.Abstractions.TemplateBase<TModel>

<div class="input-group mb-3">
	<span class="input-group-text" >@Text</span>
	@ChildContent
</div>

@code{
	private Object Text => Attributes?.SingleOrDefault(kvp => kvp.Key == "label").Value ??
								"Enter Value:";
}

Source: TestApp.Templates.MyTemplate

using AutoForm.Blazor.Templates.Abstractions;

namespace TestApp.Templates;

public abstract partial class MyTemplate<TModel> : TemplateBase<TModel>
{
}

Source: TestApp.Templates.MyTemplate

Implement a template for controls whose model is of type System.String or that control the MyModel.Name property:

using AutoForm.Attributes;

using TestApp.Models;

namespace TestApp.Templates;

[DefaultTemplate(typeof(String))]
[DefaultTemplate(typeof(MyModel), nameof(MyModel.Name))]
public sealed class StringTemplate : MyTemplate<String>
{
}

Source: TestApp.Templates.MyModelNameTemplate


6.4. Creating Controls

Controls may be declared in order to override default or generated controls. They must semantically implement the following interface:

using Microsoft.AspNetCore.Components;

namespace AutoForm.Blazor.Controls.Abstractions
{
	public abstract class ControlBase<TModel> : ComponentBase
	{
		[Parameter]
		public virtual TModel? Value { get; set; }

		[Parameter]
		public virtual EventCallback<TModel> ValueChanged { get; set; }

		[Parameter]
		public virtual IEnumerable<KeyValuePair<String, Object>>? Attributes { get; set; }
	}
}

Source: AutoForm.Blazor.Controls.Abstractions.ControlBase

Here, TModel is the model type this control should be applied to.

Note that controls must only provide properties that are semantically identical to those found in ControlBase, as well as inherit from ComponentBase.

Implement a control for models of type System.String or subcontrol for MyModel.Name:

@inherits AutoForm.Blazor.Controls.Abstractions.ControlBase<String>

<input class="form-control" type="text" @attributes="Attributes" value="@Value" @onchange="v=>ValueChanged.InvokeAsync(Value = v.Value?.ToString())" />

Source: TestApp.Controls.MyModelNameControl

using AutoForm.Attributes;

using TestApp.Models;

namespace TestApp.Controls;

[DefaultControl(typeof(String))]
[DefaultControl(typeof(MyModel), nameof(MyModel.Name))]
public partial class StringControl : AutoForm.Blazor.Controls.Abstractions.ControlBase<String>
{
}

Source: TestApp.Controls.MyModelNameControl


6.5. First Results

The html rendered by AutoForm.Blazor.AutoControl for models of type MyModel will now look something like this:

Missing Image


6.6. Complex Models

Models require all their marked properties to be assignable to a control. This means thet composite models will require all their submodels to also be marked for control generation. Note that this model requires a subcontrol for its NestedModel property. Luckily, its type has already been marked as a model. Had it not been, the generator would have issued an error.

using AutoForm.Attributes;

namespace TestApp.Models;

public sealed class ComplexModel
{
	[ModelProperty]
	public String? Street {
		get; set;
	}
	[ModelProperty]
	public String? City {
		get; set;
	}
	[ModelProperty]
	public Int32 ZipCode {
		get; set;
	}
	[ModelProperty]
	public String? Address {
		get; set;
	}
	[ModelProperty]
	public MyModel NestedModel { get; set; } = new MyModel();
}

Source: TestApp.Models.ComplexModel.cs


6.7. Complex Models Result

The html rendered by AutoForm.Blazor.AutoControl for models of type ComplexModel will now look something like this:

Missing Image


6.8. Submodels

Models may inherit their base types model properties. Annotating a model with the SubModelAttribute will instruct the generator to enhance the generated control with subcontrols for properties found in the basetype.

using AutoForm.Attributes;

namespace TestApp.Models;

//Inherit subcontrols for all properties of MyModel
[SubModel(typeof(MyModel))]
//Inherit subcontrols for specific properties in MyModel
[SubModel(typeof(MyModel), nameof(MyModel.Name))]
public class SubModel : MyModel
{
	[ModelProperty]
	public Int32 Age {
		get; set;
	}
}

Source: TestApp.Models.SubModel.cs

For this model, labeled subcontrols are created using templates:

using AutoForm.Attributes;

namespace TestApp.Templates.SubModel;

[DefaultTemplate(typeof(Models.SubModel), nameof(Models.SubModel.Name))]
public sealed class NameTemplate : MyTemplate<String>
{
	public override IEnumerable<KeyValuePair<String, Object>>? Attributes {
		get; set;
	}
		= new Dictionary<String, Object>()
		{
				{"label", nameof(Models.SubModel.Name) }
		};
}

Source: TestApp.Templates.SubModel.NameTemplate.cs

using AutoForm.Attributes;

namespace TestApp.Templates.SubModel;

[DefaultTemplate(typeof(Models.SubModel), nameof(Models.SubModel.Age))]
public sealed class AgeTemplate : MyTemplate<Int32>
{
	public override IEnumerable<KeyValuePair<String, Object>>? Attributes {
		get; set;
	}
		= new Dictionary<String, Object>()
		{
				{"label", nameof(Models.SubModel.Age) }
		};
}

Source: TestApp.Templates.SubModel.AgeTemplate.cs


6.9. Submodels Result

The html rendered by AutoForm.Blazor.AutoControl for models of type SubModel will now look something like this:

Missing Image


7. Planned Features

  • Importing/Exporting types from and to other assemblies

8. License

This software is licensed to you under the MIT license.


9. Contributors

About

AutoForm is a model-driven UI control generation tool for .Net.

Resources

License

Stars

Watchers

Forks