控制器没有收到表单返回的所有属性

Controller doesn't receive all properties back from the form

我有一个 class QuestionnaireDetailsViewModel,它包含一些属性和一个列表。

该列表可以包含 0 到多个项目。 我将 class QuestionnaireDetailsViewModel 发送到我的视图,以便在表单中显示它。 对于列表部分,我使用 foreach 来渲染它,它工作得很好。

问题是,所有这些属性都是输入,当我提交表单时,我的列表到达控制器时总是空的。

我还注意到,在调用的有效负载中,它只将列表中的第一项发送到控制器,但我相信这是因为每一项都具有相同的名称。

我不明白如何使列表属性名称与表单的每个输入相匹配,因为它们需要有一个唯一的名称。 是否可以实现此目的,或者我应该使用 JS 循环分别保存每个列表项?

能请教一下吗?

QuestionnaireDetailsViewModel class

public class QuestionnaireDetailsViewModel
{
    public string Id { get; set; }
    public string Name { get; set; }
    public List<VenueViewModel> VenueItems { get; set; }
}

VenueViewModel class

public class VenueViewModel
{
    public string Id { get; set; }
    public string Id_Que { get; set; }
    public string VenueName { get; set; }
    public string Checkbox_Other { get; set; }
    public string IsEventHaveSustainableApproach { get; set; }
}

查看

<form method="post" enctype="multipart/form-data" class="form-content-column" id="formFull">
    <div class="input-box">
        <label class="numbered" for="Name">Name<span class="span-mandatory">*</span></label>
        <input asp-for="Name" class="input-mandatory" type="text" id="Name" required />
        <span asp-validation-for="Name"></span>
        <input asp-for="Id" type="hidden" id="Id" required />
    </div>
    <div class="accordion" id="accordionExample">
        @foreach (var obj in Model.VenueItems)
        {
            <div class="accordion-item">
                <h2 class="accordion-header" id="headingOne@obj.Id">
                    <button class="accordion-button accordion-button-custom" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne_@obj.Id" aria-expanded="true" aria-controls="collapseOne_@obj.Id" style="margin-top: 0px">
                        @obj.VenueName
                    </button>
                </h2>
                <div id="collapseOne_@obj.Id" class="accordion-collapse collapse show" aria-labelledby="headingOne_@obj.Id" data-bs-parent="#accordionExample">
                    <div class="accordion-body">
                        <div class="input-box no-border">
                            <label class="numbered">Did the venue have a sustainable approach?<span class="span-mandatory">*</span></label>
                            <div class="flex flex-column">
                                <div class="single-radio">
                                    <input asp-for="@obj.IsEventHaveSustainableApproach" class="input-mandatory radio_Tab1_IsEventHaveSustainableApproach" type="radio" value="1" id="Tab1_IsEventHaveSustainableApproach_Yes_@obj.Id" />
                                    <label for="Tab1_IsEventHaveSustainableApproach_Yes_@obj.Id">Yes</label>
                                </div>
                                <div class="single-radio">
                                    <input asp-for="@obj.IsEventHaveSustainableApproach" class="input-mandatory radio_Tab1_IsEventHaveSustainableApproach" type="radio" value="0" id="Tab1_IsEventHaveSustainableApproach_No_@obj.Id" />
                                    <label for="Tab1_IsEventHaveSustainableApproach_No_@obj.Id">No</label>
                                </div>
                            </div>
                        </div>
                        <div class="input-box">
                            <label for="Tab1_Checkbox_EventVenue_Other">Other? Please explain</label>
                            <input asp-for="@obj.Checkbox_Other" type="text" id="Tab1_Checkbox_EventVenue_Other" />
                        </div>
                    </div>
                </div>
            </div>
        }
    </div>
</form>

js方法发送表单

async function saveForm() {

    let formData = new FormData(document.forms[0]);
    
    await fetch('/Reports/Save', {
        method: 'post',
        body: formData
    })
        .then((response) => {
            toastr.success('Saved successfully');
            preventLeaving = false;
            return true;
        })
        .catch((error) => {
            toastr.error('An error occured');
            return false;
        });
}

收到填有 属性 名称但列表项为空的表单的控制器

public async Task<IActionResult> SaveAsync(QuestionnaireDetailsViewModel questionnaireViewModel)
{
}

I also noticed that in the payload of the call, it only send the first item in the list to the controller, but I believe it's because each item have the same name

是的,是同名造成的。对于您的嵌套列表模型,它通过 name="[index].PropertyName"name="VenueItems[index].PropertyName".

绑定 属性

像下面这样更改您的前端代码:

@model QuestionnaireDetailsViewModel
<form method="post" enctype="multipart/form-data" class="form-content-column" id="formFull">
    <div class="input-box">
        <label class="numbered" for="Name">Name<span class="span-mandatory">*</span></label>
        <input asp-for="Name" class="input-mandatory" type="text" id="Name" required />
        <span asp-validation-for="Name"></span>
        <input asp-for="Id" type="hidden" id="Id" required />
    </div>
    <div class="accordion" id="accordionExample">
        @{
            int i = 0;         //add index here........
        }
        @foreach (var obj in Model.VenueItems)
        {
            <div class="accordion-item">
                <h2 class="accordion-header" id="headingOne@obj.Id">
                    <button class="accordion-button accordion-button-custom" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne_@obj.Id" aria-expanded="true" aria-controls="collapseOne_@obj.Id" style="margin-top: 0px">
                        @obj.VenueName
                    </button>
                </h2>
                <div id="collapseOne_@obj.Id" class="accordion-collapse collapse show" aria-labelledby="headingOne_@obj.Id" data-bs-parent="#accordionExample">
                    <div class="accordion-body">
                        <div class="input-box no-border">
                            <label class="numbered">Did the venue have a sustainable approach?<span class="span-mandatory">*</span></label>
                            <div class="flex flex-column">
                                <div class="single-radio">
                                                       @*add name attribute*@
                                    <input name="VenueItems[@i].IsEventHaveSustainableApproach" asp-for="@obj.IsEventHaveSustainableApproach" class="input-mandatory radio_Tab1_IsEventHaveSustainableApproach" type="radio" value="1" id="Tab1_IsEventHaveSustainableApproach_Yes_@obj.Id" />
                                    <label for="Tab1_IsEventHaveSustainableApproach_Yes_@obj.Id">Yes</label>
                                </div>
                                <div class="single-radio">
                                    <input name="VenueItems[@i].IsEventHaveSustainableApproach" asp-for="@obj.IsEventHaveSustainableApproach" class="input-mandatory radio_Tab1_IsEventHaveSustainableApproach" type="radio" value="0" id="Tab1_IsEventHaveSustainableApproach_No_@obj.Id" />
                                    <label for="Tab1_IsEventHaveSustainableApproach_No_@obj.Id">No</label>
                                </div>
                            </div>
                        </div>
                        <div class="input-box">
                            <label for="Tab1_Checkbox_EventVenue_Other">Other? Please explain</label>
                                                                 @*add name attribute*@
                            <input asp-for="@obj.Checkbox_Other" name="VenueItems[@i].Checkbox_Other" type="text" id="Tab1_Checkbox_EventVenue_Other" />
                        </div>
                    </div>
                </div>
            </div>
            i++;          //add this.........
        }
    </div>
</form>

<button type="button" onclick="saveForm()">Post</button>
@section Scripts
{
    <script>
        async function saveForm() {
            let formData = new FormData(document.forms[0]);   
            await fetch('/Reports/Save', {
                method: 'post',
                body: formData
            })
                .then((response) => {
                    toastr.success('Saved successfully');
                    preventLeaving = false;
                    return true;
                })
                .catch((error) => {
                    toastr.error('An error occured');
                    return false;
                });
        }
    </script>
}

结果: