28

We are experiencing the following error when build ASP.NET Core MVC application which is recently migrated from .NET Core 2.2 to 3.0:

The tag helper 'input' (or 'textarea' or any other) must not have C# in the element's attribute declaration area.

We used Razor @functions to return HTML attribute content in order to overcome that issue but it looks ugly when you use variable for returning it from function without any extra logic (function dummy(htmlAttributeContent) return htmlAttributeContent)

@{
    var roAttrHTML = "";
    if (!user.GotAccessToken("someToken")) {
        roAttrHTML = " readonly=\"readonly\" ";
    }
}
<textarea class="testClass" asp-for="testId" @readonlyAttribute>@Model.Id</textarea>

Actually we got error

The tag helper 'textarea' must not have C# in the element's attribute declaration area.

when compile our ASP.NET Core MVC application whereas we need to get approach (better without @functions usage) which will provide us a way to overcome that issue (as we have plenty of pages with similar logic which we need to touch once and avoid any possible problems with presumable new changes in support of attributes in the future .NET Core versions)

Milo
  • 3,365
  • 9
  • 30
  • 44
Alexey
  • 281
  • 1
  • 3
  • 3
  • 3
    How did you define `@readonlyAttribute`?I could also get the same error in asp.net core 2.2.Why not try `readonly=@readonlyAttribute`? – Rena Nov 13 '19 at 03:26
  • @Rena that should be the accepted answer! – MDummy Jul 06 '20 at 22:15
  • 1
    @MDummy actually, that does nothing, because it doesn't matter what value there is for `readonly` attribute, once it's there, the field is readonly – Matus May 01 '21 at 10:32

7 Answers7

24

If you don't need to use the TagHelper, you can use <!elementName> to disable it for a specific element:

<!textarea class="testClass" asp-for="testId" @readonlyAttribute>@Model.Id</!textarea>

See @glenn223's answer for a more structural solution. I made an improved version of his solution, by adding support for anonymous objects:

using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;

namespace  {YourBaseNameSpace}.Helpers.TagHelpers
{
    [HtmlTargetElement(Attributes = "custom-attributes")]
    public class CustomAttributesTagHelper : TagHelper
    {
        public object CustomAttributes { get; set; }

        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            var customAttributesDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(CustomAttributes);
            foreach (var (key, value) in customAttributesDictionary)
            {
                output.Attributes.SetAttribute(key, value);
            }
        }
    }
}
Mark Lagendijk
  • 6,247
  • 2
  • 36
  • 24
19

According to the docs, you can do it this way, in ASP .NET 5.0

<input asp-for="LastName" 
       disabled="@(Model?.LicenseId == null)" />

https://learn.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro?view=aspnetcore-5.0#c-in-tag-helpers-attributedeclaration

awhig
  • 507
  • 1
  • 7
  • 13
11

You can do it like this:

@if (!user.GotAccessToken("someToken")) 
{
   <textarea class="testClass" asp-for="testId" readonly>@Model.Id</textarea>
}
else
{
   <textarea class="testClass" asp-for="testId">@Model.Id</textarea>
}
Matus
  • 410
  • 5
  • 15
6

I got the same problem, but i got an easy solution:

Code that throws the error:

<select asp-for="Journeys.VehicleType" class="test" style="width: 100%;" id="vehicleType" @disabled>

Code that works:

<select @disabled asp-for="Journeys.VehicleType" class="test" style="width: 100%;" id="vehicleType">

All i did to make my IDE doesnt throw the error is moving the @disabled attribute infront of the "asp-for" attribute.

Also works for normal input fields:

Code that throws the error:

<input  type="datetime-local" asp-for="Journeys.StartTime" @disabled id="startTime"/>

Code that works:

 <input @disabled type="datetime-local" asp-for="Journeys.StartTime" id="startTime"/>

Info: @disabled is just a variable which contains empty string or "disabled":

var disabled = "";   
if (Model.Disabled)
{
    disabled = "disabled";       
}

Hope this will help some people with the same problem.

Pino
  • 61
  • 1
  • 2
3

I encountered this same problem, when creating a new 3.0 application. An old 2.2 application allowed me to have C# in the declaration even though "it's not valid"1.

What I did was author my own TagHelper2, using the below steps:

  1. Create file: CustomAttributeTagHelper .cs
namespace {YourBaseNameSpace}.Helpers.TagHelpers
{
    using System.Collections.Generic;
    using Microsoft.AspNetCore.Razor.TagHelpers;

    [HtmlTargetElement(Attributes = "custom-attributes")]
    public class CustomAttributeTagHelper : TagHelper
    {
        public Dictionary<string, string> CustomAttributes { get; set; }

        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            if (CustomAttributes != null)
                foreach (var pair in CustomAttributes)
                    if (!output.Attributes.ContainsName(pair.Key))
                        output.Attributes.Add(pair.Key, pair.Value);
        }
    }
}
  1. Modify: /Pages/Shared/_ViewImports.cshtml, adding the below line below Microsoft's own @addTagHelper
@addTagHelper {YourBaseNameSpace}.Helpers.TagHelpers.*, {Your Assembly Name}

KEY POINT: ensure you use the assembly name and not the namespace after the comma above

2a. If you're not sure what your assembly name is then run the below code in your Program => Main(string[] args) method:

Type t = typeof({YourBaseNameSpace}.Helpers.TagHelpers.CustomAttributeTagHelper);
string s = t.Assembly.GetName().Name.ToString();
Console.WriteLine($"The fully qualified assembly name you need is: {s}.");
  1. You then use it as simply as this in a razor page:
@{
    Dictionary<string, string> myCustomAttributes = new Dictionary<string, string> {
        ["data-first-custom-attribute"] = "my custom value"
    };

    if (!user.GotAccessToken("someToken")) {
        myCustomAttributes.Add("readonly","readonly");
    }
}

<input custom-attributes="myCustomAttributes" />

NOTE: This code can be used on any TagHelper and means you can add any custom attributes you like.

If you wanted to make it for just a single attribute (like your readonly above) then you can create a conditional TagHelper that requires a bool on construction or as it's value.

glenn223
  • 238
  • 1
  • 4
  • 16
  • 7
    What a pain in the ass. – tnk479 May 07 '20 at 16:46
  • This is a great solution for this problem. The class can be improved by adding support for specifying the attributes via anonymous objects, using `HtmlHelper.AnonymousObjectToHtmlAttributes(CustomAttributes);` – Mark Lagendijk Apr 28 '21 at 13:40
1

For the same issue but with multiple attributes I did following workaround:

@{
    //...
    string attributes = "...prepare attributes ...";
    string tag = "<input " + attributes + " />";
    <text>@Html.Raw(tag)</text>
    //...
}

@Html.Raw is not recommended because of some security vulnerabilities, but I still used it here. Be aware of that risk. If needed I can share some additional articles about that risk. Otherwise, for a single attribute I agree with @Rena's comment.

Vedran
  • 143
  • 10
1

I ended up removing the line

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 

from _ViewImports.cshtml

In my case I had to construct the html with great precision and I did not use the the TagHelpers anyway.

Hans Karlsen
  • 2,275
  • 1
  • 15
  • 15