如何解决使用选择元素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.xxx
或BookAuthors[i].xxx
这样的名称:
<input type="hidden" name="data" value="@guid" />
更新:
之所以无法将列表传递给后端,是因为每次渲染部分视图时,BookAuthors的索引始终为0。您需要渲染html,如下所示:
一个简单的解决方法是设置一个属性以记录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();
//...
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。