dotnet / razor

Compiler and tooling experience for Razor ASP.NET Core apps in Visual Studio, Visual Studio for Mac, and VS Code.
https://asp.net
MIT License
475 stars 187 forks source link

Blazor RZ9986 — Component attributes do not support complex content (mixed C# and markup) #7684

Open vova-lantsov-dev opened 5 years ago

vova-lantsov-dev commented 5 years ago

I've found that this issue was already fixed in 0.4.0 Reference 1 Reference 2

But it still present. Blazor 3.0.0-preview4 Visual Studio 2019 16.1.0 preview2 Latest vsix extension installed

Files where issue happened: Button.razor HalfButton.razor

Project repository

I also tried to solve this problem by deleting .vs / bin / obj directories and restarting VS.

vova-lantsov-dev commented 5 years ago

image

danroth27 commented 5 years ago

Currently we don't support concatenation of strings inside the values of attributes passed to components. You can work around the issue by doing the concatenation separately and then pass in the value.

vova-lantsov-dev commented 5 years ago

@danroth27 so you mean replace

Color="red" ... w3-@(Color) ...

With

Color="w3-red" ... @(Color) ... ?

danroth27 commented 5 years ago

Instead of this:

@if (Disabled) {
    <button class="w3-@(Color) ... " >...</button>
}

I think you can do this:

@if (Disabled) {
    var classText = "w3-" + Color + .... ;
    <button class="@classText" >...</button>
}
kjeske commented 5 years ago

You can also use:

@if (Disabled) {
    <button class=@($"w3-{Color}")>Click me</button>
}
pippipmilk commented 5 years ago

This is pretty important IMHO. At the moment you can't even pass an id into a NavLink helper. Strange that it used to work.

rupeshtech commented 4 years ago

I solved it by diff way...

 var link = "SomeOtherUrl/" + forecast.SomeValue;
                    <tr>
                        <td>@forecast.SomeValue</td>
                        <td>
                            <NavLink class="nav-link" href="@link">
                                <span class="oi oi-plus" aria-hidden="true"></span> Detail
                            </NavLink>
                        </td>
                    </tr>
burakakca commented 4 years ago

will this problem be addressed?

danroth27 commented 4 years ago

Hi @burakakca. Currently this issue is on our backlog with no committed time frame to address it. So I wouldn't expect it to be addressed any time soon. In the meantime you can use the various workaround described in the earlier comments.

gojanpaolo commented 3 years ago

Just hit a similar issue today when trying to add a class conditionally.

Current work around is:

var percentageClass = "form-control";
if (!isValid)
    percentageClass += " invalid";
<InputNumber
    @bind-Value="@m.Percentage"
    class="@percentageClass" />

... hoping to get something similar in Angular like:

<InputNumber
    @bind-Value="@m.Percentage"
    class="form-control"
    [invalid]="@(!isValid)" />

UPDATE: I updated how I write conditional css class like so:

<InputNumber
    @bind-Value="@m.Percentage"
    class=@($"form-control {(isValid ? "" : "invalid")}") />
araxemy commented 3 years ago

More surprising than the fact that it's been 2 years and this is STILL not fixed, is the fact that @danroth27 says that it's not coming any time soon! Don't you think this is among the most basic of features?! Any updates on this? @danroth27 @SteveSandersonMS

By what logic is something like this not allowed:

<Button Label="Delete @Product.Title" />

I know there are workarounds, but this should obviously be supported natively.

danroth27 commented 3 years ago

When you set a component parameter that is of type string using an attribute, the attribute value is treated as a C# string. So, in Blazor, <Button Label="Delete @Product.Title" /> is equivalent to button.Label = "Delete @Product.Title"; in C#, which isn't what you actually wanted.

Instead you need to use C# syntax to construct the string, something like button.Label = $"Delete {Product.TItle}";. In Razor, it get's a bit clunky to manipulate C# strings in an HTML attribute because of the quotes, but you can do something like this: <Button Label="@($"Delete {Product.TItle}")" /> , where the @() syntax is used to clarify where the C# begins and ends.

We could still do work in the Razor compiler to make this scenario less cumbersome, but hopefully that at least give some context for what is happening.

ud-waffle commented 3 years ago

This issue for projects that use external libraries (such as MudBlazor) makes 'dynamic styling' very pesky to implement without duplicating the component & nested content w/ @if's and @else's

(Desired code)

<MudGrid style=" width: @(condition ? "100%" : "50%" );">

@*Nested content...*@

</MudGrid>
SteveSandersonMS commented 3 years ago

@ud-waffle You can always do something like:

<MudGrid style="@MyGridStyle">
...
</MudGrid>

@code {
    string MyGridStyle => $"width: {(condition ? "100%" : "50%" )}";
}

or

<MudGrid style="@MyGridStyle(condition)">
...
</MudGrid>

@code {
    string MyGridStyle(bool condition) => $"width: {(condition ? "100%" : "50%" )}";
}
cedwards-telis commented 3 years ago

I find myself doing this sort of thing:-

<MudGrid style=@(condition ? "width:100%;" : "width:50%;")>

@*Nested content...*@

</MudGrid>
cedwards-telis commented 3 years ago

Or closer to what you started with

<MudGrid style=@($"width:{(condition ? "100%" : "50%" )};")>

@*Nested content...*@

</MudGrid>
ud-waffle commented 3 years ago

For that case I also needed to apply other styles so I went with a different approach

<div style=" width: @(condition ? "100%" : "50%" );">
    <MudGrid Style="width:100%; (...) ">
        @*Nested content...*@
    </MudGrid>
</div>
CodyJ-GB commented 2 years ago

I also came across this issue today when trying to do the following:

<div class="@(visible ? "" : "hidden") @cssClass"

@code {
    [Parameter]
    public string cssClass { get; set; }
    [Parameter]
    public bool visible { get; set; } = true;
}

Perhaps this is because of the use of two @ symbols, but it still references the root of the issue (this post).

iaingymware commented 2 years ago

Is there any update on this? Seems a very common use case not currently being handled...

iaingymware commented 2 years ago

It's 100% still an issue.

I tried to have a variable as part of a NavLink href tag and it threw an error.

Get Outlook for Androidhttps://aka.ms/AAb9ysg


From: Vova Lantsov @.> Sent: Monday, April 25, 2022 7:16:16 PM To: dotnet/razor-compiler @.> Cc: Iain Wandless @.>; Comment @.> Subject: [dotnet/razor-compiler] Blazor RZ9986 — Component attributes do not support complex content (mixed C# and markup) (#224)

I've found that this issue was already fixed in 0.4.0 Reference 1https://github.com/aspnet/Blazor/issues/784 Reference 2https://github.com/aspnet/Blazor/issues/753

But it still present. Blazor 3.0.0-preview4 Visual Studio 2019 16.1.0 preview2 Latest vsix extension installed

Files where issue happened: Button.razorhttps://github.com/vova-lantsov-dev/bookkeeping-blazor/blob/feature-blazor-3.0.0-preview4/BookkeepingNasheDetstvo.Client/Components/ForButtons/Button.razor HalfButton.razorhttps://github.com/vova-lantsov-dev/bookkeeping-blazor/blob/feature-blazor-3.0.0-preview4/BookkeepingNasheDetstvo.Client/Components/ForButtons/HalfButton.razor

Project repositoryhttps://github.com/vova-lantsov-dev/bookkeeping-blazor/tree/feature-blazor-3.0.0-preview4

I also tried to solve this problem by deleting .vs / bin / obj directories and restarting VS.

— Reply to this email directly, view it on GitHubhttps://github.com/dotnet/razor-tooling/issues/7684, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AR3UAAP3M2GVH4XCZUHC4ZDVG3OPBANCNFSM5UJL6GKA. You are receiving this because you commented.Message ID: @.***>

DerekFoulk commented 2 years ago

I encountered this when simply trying to pass an ID into the href attribute of a NavLink component. I am surprised that this is not supported. This seems very basic. I am sure that somebody attempted to do something like this during the development of Blazor. It makes me wonder if there are other basic things that don't work as expected. I suppose I need to train more.

haakonfp commented 1 year ago

I encountered this error today when trying to add a class to an InputText component in an EditForm. I had a TailwindCSS class apply a red border on the InputText when it's in the Invalid state (namely the class invalid:border-red-500), but didn't want it to be red when the InputText was empty. I did not find a better solution than simply overwriting the Tailwind class with another border color.

I had originally added a local bool, @IsInvalid, to the class, but this caused the RZ9986 compilation error. After reading here, I added the workaround of a conditional CSS class, as proposed by @gojanpaolo, and it worked, as such:

<InputText @bind-Value="model.example" class=@($"form-control {( model.example != "" ? "invalid:border-red-500" : "invalid:border-gray-500" )} ") />

@code {
    public bool 
}

That only a conditional css class works is not very intuitive, and it does not seem to be a very well documented error either. Any updates on timeline for a fix for this?

joscha999 commented 1 year ago

Adding to this issue as I've also run into it. I think the weird thing is that e.g. a simple ternary operation works fine if it happens e.g. in a non-component tag but the same operation throws this error if it is on a component.

Example, in the following Component:

<li class="display-inline float-left nav-link @(Active ? "active" : "")">
    <NavLink href="@HRef" Match="NavLinkMatch.All" class="display-block px1 py05 @(Active ? "bg-highlight" : "")">
        @ChildContent
    </NavLink>
</li>

//Code part omitted, Active is a bool and HRef a string

The ternary part on the li tag is valid razor but (nearly) the same ternary is invalid on a component (the NavLink in this case).
To me this feels like a weird discrepancy and I'm guessing it trips up others too.

DataJuggler commented 1 year ago

I get this error trying to do something I thought I had done before in Blazor. I might be confusing something I did in MVC at work.

In my grid I am building, I need to know which row was just double clicked to enter edit mode. Is there a way to get the row that was double clicked? I have an int Number and Guid Id property on each row.

    // if the row has columns
    if (row.HasColumns)
    {
        <div class="@row.ClassName">

        @foreach (Column column in row.Columns)
        {

            <div class="@column.ClassName" @ondblclick="OnDoubleClick(@row.Number)">@column.ColumnText</div>
        }
        </div>
        <br />
    }

Thanks

DataJuggler commented 1 year ago

I figured out my issue by using a Lambda expression:

    // if the row has columns
    if (row.HasColumns)
    {
        <div class="@row.ClassName">

        @foreach (Column column in row.Columns)
        {
            <div class="@column.ClassName" @ondblclick="() => OnDoubleClick(row)">@column.ColumnText</div>
        }
        </div>
        <br />
    }

And I had to change my edit method to accept a row.

public void OnDoubleClick(Row row)
{
    // turn on EditMode
    EditMode = true;

    // Update the page in EditMode
    Refresh();
}

Maybe this will help someone else.

jhabaa commented 10 months ago

I solved it by diff way...

 var link = "SomeOtherUrl/" + forecast.SomeValue;
                    <tr>
                        <td>@forecast.SomeValue</td>
                        <td>
                            <NavLink class="nav-link" href="@link">
                                <span class="oi oi-plus" aria-hidden="true"></span> Detail
                            </NavLink>
                        </td>
                    </tr>

This is working for me with "div" inside, but I need to refresh the page to make it work.

springy76 commented 7 months ago

Why this f*cking artificial limitation (still) exists at all?

Something which works:

    private RenderFragment Test(int a, int b, int c) => __builder =>
    {
        <span title="@a and @b is @c"></span>
    };

Decompilation What the source-generator creates of that:

    private RenderFragment Test(int a, int b, int c) => __builder =>
    {
        __builder.OpenElement(19, "span");
        __builder.AddAttribute(20, "title", (
                      a
        ) + " and" + " " + (
                             b
        ) + " is" + " " + (
                                   c
        ));
        __builder.CloseElement();
    };

Modified to create a component and bind to string parameter (still compiles):

    private RenderFragment Test(int a, int b, int c) => __builder =>
    {
        __builder.OpenComponent<InputText>(19);
        // BTW next line even works using AddAttribute instead of AddComponentParameter
        __builder.AddComponentParameter(20, "Value", (
                      a
        ) + " and" + " " + (
                             b
        ) + " is" + " " + (
                                   c
        ));
        __builder.CloseComponent();
    };

But writing the same using razor syntax fails artificially:

    private RenderFragment Test(int a, int b, int c) => __builder =>
    {
        <InputText Value="@a and @b is @c"></InputText>
    };

Why the razor transpiler (or what is it named exactly) has those artificial limitations? The same with abstract components: There is IComponentActivator since net5 but it only works for hand-written code against RenderTreeBuilder - the razor transpiler just throws artificial errors for things which just work: https://github.com/dotnet/aspnetcore/pull/23607#issuecomment-1437310969

max-programming commented 2 months ago

I did a workaround like @gojanpaolo. Let me know if there's a better way. One caveat to this is that I lose the Tailwind Intellisense (I come from the React world so pardon me for using a ternary inline 😛 )

class=@($"py-3 px-4 block w-full border-gray-200 rounded-lg text-sm focus:border-blue-500 focus:ring-blue-500 {(context.IsValid(() => Location.Name) == false ? "border-red-500" : "")} disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-900 dark:border-neutral-700 dark:text-neutral-400 dark:placeholder-neutral-500 dark:focus:ring-neutral-600")