17

How can I write Blazor HTML code within a function inside of the @code block?

Consider the following code:

@page "/Test"

@if (option == 1)
{
    drawSomething("Something");
}
else
{
    drawSomething("Something else");
}

@code {
    int option;

    void drawSomething(string message)
    {
        <p>message</p>
    }
}

It does not produce any errors until I try to build, then it gives me the following error:

Error CS0103 The name '__builder' does not exist in the current context

On the lines in (Test.razor.g.cs):

__builder.AddContent(0, "        ");
__builder.AddMarkupContent(1, "<p>message</p>\r\n");

It seems very limited if this means Blazor HTML code can only be written in the first part of the file and not inside functions or classes.

I'm using the latest version as of writing version (3.0.100-preview9-014004) of blazor.

Note: The output in the given example is highly simplified, and I would like to know if and how I am able to write code from within a function and not solve the output above in a better way.

jsmars
  • 1,640
  • 5
  • 21
  • 33

2 Answers2

19

Update, you can now use:

@GreetPerson("John")

@code {
  RenderFragment GreetPerson(string name)
  {
    return @<p>Hello <em>@name</em></p>;
  }
}

Old answer:

This was announced as a feature for Preview6 but it didn't work as advertised then, and some details seem to have changed later. There is a comment from Cosmin Sontu at the bottom of that page that points the right way:

@using Microsoft.AspNetCore.Components.Rendering

@*tested with preview 9*@
@{ GreetPerson(__builder, "John"); }

@code {

    void GreetPerson(RenderTreeBuilder __builder, string name)
    {            
        <p>Hello, <em>@name!</em></p>
    }
}

The name __builder cannot be changed. That is a double underscore.

H H
  • 263,252
  • 30
  • 330
  • 514
  • Excellent, it looks a bit like a hack, but works exactly as I needed so I'm very grateful that its possible, thank you very much for the help! – jsmars Sep 08 '19 at 08:32
  • This is not suppose to be a viable solution in Razor Components, This is worse than a hack, even if currently works. Next preview, it won't. I'll write down a sort of an answer... – enet Sep 08 '19 at 08:43
  • 1
    @Issac Don't you think there will be a simple change in syntax for supporting similar functionality if this is removed though since it is mentioned as a feature in the blog post for Preview6, and seems like quite basic functionality to me tbh. – jsmars Sep 08 '19 at 09:26
  • @Issac - the code generation inside GreetPerson() doesn't work by accident - this is a feature, just not finished yet. – H H Sep 08 '19 at 09:55
  • 1
    This is a great way to make reusable templates! The render methods can be made static and placed in any random .razor file and referenced anywhere. @enet, it still works in production and I disagree it's a hack. It's simply taking advantage of the known way Blazor generates .cs from .Razor files. – Emperor Eto Feb 13 '21 at 15:07
  • 1
    This is also documented as a valid method of doing this: https://github.com/dotnet/AspNetCore.Docs/blob/1e199f340780f407a685695e6c4d953f173fa891/aspnetcore/blazor/webassembly-performance-best-practices.md#defining-reusable-renderfragments-in-code – Emperor Eto Feb 13 '21 at 15:22
12

Version 1

In Blazor idiomatic way would be create component instead of attempting to write HTML directly in the @code.

Create drawSomething.razor

<p>@Message</p>

@code {
    [Parameter]
    public string Message {get;set;}
}

and in your Test.razor

@page "/Test"

@if (option == 1)
{
    <drawSomething Message="Something" />
}
else
{
    <drawSomething Message="Something else" />
}

@code {
    int option;
}

Here I assuming that you have something more complex, then just plain

.

Version 2

If you really want easy way, then just

@page "/Test"

@if (option == 1)
{
    <p>Something</p>
}
else
{
    <p>Something else</p>
}

@code {
    int option;
}

Version 3 Based on suggestion from Isaac

@page "/Test"

@if (option == 1)
{
    @drawSomething("Something")
}
else
{
    @drawSomething("Something else")
}

@code {
    int option;

    RenderFragment drawSomething(string message)
    {
        return @<p>@message</p>;
    }
}

codevision
  • 5,165
  • 38
  • 50
  • Thanks for the extensive answer, however I do know about components, the example may be a bit too simplified as your solution is of course much better for it, but for this question I'm interested in actually writing HTML within a function and not just outputting the message if it is possible. – jsmars Sep 08 '19 at 07:23
  • 1
    Your version 3 is wrong or outdated. The usage must be: @drawSomething("Something") – Sven Oct 06 '20 at 08:16