I'm trying to create a custom Tag Helper that would essentially be replacing an Editor Template in versions of MVC prior to Core. I want to allow the ability to edit items in a collection. For now, I just want to be able to edit existing items but eventually would extend it to allow adding and removing items dynamically.
From what I understand this should be achievable with Tag Helpers. I can get the existing collection to display using my tag helper but the items aren't being included in the view model when the form is posted.
I have these two Tag Helpers:
SongListTagHelper.cs
[HtmlTargetElement("songlist", TagStructure = TagStructure.NormalOrSelfClosing)]
public class SongListTagHelper : TagHelper
{
private readonly HtmlHelper _htmlHelper;
public ICollection<SongViewModel> Songs { get; set; }
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }
public SongListTagHelper(IHtmlHelper htmlHelper)
{
_htmlHelper = htmlHelper as HtmlHelper;
}
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.TagName = null;
_htmlHelper.Contextualize(ViewContext);
var partial = await _htmlHelper.PartialAsync(
"~/Views/Shared/TagHelperTemplates/SongList.cshtml",
Songs);
output.Content.SetHtmlContent(partial);
}
}
SongTagHelper.cs
[HtmlTargetElement("song", TagStructure = TagStructure.NormalOrSelfClosing)]
public class SongTagHelper : TagHelper
{
private readonly HtmlHelper _htmlHelper;
public SongViewModel Song { get; set; }
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }
public SongTagHelper(IHtmlHelper htmlHelper)
{
_htmlHelper = htmlHelper as HtmlHelper;
}
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.TagName = null;
_htmlHelper.Contextualize(ViewContext);
var partial = await _htmlHelper.PartialAsync(
"~/Views/Shared/TagHelperTemplates/Song.cshtml",
Song);
output.Content.SetHtmlContent(partial);
}
}
And the respective views:
SongList.cshtml
@model ICollection<SongViewModel>
@foreach (var item in @Model)
{
<song song="item" />
}
Song.cshtml
@model SongViewModel
<div class="row">
<input asp-for="SongId" />
<div class="col-md-2">
<input asp-for="TrackNumber" placeholder="##" class="form-control" />
<span asp-validation-for="TrackNumber" class="text-danger"></span>
</div>
<div class="col-md-6">
<input asp-for="Title" placeholder="Enter Song Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="col-md-4">
<input asp-for="Duration" placeholder="00:00" class="form-control" />
<span asp-validation-for="Duration" class="text-danger"></span>
</div>
</div>
In my controller action I'm adding a blank item to the collection initially and passing that to the view:
var vm = new EditViewModel();
vm.SongList.Add(new SongViewModel());
return View(vm);
Then in the main view file I pass the collection to the Tag Helper like this:
<div class="form-group">
<label class="col-md-2 control-label">Song List</label>
<div class="col-md-10">
<songlist songs="@Model.SongList"></songlist>
</div>
</div>
When I run this and enter values in the TrackNumber, Title and Duration fields and submit the form, the SongList collection in the view model has 0 items.
How can I get it to properly bind to the view model when the form is submitted?
You need to use HtmlAttributeName
attribute to specify what html attribute assigns the property.
...
[HtmlAttributeName("songs")]
public ICollection<SongViewModel> Songs { get; set; }
...
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.