Aspnetcore: Support mixed C# and markup in Blazor Component attributes (RZ9986)

Created on 19 Feb 2020  Β·  22Comments  Β·  Source: dotnet/aspnetcore

Is your feature request related to a problem? Please describe.

Right now (AspNetCore 3.1) RAZORGENERATE issues an error RZ9986: Component attributes do not support complex content (mixed C# and markup). Attribute: 'class'... in:

<MyComponent class="class1 class2 @CSharpCode" />

And the following workaround is needed:

<MyComponent class="@($"class1 class2 {CSharpCode}")" />

That's ok for this simple example, but with more complex code can get ugly, especially since you have to repeat it 100s of times.

Describe the solution you'd like

Support simpler syntax:

<MyComponent class="class1 class2 @CSharpCode" />
affected-few area-blazor area-razor.tooling enhancement severity-minor

Most helpful comment

Yep. You can do anything if you believe in yourself. https://github.com/dotnet/aspnetcore-tooling/blob/72b939d85573202c4406cd1ec1c39e97f870cb08/src/Razor/test/RazorLanguage.Test/Legacy/HtmlToCodeSwitchTest.cs#L69

If you're really curious, I'm sure that @ajaybhargavb or @NTaylorMullen could track down where it is in the implementation.


The other that's really mind blowing is that we have time-dependent parsing and completion behavior in the IDE for cases like <p>Today is @DateTime.Now.DayOfWeek.</p> (note the period at the end of the sentence.)

If you type that in, you'll get C# completion after the period as you're typing. If you return to it later, place your cursor at the period and trigger completion you'll get HTML.

mind-blown


Razor is the only HTML templating language I'm aware of that doesn't use closing delimiters. There are a lot of challenges with that 😁

All 22 comments

We've moved this issue to the Backlog milestone. This means that it is not going to happen for the coming release. We will reassess the backlog following the current release and consider this item at that time. However, keep in mind that there are many other high priority features with which it will be competing for resources.

@NTaylorMullen I've moved this to Backlog, and wondering how big of an effort this will require?

We parse it already (hence why we can provide an error) so making it codegen something isn't a big deal. The big deal is more about "what" to codegen. If the ask is for us to translate those requests into C# strings aka $".." then that adds a level of complexity; however, if the question is to codegen something for the runtime to buffer then that becomes a question for @SteveSandersonMS on what's possible.

At least an M.

@NTaylorMullen - how does this work today in MVC - does your static content get encoded or not? That's the big decision point. There's no buffering in Blazor πŸ‘. We'd probably just do a string.Concat.

how does this work today in MVC - does your static content get encoded or not?

It wraps the entire piece in a buffering lambda where static content is not encoded and dynamic content is.

You didn't answer the question, which is about HTML encoding. I don't think we'd try to replicate MVC's semantics, which are related to preserving whitespace.

You didn't answer the question,

Fat fingered my response (updated above): static content is not HTML encoded, dynamic content is HTML encoded.

Generating an AddAttribute call in which the "value" is given by global::System.String.Concat("literal", variable, "anotherliteral") etc would be perfect.

TBH I'm not sure why that's easier than generating $"literal{variable}anotherliteral" but either's sufficient.

The HTML encoding bit of this means we'd either have to:

  • Have a different encoding behavior (stricter, more stuff gets encoded)
  • Have an HTML-> string encoder everywhere Blazor runs
  • Have a different representation in frames

Yes, I realize that in the overall Blazor context the mechanism isn't literally HTML encoding, but the effect is similar

I suppose the escape hatch if someone wants to control the encoding behavior themselves would be to use MarkupString.

πŸ‘€

_HTML-> string encoder_ what are you thinking this means?

Something that turned text into HTML-encoded text. Like the encoders we already have

Ah ok, not the other way around, which was why the arrow worried me :)

What are the Blazor scenarios for why encoding is relevant here?

  • For interactive Blazor rendering, the attribute will always be added using a element.setAttribute(name, value) call in JS.
  • For prerendering, we always output htmlEncoder(value) for the attribute value

Neither of these needs to vary according to whether it's dynamic or static content. Post-compilation, we don't even know if it was static or dynamic content, since in both cases it's just:

builder.AddAttribute(x, "name", value);

... where value might be a string literal or a C# expression.

What are the Blazor scenarios for why encoding is relevant here?

I'm mentioning it so we can discuss whether consistency is relevant for this scenario. We can decide that it's not relevant.

In MVC Razor the static text gets left alone and the dynamic text gets encoded. We commonly see people do things like put HTML code in the static-text parts of attributes in MVC, in some scenarios today that will work without using HtmlString.

Sorry to hijack the discussion with a somewhat tangential note, but since you are discussing HTML encoding, I thought I would mention that right now the Blazor story for outputting unencoded text compared to MVC seems somewhat limited (at least for some of our scenarios):

  • There is indeed a need to write raw, unencoded HTML as a value of an attribute (regardless of whether it's a component or an element attribute). Example is Bottstrap tooltip that can display HTML tooltips using element's title attribute. When writing custom compoents, in the code the problem is that RenderTreeBuilder.AddAttribute always encodes it, unless you are using an overload that takes in RenderTreeFrame which you do not recommend using.
  • Sometimes there is a need to write the text verbatim that appears somewhere in the .razor file as the content of either a component or an element. It would be nice to introduce either a new Component or Razor syntax to define a region where no HTML encoding should take place (but C# code would still be allowed using @ syntax).

We commonly see people do things like put HTML code in the static-text parts of attributes in MVC, in some scenarios today that will work without using HtmlString.

Could you clarify with an example? What does it mean for an attribute's contents to be HTML code, other than them being the text content of that attribute?

Is it for something like HTML entities? Example:

<h1 title="My Heading&trade;">...</h1>

it's generally something like <div data-tooltip="<span>some more markup</span>">

It might be something that just works without much thought in Blazor. Except we do have to think about encoding behaviors for pre-rendering.

Thanks for clarifying.

it's generally something like <div data-tooltip="<span>some more markup</span>">

I checked, and this already works as expected with .razor files. You get the same results whether the attribute is injected as markup or constructed dynamically, i.e.:

<h1 data-tooltip="<span>some more markup</span>">A</h1>
<h1 data-tooltip="@myTooltip">B</h1>

@code {
    string myTooltip = "<span>some more markup</span>";
}

... both produce an attribute whose text value is <span>some more markup</span>.

However, there is actually one case where we have an inconsistency related to encoding in attributes: HTML entities. Compare the following:

<h1 title="This is nice&trade;">A</h1>
<h1 title="This is nice&trade;">B @DateTime.Now</h1>

In the first case, the tooltip displays This is niceβ„’, whereas in the second case it displays This is nice&trade;.

The reason for the difference is that, when we introduce the element as markup, it goes through the HTML parser which implicitly converts &trade; to β„’, whereas when we build the element dynamically we assign the attribute value using setAttribute which assumes the value already contains the desired, unencoded string.

I'm filing https://github.com/dotnet/aspnetcore/issues/19336 to track the HTML entities issue. I think it should be regarded as independent of this "support mixed content" issue since it already happens today and (hopefully) wouldn't be made any worse by supporting mixed content.

Wouldn't this make it more difficult to pass a string value that had an @ symbol in it to a parameter?

<EmailTo Address="[email protected]">

Razor has special parsing rules for email addresses πŸ˜†

Razor has special parsing rules for email addresses

Are you being serious, does it really?

Yep. You can do anything if you believe in yourself. https://github.com/dotnet/aspnetcore-tooling/blob/72b939d85573202c4406cd1ec1c39e97f870cb08/src/Razor/test/RazorLanguage.Test/Legacy/HtmlToCodeSwitchTest.cs#L69

If you're really curious, I'm sure that @ajaybhargavb or @NTaylorMullen could track down where it is in the implementation.


The other that's really mind blowing is that we have time-dependent parsing and completion behavior in the IDE for cases like <p>Today is @DateTime.Now.DayOfWeek.</p> (note the period at the end of the sentence.)

If you type that in, you'll get C# completion after the period as you're typing. If you return to it later, place your cursor at the period and trigger completion you'll get HTML.

mind-blown


Razor is the only HTML templating language I'm aware of that doesn't use closing delimiters. There are a lot of challenges with that 😁

Was this page helpful?
0 / 5 - 0 ratings