使用选择元素ASP.NET Core MVC中的属性保存父项和子项

如何解决使用选择元素ASP.NET Core MVC中的属性保存父项和子项

在我正在使用的ASP.NET Core MVC Web应用程序中,我有一个问题,将在下面的示例中进行说明。

比方说,用户必须保存带有书名和页数的书以及一个或多个作者。对于每个作者,用户都应从下拉列表中选择其姓名和角色。

模型有四个类:

public class Author
{
    public int AuthorID { get; set; }
    public string AuthorName { get; set; }

    public Author(int id,string name)
    {
        AuthorID = id;
        AuthorName = name;
    }
}

public class AuthorRole
{
    public int AuthorRoleID { get; set; }
    public string AuthorRoleName { get; set; }

    public AuthorRole(int id,string name)
    {
        AuthorRoleID = id;
        AuthorRoleName = name;
    }
}

public class BookAuthor
{
    public int? BookAuthorID { get; set; }
    public int? BookID { get; set; }
    [DisplayName("Name")]
    public int? AuthorID { get; set; }
    [DisplayName("Role")]
    public int? AuthorRoleID { get; set; }
}

public class BookViewModel
{
    public int? BookID { get; set; }
    [DisplayName("Title")]
    public string Title { get; set; }
    [DisplayName("Number of pages")]
    public int NumberOfPages { get; set; }
    public SelectList AuthorsList { get; set; }
    public SelectList AuthorRolesList { get; set; }

    [DisplayName("Authors")]
    public List<BookAuthor> BookAuthors { get; set; }
    public BookViewModel()
    {
        BookAuthors = new List<BookAuthor>();
    }
}

表单初始化操作

[HttpGet]
        public async Task<IActionResult> AddBook()
        {
            BookViewModel book = new BookViewModel();
            PopulateDropdowns(book);
            book.BookAuthors.Add(new BookAuthor());
            return View(book);
        }

        private void PopulateDropdowns(BookViewModel book)
        {
            List<Author> authors = new List<Author>();
            authors.Add(new Author(1,"Morgan Beaman"));
            authors.Add(new Author(2,"Cleveland Lemmer"));
            authors.Add(new Author(3,"Joanie Wann"));
            authors.Add(new Author(4,"Emil Shupp"));
            authors.Add(new Author(5,"Zenia Witts"));
            book.AuthorsList = new SelectList(authors,"AuthorID","AuthorName");

            List<AuthorRole> authorRoles = new List<AuthorRole>();
            authorRoles.Add(new AuthorRole(1,"Author"));
            authorRoles.Add(new AuthorRole(2,"Editor"));
            authorRoles.Add(new AuthorRole(3,"Translator"));
            book.AuthorRolesList = new SelectList(authorRoles,"AuthorRoleID","AuthorRoleName");
        }

返回视图:

@{
    Layout = "_DetailsLayout";
}

@model Test.BookViewModel

<div class="container-fluid">
    <div class="row justify-content-center">
        <div class="col-sm-8 col-lg-6">
            <form asp-action="SaveBook" class="form-horizontal">
                <div asp-validation-summary="ModelOnly" class="text-danger"></div>
                <input type="hidden" asp-for="BookID" />
                <div class="form-group">
                    <label asp-for="Title" class="control-label"></label>
                    <input asp-for="Title" class="form-control" />
                    <span asp-validation-for="Title" class="text-danger"></span>
                </div>

                <div class="form-group">
                    <label asp-for="NumberOfPages" class="control-label"></label>
                    <input asp-for="NumberOfPages" class="form-control" />
                    <span asp-validation-for="NumberOfPages" class="text-danger"></span>
                </div>

                <label asp-for="BookAuthors" class="control-label"></label>
                @await Html.PartialAsync("_AuthorDetails",Model)
                <div id="AuthorDetailsWrapper"></div>

                <div class="clearfix">
                    <a class="btn btn-link text-success btn-sm float-right" data-ajax="true"
                       data-ajax-url="@Url.Action("AddAuthor",new { id = Model.BookID })"
                       data-ajax-method="GET" data-ajax-mode="after"
                       data-ajax-loading-duration="400"
                       data-ajax-update="#AuthorDetailsWrapper" href="">ADD AUTHOR</a>
                </div>
                <div class="row mt-5">
                    <div class="col">
                        <a asp-action="Index" class="btn btn-outline-secondary block-on-mobile mb-2">Back</a>
                    </div>
                    <div class="col">
                        <button type="submit" class="btn btn-success float-right block-on-mobile">Save</button>
                    </div>
                </div>
            </form>
        </div>
    </div>
</div>

如果书中有一位以上的作者,则用户应点击“添加作者”来调用操作

[HttpGet]
        public IActionResult AddAuthor(int? bookid)
        {
            BookViewModel book = new BookViewModel();
            BookAuthor newAuthor = new BookAuthor();
            if (bookid != null)
            {
                newAuthor.BookID = bookid;
            }
            PopulateDropdowns(book);
            book.BookAuthors.Add(newAuthor);
            return PartialView("_AuthorDetails",book);
        }

返回视图:

@model Test.BookViewModel

@for (var i = 0; i < Model.BookAuthors.Count; i++)
{
    string guid = Guid.NewGuid().ToString();
    <div class="form-row">
        <input type="hidden" name="BookAuthors.Index" value="@guid" />
        <input asp-for="@Model.BookAuthors[i].BookID" type="hidden" data-guid="@guid" />
        <input asp-for="@Model.BookAuthors[i].BookAuthorID" type="hidden" data-guid="@guid" />
        <div class="form-group col">
            <label asp-for="@Model.BookAuthors[i].AuthorID"></label>
            <select asp-for="@Model.BookAuthors[i].AuthorID" asp-items="Model.AuthorsList" data-guid="@guid" class="dropdowns">
                <option></option>
            </select>
            <span asp-validation-for="@Model.BookAuthors[i].AuthorID" class="text-danger" data-guid="@guid"></span>
        </div>
        <div class="form-group col">
            <label asp-for="@Model.BookAuthors[i].AuthorRoleID"></label>
            <select asp-for="@Model.BookAuthors[i].AuthorRoleID" asp-items="Model.AuthorRolesList" data-guid="@guid" class="dropdowns">
                <option></option>
            </select>
            <span asp-validation-for="@Model.BookAuthors[i].AuthorRoleID" data-guid="@guid" class="text-danger"></span>
        </div>
    </div>
}

在提交表单时,应将模型传递给行动:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SaveBook(BookViewModel book)
{
    // validation and saving book to database
    }

具有所有属性-页面的标题和页数,以及作者的整个列表,每个列表都有AuthorID和AuthorRoleID。
问题在于作者的属性(AuthorID和AuthorRoleID,是从下拉菜单中选择的)没有传递,即具有空值。
例如,如果我填写这样的表格 Filled form,with two authors 对于列表BookAuthors的两个元素,preoperities AuthorID和AuthorRoleID为null: Data not passed to action

我认为出现问题是因为AuthorID(和AuthorRoleID)的“ asp-for”标记帮助程序为选择元素的“ id”和“ name”属性生成了错误的值-它使用0作为索引,而不是guid。所以代替

<select data-guid="283a0dea-9d64-432f-a03d-8c1a167b062a" class="dropdowns" id="BookAuthors_283a0dea-9d64-432f-a03d-8c1a167b062a__AuthorID" name="BookAuthors[283a0dea-9d64-432f-a03d-8c1a167b062a].AuthorID">
                <option></option>
            <option value="1">Morgan Beaman</option>
<option value="2">Cleveland Lemmer</option>
<option value="3">Joanie Wann</option>
<option value="4">Emil Shupp</option>
<option value="5">Zenia Witts</option>
</select>

元素看起来像这样

<select data-guid="283a0dea-9d64-432f-a03d-8c1a167b062a" class="dropdowns" id="BookAuthors_0__AuthorID" name="BookAuthors[0].AuthorID">
                <option></option>
            <option value="1">Morgan Beaman</option>
<option value="2">Cleveland Lemmer</option>
<option value="3">Joanie Wann</option>
<option value="4">Emil Shupp</option>
<option value="5">Zenia Witts</option>
</select>

如果在提交之前在DevTools中编辑此元素(我将零替换为guid值),则所有内容均按预期提交。

令我困惑的是,如果AuthorID不是select元素,而是input元素,那么相同的代码就可以工作。例如,在_AuthorDetails.cshtml部分视图中,如果我替换

<select asp-for="@Model.BookAuthors[i].AuthorID" asp-items="Model.AuthorsList" data-guid="@guid" class="dropdowns">
                <option></option>
            </select>

使用

<input asp-for="@Model.BookAuthors[i].AuthorID" data-guid="@guid" />

生成的元素是:

<input data-guid="926eb580-d52f-4132-ab43-b8fbb63e3d2c" type="number" id="BookAuthors_926eb580-d52f-4132-ab43-b8fbb63e3d2c__AuthorID" name="BookAuthors[926eb580-d52f-4132-ab43-b8fbb63e3d2c].AuthorID" value="" minlength="0">

如果我手动输入作者的ID并提交表单,则会传递数据: Filled part of form,5 is entered for author's id Data passed to action

那么,如何解决此代码,以便使用正确的索引生成选择元素?
同样,还可以选择其他解决方案和一般改进技巧。可能的替代解决方案应允许在同一页面上添加(并随后使用同一视图进行编辑)具有任意数量作者的书...

解决方法

如果我填写这样的表单,并为列表BookAuthors的两个元素添加了两个作者,则前置操作AuthorID和AuthorRoleID为空

这是因为您的局部视图包含一个输入元素,该输入元素不是BookAuthors中的属性,但是您将其定义为BookAuthors.Index,因此模型绑定系统会误解它:

<input type="hidden" name="BookAuthors.Index" value="@guid" />

要解决此问题,只需更改如下名称,避免使用BookAuthors.xxxBookAuthors[i].xxx这样的名称:

<input type="hidden" name="data" value="@guid" />

更新

之所以无法将列表传递给后端,是因为每次渲染部分视图时,BookAuthors的索引始终为0。您需要渲染html,如下所示: enter image description here

一个简单的解决方法是设置一个属性以记录BookAuthors的索引。如下更改代码:

型号:

public class BookViewModel
{
    //more properties...
    //add Index property...
    public int Index { get; set; }

    [DisplayName("Authors")]
    public List<BookAuthor> BookAuthors { get; set; }
    public BookViewModel()
    {
        BookAuthors = new List<BookAuthor>();
    }
}

_AuthorDetails.cshtml:

请勿使用标签助手。由于单击添加作者按钮时,索引将更改为1,并且标签助手不允许列表从1开始计数。请使用name属性代替标签助手。

@model BookViewModel

@{
    string guid = Guid.NewGuid().ToString();
}
<div class="form-row">
    <input type="hidden" name="Index" value="@guid" />
    <input name="BookAuthors[@Model.Index].BookID" type="hidden" data-guid="@guid" />
    <input name="BookAuthors[@Model.Index].BookAuthorID" type="hidden" data-guid="@guid" />
    <div class="form-group col">
        <label asp-for="BookAuthors[Model.Index].AuthorID"></label>
        <select name="BookAuthors[@Model.Index].AuthorID" asp-items="Model.AuthorsList" data-guid="@guid" class="dropdowns">
            <option></option>
        </select>
        <span asp-validation-for="BookAuthors[Model.Index].AuthorID" class="text-danger" data-guid="@guid"></span>
    </div>
    <div class="form-group col">
        <label asp-for="BookAuthors[Model.Index].AuthorRoleID"></label>
        <select name="BookAuthors[@Model.Index].AuthorRoleID" asp-items="Model.AuthorRolesList" data-guid="@guid" class="dropdowns">
            <option></option>
        </select>
        <span asp-validation-for="BookAuthors[Model.Index].AuthorRoleID" data-guid="@guid" class="text-danger"></span>
    </div>
</div>

控制器:

[HttpGet]
public IActionResult AddAuthor(int? bookid)
{
    var index = HttpContext.Session.GetInt32("item").Value;
    BookViewModel book = new BookViewModel();
    BookAuthor newAuthor = new BookAuthor();
    if (bookid != null)
    {
        newAuthor.BookID = bookid;
    }
    PopulateDropdowns(book);
    book.BookAuthors.Add(newAuthor);
    book.Index= index+1;
    HttpContext.Session.SetInt32("item",book.Index);
    return PartialView("_AuthorDetails",book);
}
public IActionResult AddBook()
{                      
    BookViewModel book = new BookViewModel();
    PopulateDropdowns(book);
    book.BookAuthors.Add(new BookAuthor());

    book.Index = 0;
    HttpContext.Session.SetInt32("item",book.Index);

    return View(book);
}

确保在Startup.cs中注册会话:

public void ConfigureServices(IServiceCollection services)
{       
    services.AddSession();
    //....
}

public void Configure(IApplicationBuilder app,IWebHostEnvironment env)
{        
    app.UseSession();
    //...
}

结果: enter image description here

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res