我们如何在单独的代码文件中创建可重用的 RenderFragment?

How do we create a reusable RenderFragment in a separate code file?

this Microsoft article 中,他们展示了如何将一段 Blazor 代码提取到方法中以便可以重用。他们展示了以下示例...

<h1>Hello, world!</h1>

@RenderWelcomeInfo

<p>Render the welcome info a second time:</p>

@RenderWelcomeInfo

@code {
    private RenderFragment RenderWelcomeInfo = __builder =>
    {
        <p>Welcome to your new app!</p>
    };
}

然后他们继续解释说如果你想把这个方法提取到一个单独的 .razor 文件中,你可以把它做成 static.

对于一个简单的示例(例如他们展示的示例)来说,这一切都很好,但如果您想要任何现实的东西,您可能需要至少向该方法传递一个参数。

但是,如果您更改他们的 SayHello 示例以采用参数...

  public static RenderFragment SayHelloName(string name) = __builder => {
    <h1>Hello @name</h1>
  };

...然后你会得到一个有趣的编译器错误组合...

更令人困惑的是,这些错误中只有第 4 个和第 5 个出现在错误面板中,尽管措辞略有不同。

按照 Telerik's KB 中的示例,我尝试了以下...

  public static RenderFragment<object> SayHelloName(string name) => context => __builder   => {
    <h1>Hello @name</h1>
  };

但是,当我尝试使用它时...

<div>@SayHelloName("Jim")</div>

...页面呈现如下...

Microsoft.AspNetCore.Components.RenderFragment`1[System.Object]

...而不是我预期的“Hello Jim”。

谁能解释我做错了什么?如何提取将参数带入单独 class?

的 RenderFragment 方法

阅读您链接到的页面的更下方:

RenderFragment delegates can also accept parameters. The following component passes the message (message) to the RenderFragment delegate:

<div class="chat">
    @foreach (var message in messages)
    {
        @ChatMessageDisplay(message)
    }
</div>

@code {
    private RenderFragment<ChatMessage> ChatMessageDisplay = message => __builder =>
    {
        <div class="chat-message">
            <span class="author">@message.Author</span>
            <span class="text">@message.Text</span>
        </div>
    };
}

看来你需要

public static RenderFragment<string> SayHelloName = name => __builder => {
    <h1>Hello @name</h1>
};

如果你想传入更多参数,将它们包装在一个类型中,例如 Greeting:

public class Greeting
{
    public string Title { get; set; }
    public string Name { get; set; }
}

然后将 Greeting 作为类似于文档中 ChatMessage 示例的类型参数传递:

public static RenderFragment<Greeting> SayHelloName = greeting=> __builder => {
        <h1>Hello @greeting.Title @greeting.Name</h1>
    };

或者您可以使用 non-generic 版本:

public static RenderFragment SayHelloName(string title, string name) => __builder => {
    <h1>Hello @greeting.Title @greeting.Name</h1>
};

替代语法:

@page "/razor-template"

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string? Name { get; set; }
    }
}

这是来自文档:https://docs.microsoft.com/en-gb/aspnet/core/blazor/components/?view=aspnetcore-6.0#razor-templates

了解 RenderFragment 的关键是它是一个委托。片段中的代码将 运行 在其定义的 class 的上下文中。

有很多方法可以定义一个。这里有一些:

<h3>RenderFragments.razor</h3>

@code {
    public static RenderFragment SayHello = (builder) =>
    {
        builder.OpenElement(0, "div");
        builder.AddContent(1, "Hello");
        builder.CloseElement();
    };

    public static RenderFragment SayHelloAnotherWay => (__builder) =>
    {
        <div>
            Hello Another Way
        </div>
    };

    public static RenderFragment SayHelloAnotherWay1(string name) => (__builder) =>
    {
        <div>
            Hello Another Way @name
        </div>
    };

    public static RenderFragment SayHelloToName(string name) => (builder) =>
    {
        builder.AddMarkupContent(0, $"<div>Hello {name}</div>");
    };

    public static RenderFragment<string> SayHello1 => (context) => (builder) =>
    {
        builder.AddMarkupContent(0, $"<div>Hello {context}</div>");
    };
}

和用法:

@page "/"

@RenderFragments.SayHello

@RenderFragments.SayHelloToName("Fred")

@RenderFragments.SayHello1(Name)

@RenderFragments.SayHelloAnotherWay

@RenderFragments.SayHelloAnotherWay1(Name)

@code {
    string Name = "John";
}