MVC 6 Tag Helpers as Replacement for Editor Templates
Asked Answered
B

1

13

MVC 6 introduces Tag Helpers as a better alternative to @Html.EditorFor. It is possible to create custom Editor Template. It's also possible to create a custom Tag Helper.

However, when creating the Tag Helper, the HTML needs to be created by C# code (using TagBuilder, etc.). For complex Tag Helpers it's not as convenient as using a Razor syntax.

Is there any way I'm missing that would allow me to create an custom Tag Helper from a Razor page?

Bitternut answered 8/8, 2015 at 16:6 Comment(0)
S
15

The beauty of TagHelpers is that you can intermingle C# and Razor in many different ways. So lets say you have a custom Razor template such as:

CustomTagHelperTemplate.cshtml

@model User

<p>User name: @Model.Name</p>
<p>User id: @Model.Id</p>

And you have the model:

namespace WebApplication1
{
    public class User
    {
        public string Name { get; set; }
        public int Id { get; set; }
    }
}

And the TagHelper:

using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.WebEncoders;

namespace WebApplication1
{
    public class UserTagHelper : TagHelper
    {
        private readonly HtmlHelper _htmlHelper;
        private readonly IHtmlEncoder _htmlEncoder;

        public UserTagHelper(IHtmlHelper htmlHelper, IHtmlEncoder htmlEncoder)
        {
            _htmlHelper = htmlHelper as HtmlHelper;
            _htmlEncoder = htmlEncoder;
        }

        [ViewContext]
        public ViewContext ViewContext
        {
            set
            {
                _htmlHelper.Contextualize(value);
            }
        }

        public string Name { get; set; }

        public int Id { get; set; }

        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            output.TagName = null;
            output.SelfClosing = false;

            var partial = await _htmlHelper.PartialAsync(
                "CustomTagHelperTemplate",
                new User
                {
                    Name = Name,
                    Id = Id
                });

            var writer = new StringWriter();
            partial.WriteTo(writer, _htmlEncoder);

            output.Content.SetContent(writer.ToString());
        }
    }
}

You can then write the following page:

@addTagHelper "*, WebApplication1"

<user id="1234" name="John Doe" />

Which generates:

<p>User name: John Doe</p>
<p>User id: 1234</p>
Sleeve answered 10/8, 2015 at 0:23 Comment(6)
So HTML Helpers are not actually being deprecated, but rather are supplemented by Tag Helpers?Bitternut
Couldn't have put it better myself! Yes :)Sleeve
is it possible to provide the model (User) from my page, let's say the sub-model of my main model, rather than instantiating it in the ProcessAsync method in tag helper class?Neckwear
This does not work in ASP.NET Core 1.1. I get an error in line where you say partial.WriteTo(writer, _htmlEncoder) because WriteTo expects System.Text.Encodings.Web.HtmlEncoder not Microsoft.Extensions.WebEncoders.IHTMLEncoder. If you comment that out and try to run it - you then get InvalidOperationException: Unable to resolve service for type 'Microsoft.Extensions.WebEncoders.IHtmlEncoder' while attempting to activate 'UserTagHelper'.Abhor
This reminds me of the ASP .NET Web Forms days, with the <asp: controls (like asp:TextBox etc) came into play, and you would read one thing in the HTML markup and then get something completely different in the HTML response stream. Bad vibes. Of course I could have it completely wrong.Dictatorial
This strategy only works for readonly purposes, if you wanted to use input elements you have to handle this completely within the tag helper code and not call a partial view. The problem is none of your html input "names" are going to align to your original model once they are rendered, and the core team has decided not to support binding a ModelExpression to a razor field. github.com/aspnet/Razor/issues/926Bruis

© 2022 - 2024 — McMap. All rights reserved.