The book Pro ASP.NET Core 3 has the the following code in its example for chapter 27:
<div class="m-2">
<h5 class="bg-primary text-white text-center p-2">HTML Form</h5>
<form asp-page="FormHandler" method="post" id="htmlform">
<div class="form-group">
<label>Name</label>
<input class="form-control" asp-for="Product.Name" />
</div>
<div class="form-group">
<label>Price</label>
<input class="form-control" asp-for="Product.Price" />
</div>
<div class="form-group">
<label>Category</label>
<input class="form-control" asp-for="Product.Category.Name" />
</div>
<div class="form-group">
<label>Supplier</label>
<input class="form-control" asp-for="Product.Supplier.Name" />
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<button form="htmlform" asp-page="FormHandler" class="btn btn-primary mt-2">
Sumit (Outside Form)
</button>
</div>
As you can see, the <div class="form-group>...</div>
pattern is repeated four times. As a test of partials, I added the file Pages\_FormGroupPartial.cshtml
:
@model FormGroupParams
<div class="form-group">
<label>@Model.LabelContent</label>
<input class="form-control" asp-for="@Model.ForString" />
</div>
In Pages\FormHandler.cshtml.cs
I added the following class:
public class FormGroupParams
{
public string LabelContent;
public string ForString;
public FormGroupParams(string labelContent, string forString)
{
LabelContent = labelContent;
ForString = forString;
}
}
To test this out, I edited Pages\FormHandler.cshtml
to use the new partial right after the original approach:
<div class="form-group">
<label>Supplier</label>
<input class="form-control" asp-for="Product.Supplier.Name" />
</div>
<partial name="_FormGroupPartial" model='new FormGroupParams("Supplier", "Product.Supplier.Name")' />
In the resulting page, we see that the partial did not quite generate the same code; note that the value is now Product.Supplier.Name
instead of the expected Splash Dudes
:
Looking at the source, we see that the original code:
<div class="form-group">
<label>Supplier</label>
<input class="form-control" asp-for="Product.Supplier.Name" />
</div>
expanded into:
<div class="form-group">
<label>Supplier</label>
<input class="form-control" type="text" id="Product_Supplier_Name" name="Product.Supplier.Name" value="Splash Dudes" />
</div>
Whereas our partial:
<partial name="_FormGroupPartial" model='new FormGroupParams("Supplier", "Product.Supplier.Name")' />
expanded into:
<div class="form-group">
<label>Supplier</label>
<input class="form-control" type="text" id="ForString" name="ForString" value="Product.Supplier.Name" />
</div>
Is there a good way to implement this partial so as to get the same output as the original code?
UPDATE 2020-09-30
PeterG suggests the following in his answer below:
<partial name="_FormGroupPartial" model='new FormGroupParams("Supplier", @Model.Product.Supplier.Name)' />
This expands into the following:
<div class="form-group">
<label>Supplier</label>
<input class="form-control" type="text" id="ForString" name="ForString" value="Splash Dudes" />
</div>
whereas the goal is the following:
<div class="form-group">
<label>Supplier</label>
<input class="form-control" type="text" id="Product_Supplier_Name" name="Product.Supplier.Name" value="Splash Dudes" />
</div>
So the value
is now correct. But the id
and name
are still off.
The author of the book, Adam Freeman, has provided an example to me via email which demonstrates solving this using a tag helper.
Add the file TagHelpers/FormGroupTagHelper.cs
:
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
namespace WebApp.TagHelpers
{
[HtmlTargetElement("form-group")]
public class FormGroupTagHelper : TagHelper
{
public string Label { get; set; }
public ModelExpression For { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "div";
output.TagMode = TagMode.StartTagAndEndTag;
output.Attributes.SetAttribute("class", "form-group");
var label = new TagBuilder("label");
label.InnerHtml.Append(Label ?? For.Name);
var input = new TagBuilder("input");
input.AddCssClass("form-control");
input.Attributes["type"] = "text";
input.Attributes["id"] = For.Name.Replace(".", "_");
input.Attributes["name"] = For.Name;
input.Attributes["value"] = For.Model.ToString();
output.Content.AppendHtml(label);
output.Content.AppendHtml(input);
}
}
}
Now, in Pages/FormHandler.cshtml
instead of this:
<div class="form-group">
<label>Supplier</label>
<input class="form-control" asp-for="Product.Supplier.Name" />
</div>
you can use the following:
<form-group label="Supplier" for="Product.Supplier.Name"/>
Thanks Adam!