Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
vova-lantsov-dev opened this issue Apr 30, 2019 · 28 comments · May be fixed by #9742
Open
Labels
area-compiler Umbrella for all compiler issues enhancement Small improvement request Priority:1 triaged
Milestone

Comments

@vova-lantsov-dev
Copy link

vova-lantsov-dev commented Apr 30, 2019

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 vova-lantsov-dev changed the title RZ9986 — Component attributes do not support complex content (mixed C# and markup) Blazor RZ9986 — Component attributes do not support complex content (mixed C# and markup) Apr 30, 2019
@vova-lantsov-dev
Copy link
Author

image

@danroth27
Copy link
Member

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.

@danroth27 danroth27 added the enhancement Small improvement request label Apr 30, 2019
@vova-lantsov-dev
Copy link
Author

vova-lantsov-dev commented Apr 30, 2019

@danroth27 so you mean replace

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

With

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

@danroth27
Copy link
Member

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
Copy link

kjeske commented May 1, 2019

You can also use:

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

@pippipmilk
Copy link

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
Copy link

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
Copy link

will this problem be addressed?

@danroth27
Copy link
Member

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
Copy link

gojanpaolo commented Nov 30, 2020

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
Copy link

araxemy commented Dec 1, 2020

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
Copy link
Member

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
Copy link

ud-waffle commented Jun 17, 2021

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
Copy link
Member

SteveSandersonMS commented Jun 17, 2021

@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
Copy link

I find myself doing this sort of thing:-

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

@*Nested content...*@

</MudGrid>

@cedwards-telis
Copy link

cedwards-telis commented Jun 17, 2021

Or closer to what you started with

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

@*Nested content...*@

</MudGrid>

@ud-waffle
Copy link

ud-waffle commented Jun 18, 2021

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
Copy link

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
Copy link

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

@mkArtakMSFT mkArtakMSFT transferred this issue from dotnet/aspnetcore Apr 25, 2022
@iaingymware
Copy link

iaingymware commented Apr 25, 2022 via email

@DerekFoulk
Copy link

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
Copy link

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
Copy link

joscha999 commented Sep 23, 2022

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.

@chsienki chsienki added the area-compiler Umbrella for all compiler issues label Oct 28, 2022
@chsienki chsienki transferred this issue from dotnet/razor-compiler Oct 28, 2022
@ghost ghost added the untriaged label Oct 29, 2022
@DataJuggler
Copy link

DataJuggler commented Nov 9, 2022

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
Copy link

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.

@chsienki chsienki removed the untriaged label Jan 5, 2023
@ghost ghost added the untriaged label Jan 5, 2023
@chsienki chsienki added this to the Backlog milestone Jan 5, 2023
@ghost ghost removed the untriaged label Jan 5, 2023
@jhabaa
Copy link

jhabaa commented Sep 6, 2023

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
Copy link

springy76 commented Dec 12, 2023

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: dotnet/aspnetcore#23607 (comment)

@jjonescz jjonescz linked a pull request Dec 20, 2023 that will close this issue
@max-programming
Copy link

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")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-compiler Umbrella for all compiler issues enhancement Small improvement request Priority:1 triaged
Projects
None yet
Development

Successfully merging a pull request may close this issue.