Mixing C# and HTML Helper tags ASP.NET MVC 6 (ASP.NET Core)

Kolya_Net picture Kolya_Net · Mar 30, 2016 · Viewed 8k times · Source

I'm using the new Helper tags in ASP.NET MVC 6.

    <form asp-area="DAS"
          asp-controller="Report"
          asp-action="Add"
          asp-route-id="@Model.id"
          asp-route-incBalance="@Model.incBalance"
          asp-route-dateSet="@Model.dataStart.ToString("yyyy-MM-dd")"
          asp-route-dateNext="@Model.dataEnd.ToString("yyyy-MM-dd")"
          method="post" role="form">
    </form>

I want to routing attribute:

asp-route-dateNext="@Model.dataEnd.ToString("yyyy-MM-dd")"

was applied only if:

          {
              if (Model.incBalance == 0)
              {
                  asp-route-dateNext="@Model.dataEnd.ToString("yyyy-MM-dd")"
              }
          }

As a result, I want to get something like this:

    <form asp-area="DAS"
          asp-controller="Report"
          asp-action="Add"
          asp-route-id="@Model.id"
          asp-route-incBalance="@Model.incBalance"
          asp-route-dateSet="@Model.dataStart.ToString("yyyy-MM-dd")"
          {
             if (Model.incBalance == 0)
             {
                 asp-route-dateNext="@Model.dataEnd.ToString("yyyy-MM-dd")"
             }
          }
          method="post" role="form">
    </form>

I get these errors:

TagHelper attributes must be well-formed.

          if (Model.incBalance == 0)

and

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

              asp-route-dateNext="@Model.dataEnd.ToString("yyyy-MM-dd")"

I'm using Visual Studio 2015 Update 1

Update 1: I also tried this option:

@(Model.incBalance == 0 ? "asp-route-dateNext=" + Model.dataEnd.ToString("yyyy-MM-dd") : string.Empty)

But the error remained:

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

      @(Model.incBalance == 0 ? "asp-route-dateNext=" + Model.dataEnd.ToString("yyyy-MM-dd") : string.Empty)

Answer

poke picture poke · Mar 30, 2016

With Razor, every part of the markup has to be well formed. You cannot have dangling opening tags, or interrupt markup for Razor expresions. So things like this are not valid and as such cause syntax errors:

<!-- Interrupting a tag -->
<div
@if (condition) {
     attribute="bar"
}
>

<!-- Also not possible: Conditionally opening tags -->
if (condition) {
    <span>
}
Some text
if (condition) {
    </span>
}

So you have to make sure that the markup within Razor expressions, basically everything between the curly braces, is a valid expression, and that tags are always complete.

This unfortunately means that you cannot use @if to conditionally add an attribute (regardless of whether that’s a tag helper attribute or not).

If it was just plain HTML, you could make Razor render raw text, which is not interpreted by the Razor parser and as such does not need to be valid markup (so above things would work). However, in raw text, tag helpers also don’t run, so that doesn’t help you here.

This leaves you essentially with three choices now:

  1. Wrap everything in a giant @if and essentially duplicate the whole form tag:

    @if (condition) {
        <form with-that-extra="attribute">
            All the form content
        </form>
    }
    else {
        <form>
            All the form content
        </form>
    }
    

    Of course, this is just terrible since you need to duplicate really the whole form content (otherwise, the tags within the @if wouldn’t be well formed).

  2. Writing your own tag helper which encapsulates the logic behind the @if condition check.

    This works well for more general things, but is a bit tedious just for adding an optional attribute.

  3. Using an expression syntax to pass some “null value” to the route if the condition is not met:

    <form …
        asp-route-dateNext="@(Model.incBalance == 0 ? Model.dataEnd.ToString("yyyy-MM-dd") : null)">
    </form>
    

    This is likely the easiest solution. The default value null there will also prevent the tag helper from running for that attribute, so it’s just as if you omitted it completely.