Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Doesn't work with prerendering #27

Open
Xeevis opened this issue Aug 5, 2021 · 3 comments
Open

Doesn't work with prerendering #27

Xeevis opened this issue Aug 5, 2021 · 3 comments

Comments

@Xeevis
Copy link

Xeevis commented Aug 5, 2021

When using ASP.NET Core hosted Blazor WebAssembly app it's possible to make use of server prerendering. With this technique first user request is responded to by server instantaneously with full page html. This is useful for SEO and fast content delivery while app is still downloading in the background. Since prerendering happens only on the server with no connection to the browser, it's invalid to call any JS Interop at that time.

- InvalidOperationException: JavaScript interop calls cannot be issued during server-side prerendering, 
- because the page has not yet loaded in the browser. Prerendered components must wrap any JavaScript 
- interop calls in conditional logic to ensure those interop calls are not attempted during prerendering.

- Blazor.IntersectionObserver.IntersectionObserverService..ctor(IJSRuntime jsRuntime) in IntersectionObserverService.cs
-    27. this.moduleTask = jsRuntime.InvokeAsync<IJSObjectReference>("import", this.scriptPath).AsTask();

Steps to reproduce the behavior:

https://docs.microsoft.com/en-us/aspnet/core/blazor/components/prerendering-and-integration?view=aspnetcore-5.0&pivots=webassembly

Expected behavior

Import JS module only when component is rendered in the browser.

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        if (module == null)
        {
            module = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "...");            
        }
    }
}
@ljbc1994
Copy link
Owner

ljbc1994 commented Aug 5, 2021

Hey thanks for raising..

Is this coming from the IntersectionObserve component or a custom component?

We do this here:

We also have it as a task for invocation:

this.moduleTask = jsRuntime.InvokeAsync<IJSObjectReference>("import", this.scriptPath).AsTask();

@Xeevis
Copy link
Author

Xeevis commented Aug 6, 2021

Hello, I've looked at this more closely and in my case it's the IIntersectionObserverService, which can't be injected directly because of JsInterop call in constructor

@inject IIntersectionObserverService ObserverService

So what I did instead was to inject IServiceProvider and retrieve this service in OnAfterRender()

private IIntersectionObserverService observerService;

[Inject]
private IServiceProvider ServiceProvider { get; set; }

public IntersectionObserver Observer { get; internal set; }

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        this.observerService ??= this.ServiceProvider.GetRequiredService<IIntersectionObserverService>();
        this.Observer = await this.observerService.Observe(this.elementRef, (entries) =>
        {
            //(...)
        });        
    }
}

Perhaps it would be worth a mention in README?

@ljbc1994
Copy link
Owner

ljbc1994 commented Sep 4, 2021

Thanks for looking into this @Xeevis.

I'll spend some more time checking this out to see if there's anything I can do. If not, I'll add this to the readme.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants