I'm using the Language-Ext library to create a generic union type that represents one of three states...
[Union]
public interface LoadingOption<T> {
LoadingOption<T> Loading();
LoadingOption<T> Loaded(T value);
LoadingOption<T> NotLoaded();
}
I then have a Blazor component that will show one of three render fragments, depending on which of the three states is represented by the instance of the union...
@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; }
}
Note that the default case in the switch
statement is needed to prevent an exception Non-exhaustive switch expression failed to match its input when the page firsts loads.
However, when I try to use this component, the HTML is never rendered. Instead, I see Microsoft.AspNetCore.Components.RenderFragment
on the page, which sounds like it's treating the RenderFragment
as a plain object, rather than as a RenderFragment
Sample usage of the Loader.razor
component is as follows...
@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>();
}
}
After clicking one of the buttons, the page looks like this...
Doesn't matter which of the buttons I click, the output is the same, and doesn't change. This supports my suspicion that Blazor isn't treating the RenderFragment
as such, but treats it as a plain .NET object and renders it by calling ToString()
, which is why I see the type, not the content.
If I do a non-generic version of this, it works fine.
Note that I have repeatedly restarted VS, as well as trying this in a separate project, but the result is always the same, so it seems the problem is encapsulated in the three files shown above.
Anyone any idea what I'm doing wrong? Thanks
Update I tried using a regular switch
statement, instead of the switch
expression, but it didn't make any difference. However, while testing this, I discovered that if I didn't have the switch
at all, and simply did this...
@NotLoaded
...then I still got the type shown on the page.