I have created a Blazor Component to handle editing a database record which is defined by a class. I would like this component to be reused to edit other records - that have the same structure, but different field names. For example, here is one of my class definitions:
public class AccountTypeCode
{
public int AccountTypeId { get; set; }
[Required]
public string AccountType { get; set; } = string.Empty;
[Required]
public string? AccountTypeDescription { get; set; } = string.Empty;
public string AdditionalInformation { get; set; } = string.Empty;
public int DisplayOrder { get; set; } = 0;
public DateOnly? DateTerminated { get; set; }
}
Another class might refer to CustomerTypeId, CustomerType, CustomerTypeDescription, etc.
In my component's @code section I currently have code like:
if (recordEdit is not null && !string.IsNullOrEmpty(recordEdit.AccountType))
{
onSaveButton(recordEdit.AccountType);
}
Where recordEdit is of type AccountTypeCode.
What I want to do is replace, for example, recordEdit.AccountType with a generic recordEdit.RecordType
By doing this I can reuse my component many times without having to write a new component for each table that I want to edit.
This is what my component looks like:
You can use an Interface or a generic type and use Reflection.
For example, create an IRecordType
Interface:
public interface IRecordType
{
string RecordType { get; set; }
string? RecordDescription { get; set; }
string AdditionalInformation { get; set; }
int DisplayOrder { get; set; }
DateOnly? DateTerminated { get; set; }
}
Now you should implement it your related classes (for the AccountTypeCode
and CustomerTypeCode
if that's its class name):
public class AccountTypeCode : IRecordType
{
public int AccountTypeId { get; set; }
[Required]
public string AccountType { get; set; } = string.Empty;
[Required]
public string? AccountTypeDescription { get; set; } = string.Empty;
public string AdditionalInformation { get; set; } = string.Empty;
public int DisplayOrder { get; set; } = 0;
public DateOnly? DateTerminated { get; set; }
// here is the implementation of the interface properties
string IRecordType.RecordType
{
get => AccountType;
set => AccountType = value;
}
string? IRecordType.RecordDescription
{
get => AccountTypeDescription;
set => AccountTypeDescription = value;
}
}
Now in your razor component add the @typeparam
directive to basically declares a generic type parameter named TRecordType for the component. It allows the component to work with a specific type that will be specified when the component is used:
@using System.Reflection
@typeparam TRecordType where TRecordType : class, IRecordType, new()
@*
Some razor markup...
*@
@code{
private TRecordType recordEdit = new();
}
So generally this allows you to use Interface's RecordType
and RecordDescription
properties instead of the specific classe's properties (eg. AccountType
and AccountTypeDescription
).
For example in your Form component:
<div>
<label>Record Type *</label>
<InputText @bind-Value="recordEdit.RecordType" />
</div>
<div>
<label>Record Description *</label>
<InputText @bind-Value="recordEdit.RecordDescription" />
</div>
Also please conside refactoring your classes and properties names, it is a good practice to name your id property just as Id
, your class name from AccountTypeCode
to AccountType
and AcountType
string in this context to AccountTypeName
string.
Hope this helps.
Use or call your component this way:
<GenericEditComponent TRecordType="AccountTypeCode" />
<GenericEditComponent TRecordType="CustomerTypeCode" />