gaelj / BlazorCodeMirror6

Blazor wrapper for CodeMirror 6
MIT License
20 stars 3 forks source link

Issue rendering after the first time #187

Open bradleyables22 opened 1 month ago

bradleyables22 commented 1 month ago

Hey! First off want to say what a great project and this already renders much better than BlazorMonaco.

The issue I'm facing is in a .NET 8 interactive page which is used to edit an item's content for display. The first time I visit the edit page (with your component in it) this works perfectly! But if I navigate away from the page and then back the editor does not render fully. If I refresh the page this resolves.

Should I be using this component in a global manor?

Current Logic Snippets:

<CodeMirror6Wrapper @ref=@editor @rendermode="InteractiveServer" 
    IsWASM =false
    Editable =true
    Language=CodeMirrorLanguage.Html
    Theme=ThemeMirrorTheme.XCodeDark
    AllowVerticalResize="false" 
    AutoFormatMarkdown="true" 
    Height="800"
    Doc=@content 
    DocChanged="(x)=>ContentChanged(x)" />

    protected override async Task OnInitializedAsync()
    {
        var state = await _auth.GetAuthenticationStateAsync();
        updatedBy = state?.User?.Identity?.Name ?? "Unknown User";

        using var _context = DbFactory.CreateDbContext();

        var end = await _context.Endpoints
        .Where(x => x.EndpointID == EndpointID)
        .FirstOrDefaultAsync();

        if (end is not null)
        {
            service = end.Service;
            content = end.Content ?? "Start Building Your Documentation Content";
            ready = true;
        }
        else
            _nav.NavigateTo("admin/endpoints");
    }

    private async Task ContentChanged(string c)
    {
    //set for preview page to snatch up
        content = c;
        await _local.SetAsync("endpoint", new PreviewContentItem { Content = content, Service = service });
    }

First Page Visit:

render1

Navigate await Then navigate back

render2

Browser Console: console

gaelj commented 1 month ago

Hi @bradleyables22, thanks for the feedback !

I edited the issue to fix the formatting of the code snippet - I hope you don't mind.

So I've just released 0.8.11 which exposes an InitializeAsync method (accessible via a ref to the component). It allows manually triggering the component's internal OnInitializedAsync lifecycle method. The idea is to allow you to call this method yourself to force a re-initialization of the component whenever needed.

See usage example in PR

If this does not solve your problem, would you mind submitting a PR in which you have adapted one of the example projects to reproduce the problem?

bradleyables22 commented 1 month ago

Fast response time! But not seeming to do the trick. When I get a free day I'll pull and create an isolated page

MadsatItide commented 3 weeks ago

Bumping since I have the same issue. The InitializeAsync did not work for me either.

Has an page reproducing the problem been sent to you?

gaelj commented 3 weeks ago

No, but if you can I'll be happy to look into the issue.

As mentioned already, ideally I would need a pull request with one of the example projects in this repo adapted (or an additional example project created) to showcase the problem in a way where I can immediately work on it. It will also be helpful to test and avoid any regressions in the future.

MadsatItide commented 3 weeks ago

Sounds good, I will try and see if I can reproduce it in your examples. I will make another project where I have it set up and use it kind of like how I'm using it in my own solution, same settings for the Wrapper, etc.

Also a thing Ive noticed that is quite weird, its not so much after first render it breaks. Like I can close my component that has it embedded, as long as I stay on the same Page component, it is alright, but if I navigate to any other page and then back it breaks.

MadsatItide commented 3 weeks ago

I have managed to reproduce it, how do I make the pull request? I am not allowed to push to your repo.

gaelj commented 3 weeks ago

Great news !

So you need to fork this repo (use the GitHub gui) in order to be able to submit your changes in a pull request over here.

As your local changes have already been done on a clone of this repo, you will just need to modify your local repo's remote url to point to your fork, so that you can push to your GitHub fork and then (via GitHub gui) you can open a pull request to merge your changes over here.

More info on how to do this here https://kodekloud.com/blog/change-remote-origin-in-git/

MadsatItide commented 3 weeks ago

Thank you so much, you should be able to see the pull request with the sample project.

The sample project should be easy to find since it doesnt follow your naming conventions at all haha

gaelj commented 3 weeks ago

@bradleyables22 @MadsatItide

The problem is that Blazor's enhanced navigation doesn't play well with CM6's optimized DOM interactions.

One way to work around the problem is to "force reload" on all navigations to a page containing a CM6 editor. Not ideal, but better than nothing.

This can be achieved by adding the attribute data-enhance-nav="false" to the relevant Navlinks in NavMenu.razor.

If navigating with NavigationManager.NavigateTo(...), the forceLoad optional parameter should be set to true.


I noticed that Blazor requires a data-permanent attribute to be set to any element that will be modified by JS so I did that but it did not solve the issue. I'll release that change shortly.

I'm still investigating for a fix that would not require full page reload, but it's not the easiest problem to solve.

gaelj commented 3 weeks ago

Another way to avoid the problem is to have

<HeadOutlet @rendermode="InteractiveServer" />

in App.razor

bradleyables22 commented 3 weeks ago

Sorry I've been swamped at my 9-5! Glad someone is joining in on this! Will put some thought on the above statements. In blazor I see a future where global interactive mode is used sparingly.

MadsatItide commented 3 weeks ago

You're a legend Gael. Thank you so much.

davhdavh commented 2 weeks ago

You probably need to register event listener on the enhancedload events blazor.addEventListener("enhancedload", callback).

Docs are here: https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/static-server-rendering?view=aspnetcore-9.0

gaelj commented 2 weeks ago

Thanks for the insight, @davhdavh . We now don't require any <HeadOutlet>.