在表单中呈现列表 <Model> 并发布到控制器
Rendering a List<Model> in form and posting to controller
我正在使用 ViewComponent 渲染递归树结构,我在表单中使用 Html.HiddenFor
和 Html.CheckBoxFor
时遇到困难。
这是视图模型:
public class ViewModelProductCategory
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Title { get; set; }
public int SortOrder { get; set; }
public bool Checked { get; set; }
public ViewModelProductCategory ParentCategory { get; set; }
public IEnumerable<ViewModelProductCategory> Children { get; set; }
public IEnumerable<ViewModelProduct> Products { get; set; }
}
ViewComponent 正在从主视图页面调用,如下所示:
@await Component.InvokeAsync("SelectCategories",
new
{
parentId = 0,
productId = Model.Id // This is the current product Id from the main View
})
...这是 ViewComponent 的调用方法:
public async Task<IViewComponentResult> InvokeAsync(int? parentId, int productId)
{
List<ViewModelProductCategory> VM = new List<ViewModelProductCategory>();
if (parentId == 0)
{
VM = _mapper.Map<List<ViewModelProductCategory>>
(await _context.ProductCategories.Include(c => c.Children)
.Where(x => x.ParentId == null).OrderBy(o => o.SortOrder).ToListAsync());
}
else
{
VM = _mapper.Map<List<ViewModelProductCategory>>
(await _context.ProductCategories.Include(c => c.Children)
.Where(x => x.ParentId == parentId).OrderBy(o => o.SortOrder).ToListAsync());
}
foreach (var item in VM)
{
// The Checked-value is not stored in the database, but is set here based
// on the occurances of ProductId in the navigation property Products.Id
// I'm not entirely confident that this statement checks the correct checkboxes...
item.Checked = item.Products.Any(c => c.Id == productId);
}
ViewData["productId"] = productId;
return View(VM);
}
这是 ViewComponent 的 Default.cshtml
:
@model List<MyStore.Models.ViewModels.ViewModelProductCategory>
<ul style="list-style:none;padding-left:0px;">
@if (Model != null)
{
int ProductId = (ViewData["productId"] != null)
? int.Parse(ViewData["productId"].ToString())
: 0;
@for (int i = 0; i < Model.Count(); i++)
{
<li style="margin-top:4px;padding:0px;">
@if (Model[i].Children.Count() == 0)// Prevent products from being placed in a category with child categories
// by not rendering a checkbox next to it
{
@Html.HiddenFor(model => Model[i].Id)
@Html.CheckBoxFor(model => Model[i].Checked)
}
@Html.LabelFor(model => Model[i].Id, Model[i].Title)
<ul>
@*Let's recurse!*@
@await Component.InvokeAsync("SelectCategories",
new
{
parentId = Model[i].Id,
productId = ProductId
})
</ul>
</li>
}
}
</ul>
输出这个HTML(这是它的一部分):
<input id="z0__Id" name="[0].Id" type="hidden" value="1003" />
<input checked="checked" id="z0__Checked" name="[0].Checked" type="checkbox" value="true" />
<label for="z0__Id">Up to 20"</label>
<input data-val="true" data-val-required="The Id field is required." id="z1__Id" name="[1].Id" type="hidden" value="1004" />
<input checked="checked" data-val="true" data-val-required="The Checked field is required." id="z1__Checked" name="[1].Checked" type="checkbox" value="true" />
<label for="z1__Id">21" - 40"</label>
<input data-val="true" data-val-required="The Id field is required." id="z2__Id" name="[2].Id" type="hidden" value="1005" />
<input checked="checked" data-val="true" data-val-required="The Checked field is required." id="z2__Checked" name="[2].Checked" type="checkbox" value="true" />
<label for="z2__Id">41" - 55"</label>
<!-- ...and so on ... -->
我在这里面临(至少)两个挑战:
如果在导航 属性 [=22] 中找到局部变量 productId
的值,则语句 item.Products.Any(c => c.Id == productId);
的计算结果应为 true
=],这意味着该特定产品链接到该特定类别(通过 item.Products
),但现在它正在检查父类别中的所有兄弟类别。
我认为 HTML- 输出不正确,至少对于 name
-属性: <input checked="checked" id="z0__Checked" name="[0].Checked" type="checkbox" value="true" />
。命名为 [0].Checked
可以吗?
我还没有写控制器的 POST
-edit 方法,所以我不把它包括在这个问题中。在其中我必须做一些从一种模型到另一种模型的转换,以及一些其他的事情。如果我能让表单正确呈现,我可以将它绑定到控制器。
是的,我预计呈现的 html 名称可能对模型绑定无效。您应该添加一个父模型以包含此 class:
的列表
public class ViewModelX
{
public List<ViewModelProductCategory> Categories = new List<ViewModelProductCategory>();
}
然后在你的视图和控制器中使用它:
@model ViewModelX
@Html.HiddenFor(model => model.Categories[i].Id)
@Html.CheckBoxFor(model => model.Categories[i].Checked)
// etc
_mapper.Map<ViewModelX> ...
这将在 html 中呈现:
name="Categories[0].Id"
我正在使用 ViewComponent 渲染递归树结构,我在表单中使用 Html.HiddenFor
和 Html.CheckBoxFor
时遇到困难。
这是视图模型:
public class ViewModelProductCategory
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Title { get; set; }
public int SortOrder { get; set; }
public bool Checked { get; set; }
public ViewModelProductCategory ParentCategory { get; set; }
public IEnumerable<ViewModelProductCategory> Children { get; set; }
public IEnumerable<ViewModelProduct> Products { get; set; }
}
ViewComponent 正在从主视图页面调用,如下所示:
@await Component.InvokeAsync("SelectCategories",
new
{
parentId = 0,
productId = Model.Id // This is the current product Id from the main View
})
...这是 ViewComponent 的调用方法:
public async Task<IViewComponentResult> InvokeAsync(int? parentId, int productId)
{
List<ViewModelProductCategory> VM = new List<ViewModelProductCategory>();
if (parentId == 0)
{
VM = _mapper.Map<List<ViewModelProductCategory>>
(await _context.ProductCategories.Include(c => c.Children)
.Where(x => x.ParentId == null).OrderBy(o => o.SortOrder).ToListAsync());
}
else
{
VM = _mapper.Map<List<ViewModelProductCategory>>
(await _context.ProductCategories.Include(c => c.Children)
.Where(x => x.ParentId == parentId).OrderBy(o => o.SortOrder).ToListAsync());
}
foreach (var item in VM)
{
// The Checked-value is not stored in the database, but is set here based
// on the occurances of ProductId in the navigation property Products.Id
// I'm not entirely confident that this statement checks the correct checkboxes...
item.Checked = item.Products.Any(c => c.Id == productId);
}
ViewData["productId"] = productId;
return View(VM);
}
这是 ViewComponent 的 Default.cshtml
:
@model List<MyStore.Models.ViewModels.ViewModelProductCategory>
<ul style="list-style:none;padding-left:0px;">
@if (Model != null)
{
int ProductId = (ViewData["productId"] != null)
? int.Parse(ViewData["productId"].ToString())
: 0;
@for (int i = 0; i < Model.Count(); i++)
{
<li style="margin-top:4px;padding:0px;">
@if (Model[i].Children.Count() == 0)// Prevent products from being placed in a category with child categories
// by not rendering a checkbox next to it
{
@Html.HiddenFor(model => Model[i].Id)
@Html.CheckBoxFor(model => Model[i].Checked)
}
@Html.LabelFor(model => Model[i].Id, Model[i].Title)
<ul>
@*Let's recurse!*@
@await Component.InvokeAsync("SelectCategories",
new
{
parentId = Model[i].Id,
productId = ProductId
})
</ul>
</li>
}
}
</ul>
输出这个HTML(这是它的一部分):
<input id="z0__Id" name="[0].Id" type="hidden" value="1003" />
<input checked="checked" id="z0__Checked" name="[0].Checked" type="checkbox" value="true" />
<label for="z0__Id">Up to 20"</label>
<input data-val="true" data-val-required="The Id field is required." id="z1__Id" name="[1].Id" type="hidden" value="1004" />
<input checked="checked" data-val="true" data-val-required="The Checked field is required." id="z1__Checked" name="[1].Checked" type="checkbox" value="true" />
<label for="z1__Id">21" - 40"</label>
<input data-val="true" data-val-required="The Id field is required." id="z2__Id" name="[2].Id" type="hidden" value="1005" />
<input checked="checked" data-val="true" data-val-required="The Checked field is required." id="z2__Checked" name="[2].Checked" type="checkbox" value="true" />
<label for="z2__Id">41" - 55"</label>
<!-- ...and so on ... -->
我在这里面临(至少)两个挑战:
如果在导航 属性 [=22] 中找到局部变量
productId
的值,则语句item.Products.Any(c => c.Id == productId);
的计算结果应为true
=],这意味着该特定产品链接到该特定类别(通过item.Products
),但现在它正在检查父类别中的所有兄弟类别。我认为 HTML- 输出不正确,至少对于
name
-属性:<input checked="checked" id="z0__Checked" name="[0].Checked" type="checkbox" value="true" />
。命名为[0].Checked
可以吗?
我还没有写控制器的 POST
-edit 方法,所以我不把它包括在这个问题中。在其中我必须做一些从一种模型到另一种模型的转换,以及一些其他的事情。如果我能让表单正确呈现,我可以将它绑定到控制器。
是的,我预计呈现的 html 名称可能对模型绑定无效。您应该添加一个父模型以包含此 class:
的列表public class ViewModelX
{
public List<ViewModelProductCategory> Categories = new List<ViewModelProductCategory>();
}
然后在你的视图和控制器中使用它:
@model ViewModelX
@Html.HiddenFor(model => model.Categories[i].Id)
@Html.CheckBoxFor(model => model.Categories[i].Checked)
// etc
_mapper.Map<ViewModelX> ...
这将在 html 中呈现:
name="Categories[0].Id"