-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Description
Is there an existing issue for this?
- I have searched the existing issues
Describe the bug
The [PersistentState] attribute causes javascript created components to fail, such as components exposed using:
.AddInteractiveServerComponents(o => o.RootComponents.RegisterForJavaScript<Counter>("blazor-counter"));
And then added to the page using:
Blazor.rootComponents.add(document.getElementById("app"), "blazor-counter", {});
This was tested on .NET 10 rc2.
Expected Behavior
The PersistentState attribute (and the ability to persist and then recover state) should work for components created using the JavaScript APIs, such as when using Blazor inside existing websites, be they legacy WebForms, react, static HTML, etc. Passing state from server PreRendered components is only once scenario and there is no reason not to expect this feature to work for Blazor components loaded from a vanilla HTML page. That scenario is the bulk of our Blazor development work.
I would hope it is a minor 1-2 line fix (see the exception I am getting below).
Steps To Reproduce
To reproduce use the Blazor template (or the older blazorserver template if preferred:
dotnet new blazor
In Program.cs change this:
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
To this:
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents(o => o.RootComponents.RegisterForJavaScript<Counter>("blazor-counter"));
Note: Add required using at the top of Program.cs:
using Microsoft.AspNetCore.Components.Web;
using TestProject.Components.Pages;
Note: Make sure the project name is correct above.
Edit the Counter.razor component and make the actual counter state persistent like this:
// private int currentCount = 0;
[PersistentState] public int currentCount {get; set;}
Note: The project and counter page should work correctly including the working PersistentState feature if you launch the app and browse to the counter page. Now lets try creating an instance from javascript.
Create a test.html page in the wwwroot directory:
<html>
<body>
<div id="app">
</div>
<script src="_framework/blazor.server.js" autostart="false"></script>
<script>
Blazor.start().then(() => {
Blazor.rootComponents.add(document.getElementById("app"), "blazor-counter", {});
});
</script>
</body>
</html>
This causes an "Object reference not set to an instance of an object", and it looks internally like AspNetCore is looking for a parent component. You can see the full exception below in the "Exceptions" section.
I also tried wrapping the component in a "CounterWrapper.razor" component and loading that instead. In this second (more esoteric) case the component did work, but then failed when pausing and resuming the circuit. using the new API.
Exceptions (if any)
fail: Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost[111]
Unhandled exception in circuit 'F1Ra7RWBZRxzwLBMocG21wmgWNjn8pnr4zuWeskWIC8'.
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.AspNetCore.Components.Server.Circuits.RemoteRenderer.GetMarkerKey(RemoteComponentState remoteComponentState)
at Microsoft.AspNetCore.Components.Server.Circuits.RemoteComponentState.GetComponentKey()
.NET Version
10.0.100-rc.2.25502.107
Anything else?
Note that I used the blazor web template to demonstrate this issue, and then included the blazor.server.js script as recommended. The blazor.web.js script in general does not support javascript rendered components yet (Microsoft has a note on this and an open issue). I also tried an older blazorserver template, upgraded to .NET 10, but in both cases the same bug appeared.
This is unfortunate because it not only means the PersistentState feature is not working when used on a vanilla HTML page; but that even just inclusion of the PersistentState attribute in a component will break that component when used in a vanilla HTML page like this. Hopefully this is just a 1 line fix?