Hello @mrpmorris, I do like Fluxor and I want to integrate it into my application but I've faced an issue.
The problem is that I can't use two-way data binding in my form cuz my state is immutable and I can't validate it. I saw for example this guide https://dev.to/mr_eking/advanced-blazor-state-management-using-fluxor-part-5-14j2 and the author proposes to use DTO with public setters. And I saw your comment down below about "avoid having mutable state" and I agree with it. I wasted a lot of time validating my form manually using DataAnnotation + EditContext, I created a field with a public setter to store property from my state, etc. The best solution that I found for now it's using FluentValidation and validate my model manually. I am pretty new to blazor so maybe you can suggest me a better solution?
I created a sample project to illustrate an issue.
This is the model that I store in my state:
public record AssetsViewModel
{
public int Id { get; init; }
public IEnumerable<AssetModel> AssetsList { get; init; }
}
public record AssetModel
{
public int Id { get; init; }
public string? Name { get; init; }
}
I wasn't able to validate the form using DataAnnotation, so I decided to use FluentValidation, here are my rules for the model:
public class AssetValidation : AbstractValidator<AssetModel>
{
public AssetValidation()
{
RuleFor(p => p.Name)
.NotEmpty().WithMessage("You must enter your first name")
.MaximumLength(30).WithMessage("Name can be max 30 characters long.");
}
}
My parent:
<h1>Assets</h1>
@foreach (var asset in State.Value.Settings.AssetsList)
{
<AssetChildComponent Asset=@asset
OnNameChanged=@HandleNameChanged />
}
@code {
[Inject] protected IState<AssetsState> State { get; set; }
[Inject] protected IDispatcher Dispatcher { get; set; }
protected void HandleNameChanged((AssetModel asset, string newName) asset)
{
var assets = UpdateName(asset.newName, asset.asset);
Dispatcher.Dispatch(new AssetsUpdateAction(assets));
}
private IEnumerable<AssetModel> UpdateName(string name, AssetModel asset)
{
var updatedAssets = new List<AssetModel>();
foreach (var originalAsset in State.Value.Settings.AssetsList)
{
AssetModel newAsset = null;
if (asset.Id == originalAsset.Id)
{
newAsset = originalAsset with { Name = name };
}
updatedAssets.Add(newAsset ?? originalAsset);
}
return updatedAssets;
}
}
My child:
InputField is my custom component to handle OnChange + OnInput
Effect dispatched GetCustomerForEditActionResult containing the dto
Any state that holds Customer info reduces values from the dto to ensure they don't have stale state.
The component that dispatched the Action has an ActionSubscriber injected
When it triggers its event you store a reference to the dto in your component for EditForm to work on... Do not store the dto itself into any feature state.
User edits
User saves, which dispatches a SaveCustomerAction(dto)
Effect talks to the server
Effect dispatches a SaveCustomerActionResult(dto sent back from server)
The values of that dto are reduced into state, not the dto itself.
Hello @mrpmorris, I do like Fluxor and I want to integrate it into my application but I've faced an issue. The problem is that I can't use two-way data binding in my form cuz my state is immutable and I can't validate it. I saw for example this guide https://dev.to/mr_eking/advanced-blazor-state-management-using-fluxor-part-5-14j2 and the author proposes to use DTO with public setters. And I saw your comment down below about "avoid having mutable state" and I agree with it. I wasted a lot of time validating my form manually using DataAnnotation + EditContext, I created a field with a public setter to store property from my state, etc. The best solution that I found for now it's using FluentValidation and validate my model manually. I am pretty new to blazor so maybe you can suggest me a better solution? I created a sample project to illustrate an issue. This is the model that I store in my state:
I wasn't able to validate the form using DataAnnotation, so I decided to use FluentValidation, here are my rules for the model:
My parent:
My child:
InputField is my custom component to handle OnChange + OnInput
Best regards.