Design proposal: solution for cascading values into interactive pages #49104
Labels
area-blazor
Includes: Blazor, Razor Components
design-proposal
This issue represents a design proposal for a different issue, linked in the description
Milestone
There's a gap in the Blazor SSR+interactive design currently, which is that whenever you have a
@page
component with Server or WebAssembly rendermode, that page component runs outside the context where it would receive any cascaded values, which means:[SupplyParameterFromQuery]
(and presumably also[SupplyParameterFromForm]
)Obviously this is something we have to address, especially because of item 2 there which is the most basic scenario among them.
Proposal
We can provide a way of registering root-level cascading values independently of the component hierarchy. This makes sense whenever there are values that are effectively global within a given circuit/wasm scope. Then:
@page
component is being rendered as an island or if it's embedded inside anApp
component or aRouteView
or whatever else.App
components,RouteView
, etc., by not having to wrap multiple layers ofCascadingValue
around things.As for implementation, I've done a quick experiment and found this works very naturally if we make the DI system do most of the work:
CascadingParameterState
recurses up to the root of the component hierarchy and didn't find a value yet, iterate through the cascading value suppliers and let each of those be the supplier if they wantFor the API, I propose we restrict this to exposing only the exact same capabilities that are currently exposed through
CascadingValue
. That is,ICascadingValueSupplier
would continue to beinternal
, but there would be APIs like:As you see, the design is oriented around providing the exact same capabilities, not more or less, than a
<CascadingValue>
component at the root. So it will always be possible to replace root-level<CascadingValue>
with one of these registrations if you want to make it global.Then it will be pretty trivial to create a variant of
<CascadingAuthenticationState>
as a DI extension method likeservices.AddCascadingAuthenticationState()
which sets up an equivalent registration, and usesNotifyChanged
to trigger updates like<CascadingAuthenticationState>
would do.Likewise, the default set of Blazor DI services can include cascading value suppliers for Form and Query values, so they will work everywhere, and we can decouple this from
RouteView
.This will be a relatively cheap solution to the overall problem. I have a working version of it already (for case 3 - custom values - I haven't validated the end-to-end with SupplyParameterFromQuery etc but it seems like it should work). The only unexpected complication was that
NotifyChanged
has to useDispatcher.InvokeAsync
internally, but with that in place things work correctly.Limitations/drawbacks
Ordering
I'm not proposing we define any way of ordering the DI-registered cascading value suppliers. Developers will be responsible for registering types that are specific enough that they don't interfere with unrelated cascading value suppliers, or to use the optional
name
parameter to be even more explicit. That won't be a problem for the built-in ones as they can use the full capabilities ofCanSupplyValue
to guarantee they only supply values for the correct type of recipient.This is mostly equivalent to what happens with cascading values in the component hierarchy, albeit with less explicit control. If this later turns out to be problematic, we can optionally add an
Order
property to the registrations - it's not a perfect solution as then you have to coordinate order values, but it would offer more control. I don't think we have to do that initially.Disposal
Just like
CascadingValue
doesn't dispose any values you give to it, I don't propose that we would auto-dispose any values you supply through these APIs. Developers already have multiple ways of doing that.Scope/lifetime control
One of the nice things here is that because
ICascadingValueSupplier
isinternal
, the only possible way to register one in DI is to use the method being proposed here, and so the framework can control the DI lifetime. I propose that we only ever register as "scoped", because other options are problematic (if we allow singleton, that creates a new kind of scenario where unrelated user circuits can be subscribed to the same cascading value, and if we allow transient, it would be completely unlike other cascading values).pinging @dotnet/aspnet-blazor-eng for feedback or alternatives
The text was updated successfully, but these errors were encountered: