Reputation: 3676
If I use Html.EditorFor
on a property of type string then the generated element is by default an <input>
of type 'text'.
Alternatively, if the property has the attribute [DataType(DataType.MultilineText)]
then Html.EditorFor
generates a <textarea>
.
If I'm using tag helpers, however, then I have to choose the element type for myself, e.g. <input asp-for='MyProperty' />
. This can use information about the property to determine the input's type (text, date, email etc), but will always generate an <input>
, never a <textarea>
, even if I have the [DataType]
attribute.
This seems to go against the idea that I can make the change from single line to multiline in one place: the model. Now I have to go through all relevant views and make the change myself.
It seems to me perfectly possible that I could write my own tag helper that could look at the property attributes and decide whether to generate an <input>
or <textarea>
, but I can't find any reference to a predefined one.
Is there such a predefined tag helper, and if not, is there a reason why?
Upvotes: 7
Views: 3743
Reputation: 4020
Expanding on the answer provided by 'Prince Owusu', I have modified it so that the 'Template' property is optional:
[HtmlTargetElement("editor", TagStructure = TagStructure.WithoutEndTag,
Attributes = ForAttributeName)]
public class EditorTagHelper : TagHelper
{
private readonly IHtmlHelper _htmlHelper;
private const string ForAttributeName = "asp-for";
private const string TemplateAttributeName = "asp-template";
[HtmlAttributeName(ForAttributeName)]
public ModelExpression For { get; set; }
[HtmlAttributeName(TemplateAttributeName)]
public string Template { get; set; }
[ViewContext]
[HtmlAttributeNotBound]
public ViewContext ViewContext { get; set; }
public EditorTagHelper(IHtmlHelper htmlHelper)
{
_htmlHelper = htmlHelper;
}
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (output == null)
throw new ArgumentNullException(nameof(output));
if (!output.Attributes.ContainsName(nameof(Template)))
{
output.Attributes.Add(nameof(Template), Template);
}
output.SuppressOutput();
(_htmlHelper as IViewContextAware).Contextualize(ViewContext);
output.Content.SetHtmlContent(_htmlHelper.Editor(For.Name, Template));
await Task.CompletedTask;
}
}
This would be used something like this:
<editor asp-for="MyModel.EmailAddress"/>
Which is equivalent of:
@Html.EditorFor(x => x.MyModel.EmailAddress)
In my usage example, I have not specified a template so ASP.NET will look for a template that matches the type of my 'EmailAddress' property. If I wanted to specify a template I would use it like this :
<editor asp-for="MyModel.EmailAddress" asp-template="MyEmailTemplate"/>
Upvotes: 0
Reputation: 31
The new way of using tag helper equivalent of Html.EditorFor is just by Using The Old HtmlHelper Inside Tag Helper itself. Take a look at this blog and you might find what you need or use my code instead. http://blog.techdominator.com/article/using-html-helper-inside-tag-helpers.html
[HtmlTargetElement("editor", TagStructure = TagStructure.WithoutEndTag,
Attributes = ForAttributeName + "," + TemplateAttributeName)]
public class EditorTagHelper : TagHelper
{
private IHtmlHelper _htmlHelper;
private const string ForAttributeName = "asp-for";
private const string TemplateAttributeName = "asp-template";
[HtmlAttributeName(ForAttributeName)]
public ModelExpression For { get; set; }
[HtmlAttributeName(TemplateAttributeName)]
public string Template { get; set; }
[ViewContext]
[HtmlAttributeNotBound]
public ViewContext ViewContext { get; set; }
public EditorTagHelper(IHtmlHelper htmlHelper)
{
_htmlHelper = htmlHelper;
}
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (output == null)
throw new ArgumentNullException(nameof(output));
output.SuppressOutput();
(_htmlHelper as IViewContextAware).Contextualize(ViewContext);
output.Content.SetHtmlContent(_htmlHelper.Editor(For.Name, Template));
await Task.CompletedTask;
}
}
Upvotes: 2
Reputation: 2414
Up until now there is no tag helper to achieve this but you can create you own similar to this.
[HtmlTargetElement("editor-for")]
public class EditorForTagHelper : TagHelper
{
private const string TemplateValuesDictionaryName = "template-all-data";
private const string HtmlAttributesDictionaryName = "html-all-data";
private IDictionary<string, object> _templateValues;
private IDictionary<string, string> _htmlAttributes;
public ModelExpression For
{
get; set;
}
/// <summary>
/// Display template name to use for rendering
/// </summary>
public string Template
{
get; set;
}
/// <summary>
/// Additional parameters for the template.
/// </summary>
[HtmlAttributeName(TemplateValuesDictionaryName, DictionaryAttributePrefix = "template-")]
public IDictionary<string, object> TemplateValues
{
get
{
if (_templateValues == null)
_templateValues = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
return _templateValues;
}
set
{
_templateValues = value;
}
}
/// <summary>
/// Additional htmlattributes for the control template
/// </summary>
[HtmlAttributeName(HtmlAttributesDictionaryName, DictionaryAttributePrefix = "html-")]
public IDictionary<string, string> HtmlAttributes
{
get
{
if (_htmlAttributes == null)
_htmlAttributes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
return _htmlAttributes;
}
set
{
_htmlAttributes = value;
}
}
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext
{
get; set;
}
/// <summary>
/// Creates a new <see cref="DisplayForTagHelper"/>.
/// </summary>
public EditorForTagHelper()
{
}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (output == null)
throw new ArgumentNullException(nameof(output));
if (For != null)
{
TemplateValues.AddOrUpdate("htmlAttributes", HtmlAttributes.ToDictionary(e => e.Key, e => (object)e.Value));
var control = ViewContext.GenerateEditor(For,For.Name, Template, TemplateValues);
output.Content.AppendHtml(control);
}
}
}
Upvotes: 0
Reputation: 239290
For what it's worth, some of what you discuss is actually part of the InputTagHelper
. It will dynamically switch between different input types based on the property type (checkbox for bools, etc.). However, as you indicated, it will only ever generate an input, as it's explicitly an input taghelper. There's an entirely different taghelper for textareas. It is technically possible to create a custom tag helper to achieve what you want, but there's a good deal of work that goes into that. For a reference, see the source for InputTagHelper
. At this time, it's not possible to call other tag helpers from a tag helper, so you would need to basically merge the entire code for InputTagHelper
with that of TextAreaTagHelper
into a custom tag helper. You could potentially save yourself a little work by inheriting your custom tag helper from InputTagHelper
and then overriding Process
to call base.Process()
and then customizing the output. However, again, this is more complicated than a simple answer here can go into.
Long and short, yes, this is achievable with a custom tag helper, but it's going to require some work. Personally, I think it's a bit more work than it's worth, considering it's just one case: switching input with textarea for a multiline input, and you can technically still use EditorFor
if your prefer.
If you want to go down this path, Microsoft has an article/tutorial that will give you a good start. Other than that, just study the source for the existing helpers.
Upvotes: 1