I am working on a razor component library in which I would like to expose some components. However I would like to have the user of the library be able to set some global (generic) object that all other components can access. The type of the generic would be set on startup and should not change runtime.
Here is what I have so far.
//MyComponent.razor
@typeparam TGlobalState
@typeparam TScope
<p>Test</p>
@Default
@code {
[CascadingParameter(Name = "Global")]
public required TGlobalState Global { get; set; }
[CascadingParameter(Name = "Scope")]
public required TScope Scope { get; set; }
public Context<TGlobalState, TScope> CurrentScope => new()
{
Global = Global,
Scope = Scope
};
[EditorRequired, Parameter]
public required RenderFragment<Context<TGlobalState, TScope>> Default { get; set; }
[CascadingParameter(Name = "ComponentName")]
public required string ComponentName { get; set; }
[Parameter]
public string Key { get; set; }
public class Context<TGlobal, TLocal>
{
public required TGlobal Global { get; set; }
public required TLocal Scope { get; set; }
}
}
//ScopeProvider.razor
@attribute [CascadingTypeParameter(nameof(TScope))]
@typeparam TScope
<CascadingValue Value="@ComponentName" Name="ComponentName">
<CascadingValue Value="@Scope" Name="Scope">
@ChildContent
</CascadingValue>
</CascadingValue>
@code {
[Parameter]
public required string ComponentName { get; set; } = "";
[Parameter]
public TScope? Scope { get; set; }
[Parameter]
public required RenderFragment ChildContent { get; set; }
}
//GlobalStateProvider.razor
@attribute [CascadingTypeParameter(nameof(TGlobalState))]
@typeparam TGlobalState
<CascadingValue Value="@Global" Name="Global">
@ChildContent
</CascadingValue>
@code {
[Parameter]
public TGlobalState? Global { get; set; }
[Parameter]
public required RenderFragment ChildContent { get; set; }
}
And here is how I imagine it would be used:
//MainLayout.razor
@inherits LayoutComponentBase
<GlobalStateProvider Global="globalState">
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
</div>
<article class="content px-4">
@Body
</article>
</main>
</div>
</GlobalStateProvider>
@code{
GlobalState globalState = new();
}
//Index.razor
@page "/"
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
@*<GlobalStateProvider Global="@(new GlobalState())">*@ //If I uncomment this it works
<ScopeProviderScope="@(new HomeState())">
<MyComponent Key="SomeText">
<Default>@context.Global.Something</Default>
</MyComponent>
</ScopeProvider>
@*</GlobalStateProvider >*@
@code{
private HomeState state = new();
private class HomeState
{
public string Name { get; set; }
}
}
This issue is as following. The MyComponent can not infer TGlobalState from the GlobalStateProvider when it is in another razor file. I am also aware that this is stated to not work in the docs.
I was just wondering if there is any workaround to achieving a similar thing. the GlobalStateProvider could just as good be a service instead of a component and the service could be registered in the dependency injection (or something).
Thank you!