.net 8 Blazor Web assembly 中复杂模型的表单绑定

问题描述 投票:0回答:2

我有一个复杂的模型,我想在表单中发布,但它没有被绑定。只有第一层有数据。 Model.Items 和后续的 item.Items 不会被绑定。组件交互正常,按预期删除和添加项目。

所有代码都被缩短了一点,以便于阅读:)这些是模型:

public class NewOrEditFactoryOrderVM
{
    public int FactoryOrderId { get; set; }

    public int? OrderId { get; set; } = null;

    public int No { get; set; }

    public DateTime DocDate { get; set; }

    public List<NewOrEditFactoryOrderItemVM>? Items = new List<NewOrEditFactoryOrderItemVM>();
}

public class NewOrEditFactoryOrderItemVM
{
    public int FactoryOrderItemId { get; set; }

    public int FactoryOrderId { get; set; }

    ...

    public List<NewOrEditFactoryItemComponentVM>? Items { get; set; } = new List<NewOrEditFactoryItemComponentVM>();
}

public class NewOrEditFactoryItemComponentVM
{
    public int FactoryItemComponentId { get; set; }

    public int FactoryOrderItemId { get; set; }

    public int ProductId { get; set; }

    ...
}

我有带有表格的父组件:

<EditForm Model="Model" OnValidSubmit="Submit" FormName="NewOrEditFactoryOrder" Enhance>
    <div class="row">
        <div class="col-20">
            <DataAnnotationsValidator />
            <ValidationSummary />
        </div>

        <div class="clearfix"></div>

        <div class="col-md-2">
            <input type="hidden" @bind="@Model.FactoryOrderId" />
            <input type="hidden" @bind="@Model.OrderId" />
            <label for="WorkOrderNo" class="form-label">@Frontend.No<span class="text-danger"> *</span></label>
            <InputNumber class="form-control" @bind-Value="Model.No" />
            <ValidationMessage For="(() => Model.No)" />
        </div>
        <div class="col-md-3">
            <label for="DocDate" class="form-label">@Frontend.Date<span class="text-danger"> *</span></label>
            <InputDate class="form-control" @bind-Value="Model.DocDate" />
            <ValidationMessage For="(() => Model.DocDate)" />
        </div>

        <div class="clearfix"></div>

        <div class="col-md-3">
            <label class="form-label">@Frontend.SearchProduct <span class="text-danger">*</span></label>
            <input type="text" placeholder="@Frontend.SearchPlaceholder" class="form-control" @oninput=@SearchProduct />
            @if (productsDD != null)
            {
                <ul class="list-group">
                    @foreach (var item in productsDD)
                    {
                        if (item.Selected == true)
                        {
                            <li @onclick="() => SelectConnectedWarehouse(item.Id)" class="list-group-item" data-bs-toggle="modal" data-bs-target="#[email protected]">@item.Name</li>
                        }
                        else
                        {
                            <li @onclick="() => AddProduct(item.Id, null)" class="list-group-item">@item.Name</li>
                        }
                    }
                </ul>
            }
        </div>
        <div class="col-md-17">
            <CascadingValue Value="Model" Name="Model">
                <NOEFactoryOrderItemCom ></NOEFactoryOrderItemCom>
            </CascadingValue>
            
        </div>

        <div class="clearfix"></div>

        <div class="col text-end">
            <input type="submit" value="@Frontend.Save" class="btn btn-primary" />
        </div>
    </div>
</EditForm>

在表单内,我调用另一个组件来管理 Model.Items:

<CascadingValue Value="Model" Name="Model">
    <NOEFactoryOrderItemCom ></NOEFactoryOrderItemCom>
</CascadingValue>

内容:

@if (Model != null && Model.Items != null && Model.Items.Any())
{
    <hr />
    foreach (var item in Model.Items)
    {
        <div class="row">
            <input type="hidden" @bind="@item.FactoryOrderItemId" />
            <input type="hidden" @bind="@item.FactoryOrderId" />
            <input type="hidden" @bind="@item.OrderItemId" />

            <div class="col-md-4">
                <label for="Items.Description" class="form-label">
                    @item.ProductName
                    @if (item.ConnectedId != null && item.ConnectedId > 0)
                    {
                        <span> + </span>

                        @item.ConnectedName
                    }
                </label>
                @if (string.IsNullOrWhiteSpace(item.Comment))
                {
                    <InputTextArea class="form-control" @bind-Value="@item.Comment" rows="1" />
                }
                else
                {
                    <InputTextArea class="form-control" @bind-Value="@item.Comment" rows="4" />
                }
            </div>
            <div class="col-md-2">
                <label for="Items.Quantity" class="form-label gremlin-label">@Frontend.Quantity</label>
                <InputNumber class="form-control" @[email protected] TValue="decimal" />
            </div>
            <div class="col-sm-12"> </div>
            <div class="col-md-1">
                <button type="button" class="btn btn-danger" @onclick="@(() => RemoveItem(item))">
                    <i class="bi bi-x-circle-fill"></i>
                </button>
            </div>
        </div>
        <div class="row">
            <CascadingValue Name="Items" Value="item.Items">
                <NOEFactoryItemComponentCom ></NOEFactoryItemComponentCom>
            </CascadingValue>
        </div>
        <hr />
    }
}

@code {
    [Inject]
    private IFactoryServices _factory { get; set; } = null!;

    [CascadingParameter(Name = "Model")]
    public NewOrEditFactoryOrderVM? Model { get; set; }

最后2.子组件:

@if (SubItems != null && SubItems.Any())
{
    foreach (var subItem in SubItems)
    {
        <div class="col-md-1 col-sm-1"> </div>

        <div class="col-md-4">
            <label class="form-label gremlin-label">@Frontend.Item</label>
            <br />
            @subItem.ProductName
        </div>
        <div class="col-md-2">
            <label for="Items.Quantity" class="form-label gremlin-label">@Frontend.Quantity</label>
            <InputNumber class="form-control" @[email protected] TValue="decimal" />
        </div>
        @if (subItem.IsService)
        {
            <div class="col-md-2">
                <label for="Items.Workers" class="form-label gremlin-label">@Frontend.Workers</label>
                <InputNumber class="form-control" @bind-Value="@subItem.Workers" TValue="int" />
            </div>
        }
        else
        {
            <input type="hidden" name="Items.Workers" value="1" />
            <div class="col-md-2"> </div>
        }
        @if (subItem.HasDimensions)
        {
            <div class="col-md-3">
                <label for="subItem.x" class="form-label gremlin-label">@Frontend.X</label>
                <InputNumber class="form-control" @bind-Value="@subItem.x" TValue="decimal?" />
            </div>
            <div class="col-md-3">
                <label for="subItem.y" class="form-label gremlin-label">@Frontend.Y</label>
                <InputNumber class="form-control" @bind-Value="@subItem.y" TValue="decimal?" />
            </div>
            <div class="col-md-3">
                <label for="subItem.z" class="form-label gremlin-label">@Frontend.Z</label>
                <InputNumber class="form-control" @bind-Value="@subItem.z" TValue="decimal?" />
            </div>
        }
        else
        {
            <div class="col-md-9"> </div>
        }
        <div class="col-md-1">
            <button type="button" class="btn btn-danger" @onclick="@(() => RemoveSubItem(subItem))">
                <i class="bi bi-x-circle-fill"></i>
            </button>
        </div>
        <div class="clearix"> </div>
    }
}
@code {
    [Inject]
    private IFactoryServices _factory { get; set; } = null!;

    [CascadingParameter(Name ="Items")]
    public List<NewOrEditFactoryItemComponentVM>? SubItems { get; set; }
}
c# forms blazor-webassembly model-binding .net-8.0
2个回答
0
投票

所以。事实上问题不在于表单绑定。调试 Blazor WebAssembly 很困难。

问题是数据序列化未按预期执行。唯一有效的解决方案是使用 Newtonsoft.Json:

发送部分:

public async Task<NewOrEditFactoryOrderVM?> SaveFactoryOrder(NewOrEditFactoryOrderVM? vm)
{
    if(vm != null)
    {
        string json = JsonConvert.SerializeObject(vm);
        StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
        var response = await _httpClient.PostAsync("api/Factory/SaveFactoryOrder", content);
        if (response.IsSuccessStatusCode)
        { ... rest of logic

API 方面:

[HttpPost("SaveFactoryOrder")]
public async Task<ActionResult<NewOrEditFactoryOrderVM>> SaveFactoryOrder()
{
    try
    {
        string requestBody = await new StreamReader(Request.Body).ReadToEndAsync();
        NewOrEditFactoryOrderVM? vm = JsonConvert.DeserializeObject<NewOrEditFactoryOrderVM>(requestBody);
        ... rest of logic

我希望这对某人有帮助。我小时和很多挫折。无法找到根本原因:(


0
投票

您是否验证服务器中的用户输入?您之前尝试过哪些替代方案?我面临着同样的问题,有些人建议使用 Fluent Validation:https://github.com/Blazored/FluentValidation 但就目前而言,它不起作用。

© www.soinside.com 2019 - 2024. All rights reserved.