【问题标题】:Blazor component shows `Microsoft.AspNetCore.Components.RenderFragment` instead of the HTML contentBlazor 组件显示“Microsoft.AspNetCore.Components.RenderFragment”而不是 HTML 内容
【发布时间】:2021-09-10 02:41:18
【问题描述】:

我正在使用Language-Ext library 创建一个表示三种状态之一的通用联合类型...

  [Union]
  public interface LoadingOption<T> {
    LoadingOption<T> Loading();
    LoadingOption<T> Loaded(T value);
    LoadingOption<T> NotLoaded();
  }

然后我有一个 Blazor 组件,它将显示三个渲染片段之一,具体取决于联合实例表示的三个状态中的哪一个...

@typeparam T

@(Data switch {
  Loading<T> => Loading,
  Loaded<T> t => Loaded(t.Value),
  NotLoaded<T> => NotLoaded,
  _ => Loading
  })

@code {

  [Parameter]
  public LoadingOption<T> Data { get; set; }

  [Parameter]
  public RenderFragment<Loading<T>> Loading { get; set; }

  [Parameter]
  public RenderFragment<Loaded<T>> Loaded { get; set; }

  [Parameter]
  public RenderFragment<NotLoaded<T>> NotLoaded { get; set; }

}

请注意,switch 语句中的默认情况需要在页面首次加载时防止异常非详尽的 switch 表达式未能匹配其输入。

但是,当我尝试使用此组件时,HTML 永远不会呈现。相反,我在页面上看到Microsoft.AspNetCore.Components.RenderFragment,这听起来像是将RenderFragment 视为普通对象,而不是RenderFragment

Loader.razor 组件的示例用法如下...

@page "/LoadingOptionSample"
<h3>Loader sample</h3>

<p><button class="btn btn-primary" @onclick="LoadCustomerFound">Existing customer</button>
   <button class="btn btn-secondary" @onclick="LoadCustomerNotFound">No such customer</button></p>

<Loader Data="_customer" Context="customer">
  <Loading>
    <p>Please wait while we look for the customer...</p>
  </Loading>
  <Loaded>
    <p>Found @customer.Value.Name (id: @customer.Value.Id), who is @customer.Value.Age years old</p>
  </Loaded>
  <NotLoaded>
    <p>Big flop, we can't find him</p>
  </NotLoaded>
</Loader>

@code {

  private LoadingOption<Customer> _customer;

  private async Task LoadCustomerFound() {
    _customer = new Loading<Customer>();
    // Simulate database access so we can see the Loading HTML...
    await Task.Delay(2000);
    _customer = new Loaded<Customer>(new Customer(1, "Jim Spriggs", 42));
  }

  private async Task LoadCustomerNotFound() {
    _customer = new Loading<Customer>();
    await Task.Delay(2000);
    _customer = new NotLoaded<Customer>();
  }

}

单击其中一个按钮后,页面如下所示...

不管我点击哪个按钮,输出都是一样的,不会改变。这支持了我的怀疑,即 Blazor 没有将 RenderFragment 视为这样,而是将其视为普通的 .NET 对象并通过调用 ToString() 来呈现它,这就是我看到类型而不是内容的原因。

如果我做这个的非通用版本,它工作正常。

注意,我已经多次重启VS,以及在单独的项目中尝试过,但结果总是一样,所以看起来问题封装在上面显示的三个文件中。

有人知道我做错了什么吗?谢谢

更新我尝试使用常规的 switch 语句,而不是 switch 表达式,但它没有任何区别。然而,在测试这个时,我发现如果我根本没有switch,那么就这样做了......

  @NotLoaded

...然后我仍然得到页面上显示的类型。

【问题讨论】:

  • [union] 与 switch 表达式的工作方式不匹配。您的返回值并非都属于同一类型。试试传统的 switch 语句。
  • @henk 感谢您的建议,但没有任何区别。请查看我更新的问题,因为您的建议导致了一个可能很重要的发现。再次感谢。
  • 您缺少一个参数。它应该类似于 @NotLoaded(Data) ,尽管它可能无法编译。
  • @HenkHolterman NotLoaded 和 Loading 不带参数,或者至少不应该带参数。只有Loaded带了一个参数,即已经加载的实体。如果您查看[Union] 代码,您可以看到这一点。不确定我是否在 Blazor 组件中获得了 RenderFragment 声明。也许这就是它出错的地方。但是,如果这是问题所在,我会期望 NotLoaded 和 Loading 工作,或 Loaded 工作。实际上,这三个都只是显示RenderFragment 的类型,而不是按照我的意愿渲染它。再次感谢,还有什么想法吗?
  • 有什么理由不能使用具有三个状态和三个 RenderFragment 的通用“加载”组件?然后从父级设置状态?

标签: c# blazor blazor-server-side language-ext


【解决方案1】:

这个答案展示了管理组件加载和状态的一种不同的方法。

状态的枚举

    public enum DataLoaderState { NotSet, Loading, Loaded, Error }

一个基本的加载器组件。

@namespace Blazor.Starter.Components

@inherits ComponentBase
@if (this.State == DataLoaderState.Loading)
{
    if (this.LoadingContent != null)
    {
        @this.LoadingContent
    }
    else
    {
        <div class="m-2 p-2">Loading......</div>
    }
}
else if (this.State == DataLoaderState.Error)
{
    if (this.ErrorContent != null)
    {
        @this.ErrorContent
    }
    else
    {
        <div class="m-2 p-2">Error Loading Data</div>
    }
}
else
{
    @this.ChildContent
}
@code{

    [Parameter] public RenderFragment ChildContent { get; set; }
    [Parameter] public RenderFragment LoadingContent { get; set; }
    [Parameter] public RenderFragment ErrorContent { get; set; }
    [Parameter] public DataLoaderState State { get; set; } = DataLoaderState.NotSet;
}

一个类似于问题中用于演示加载器的示例页面。

@page "/"

<h2>Home Page</h2>

<DataLoader State="loadState">
    <LoadingContent>
        <div class="m-2 p-2 bg-warning">
            Please wait while we look for the customer...
        </div>
    </LoadingContent>
    <ChildContent>
        <div class="m-2 p-2 bg-success">
            Name: @Model.Name - Age: @Model.Age
        </div>
    </ChildContent>
    <ErrorContent>
        <div class="m-2 p-2 bg-danger">
            Big flop, we can't find him
        </div>
    </ErrorContent>
</DataLoader>

<DataLoader State="loadState">
    <div class="m-2 p-2">
        Loaded
    </div>
</DataLoader>

<div class="m-2 p-2">
    <button class="btn btn-primary" @onclick="LoadAsync">Reload</button>
</div>

<div class="m-2 p-2">
    <button class="btn btn-danger" @onclick="LoadErrorAsync">Reload with Error</button>
</div>

@code {

    private DataLoaderState loadState = DataLoaderState.Loaded;

    private Customer Model;

    protected async override Task OnInitializedAsync()
    {
        await LoadAsync();
    }

    private async Task LoadAsync()
    {
        Model = null;
        loadState = DataLoaderState.Loading;
        // emulate a slow async data load
        await Task.Delay(3000);
        Model = new Customer { Name = "Billy Bloggs", Age = 24 };
        loadState = DataLoaderState.Loaded;
    }

    private async Task LoadErrorAsync()
    {
        Model = null;
        loadState = DataLoaderState.Loading;
        // emulate a slow async data load
        await Task.Delay(3000);
        loadState = DataLoaderState.Error;
    }

    public class Customer
    {
        public Guid ID { get; set; } = Guid.NewGuid();
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2012-02-26
  • 1970-01-01
  • 2020-06-22
  • 1970-01-01
  • 2012-09-14
  • 1970-01-01
  • 1970-01-01
  • 2019-08-01
相关资源
最近更新 更多