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

Preservation of attributes and elements in "Enhanced navigation" #49613

Closed
1 task done
marinasundstrom opened this issue Jul 24, 2023 · 8 comments
Closed
1 task done
Assignees
Labels
area-blazor Includes: Blazor, Razor Components enhancement This issue represents an ask for new feature or an enhancement to an existing one feature-full-stack-web-ui Full stack web UI with Blazor
Milestone

Comments

@marinasundstrom
Copy link

marinasundstrom commented Jul 24, 2023

Is there an existing issue for this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

Background

I have a pre-rendered server-side rendered site which is using Bootstrap, and it has got some scripts to apply color modes (Dark mode, Light mode etc).

The scripts are supposed run every time a page loads to make sure that:

  1. The data-bs-theme is set on html to whatever mode is preferred by the user: e.g. dark.
  2. The theme indicator in the navbar gets updated to reflect the active theme.

This breaks due to how "enhanced navigation" works.

Problem description

Upon navigating to page by clicking a link:

  1. The script is not executed.
  2. The data-bs-theme is removed in the diff. Causing the theme to be reset,
  3. The theme indicator in the navbar is reset, when it should be preserved.

I would be OK with the script only executing once if the important elements were preserved.

Describe the solution you'd like

My suggestion is to implement:

  1. a way to exclude certain elements and attributes from changing due to the diff being applied as part of enhanced navigation. E.g. the data-bs-theme attribute in the html element.

  2. a way to mark a component or element so that it is not affected by the diff. Use case: In order to preserve the state of the theme indicator.

Unless there are other ways to deal with this.

Additional context

Try using the theme selector here and navigation back and forth:
https://blazor8app.graypebble-0c46426e.westus2.azurecontainerapps.io/

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-blazor Includes: Blazor, Razor Components label Jul 24, 2023
@marinasundstrom
Copy link
Author

Is it possible to turn "enhanced navigation" off?

@javiercn
Copy link
Member

@marinasundstrom thanks for contacting us.

Modifying the DOM outside of the scope of Blazor is problematic, as we will silently override your changes (as it's happening).

It looks like we don't have a way to turn off enhanced navigation, so we should add one.

In addition to that, we don't have a way to specify that something needs to be preserved. There are a couple of ways you could go about it.

  • Register a MutationObserver in the element you care about, and when you see the attribute being removed, simply add it back.

On our side, we could potentially have a way to let other frameworks/code play nice with enhance navigation by giving them a way to specify attributes/elements that must be preserved.

@mkArtakMSFT mkArtakMSFT added enhancement This issue represents an ask for new feature or an enhancement to an existing one feature-full-stack-web-ui Full stack web UI with Blazor labels Jul 25, 2023
@mkArtakMSFT mkArtakMSFT added this to the 8.0-rc1 milestone Jul 25, 2023
@mkArtakMSFT
Copy link
Member

Keeping in RC1 to see if we can add a way to turn off enhanced navigation.

@SteveSandersonMS
Copy link
Member

We do already have a way to turn it off:

Blazor.start({
    ssr: { disableDomPreservation: true }
});

@marinasundstrom
Copy link
Author

marinasundstrom commented Aug 1, 2023

I have come to like DOM preservation. It really is more like a SPA - when it works. Especially when interactive components are present. But you need to get used to it and design you apps to fit it.

I'm trying to build an e-shop site.

A server-side rendered app with several Blazor WebAssembly components: A cart display icon living in the navbar, and an off-canvas for the cart. They are not pre-rendered and pretty much lives on the site as long as the DOM is preserved.

There are some other problems, like with the buffer stuff. Turning of pre-rendering for non-essential WASM components help with that.

And I even had a case of manipulating the DOM - moving the Cart display (Blazor WASM component) in the navbar to be displayed correctly on small screens and vice versa. That required timeouts to be set to wait for the component to be initialized, and then to set up the necessary event handlers for window resizing.


The problem with the attribute being removed is of course a problem for me - but right now it is not urgent for my work.

@marinasundstrom
Copy link
Author

marinasundstrom commented Aug 13, 2023

My main problems with the enhanced navigation in SSR is:

  1. Not having a way to get notified when navigation has occurred. Need that to run scripts.

  2. Not having a way to prevent nodes from being able to be replaced. To specify what nodes and attributes should be preserved during replacement.

Other frameworks (JS)

Fireship recently posted a video about View Transitions in Astro. It provides APIs to deal with transition in "soft navigation".

  • Custom event for when having done the navigation - i.ex. intercepted the navigation and applied the changes to the DOM. For running scripts.

  • Attribute to make the framework preserve an element during the replacement or transition.

More about View Transitions in the Astro docs.

Suggestion

Perhaps the Astro way is the way to go for Blazor as well.

  1. Custom JS events to notify about the navigation. In order to be able to run Javascripts after navigation.
document.addEventListener('blazor:beforeload', () => { console.log('loaded'); })
  1. Adding a @preserveNodedirective. Preferably a way to tell it to preserve certain HTML attributes. @preserveAttributes="style"
<div>
  <span>Foo</span>
  <div @preserveNode>
      Text
  </div>
</div>
<html @preserveAttributes="data-bs-theme" data-bs-theme="dark">
    <head>
    </head>

    <body> 
    </body>
</html>

@MackinnonBuck
Copy link
Member

I anticipate this will be a very popular feature request. It's common for Blazor apps and libraries to dynamically modify statically rendered DOM. For example, libraries frequently use JS initializers to dynamically add required scripts or stylesheets to the page. Even Blazor's default reconnection UI relies on dynamically modifying the DOM, and it currently breaks after an enhanced navigation occurs because it assumes that once it's added to the DOM, it stays there. It will be unfortunate if so many apps need to disable one of Blazor's major features in .NET 8 in order to work correctly.

Rather than making the preservation of DOM elements opt-in, I wonder if we could update our DOM sync logic to add some metadata to all server-rendered DOM elements to mark them as candidates for removal/replacement during an enhanced update. We could even go further and have each element hold some metadata tracking which attributes are server-rendered. Then during synchronization, any DOM element that doesn't include such metadata will be preserved. This should allow existing apps to continue working without having to go through and manually mark elements or attributes as preserved.

Of course, the main downside is that this complicates the DOM synchronization algorithm further. And if the metadata was represented by invisible properties on each DOM node (similar to how Blazor's "logical elements" work), we would have to ensure that the entire initial DOM gets populated with that metadata before other library/app JS code has the chance to dynamically mutate the DOM. Otherwise, DOM mutations happening immediately upon page load would get reverted when the first enhanced update happens. The alternative is to have the server embed the metadata as HTML attributes, but that adds a lot of clutter to the DOM.

@danroth27 danroth27 modified the milestones: .NET 9 Planning, 8.0-rc2 Aug 18, 2023
@marinasundstrom
Copy link
Author

marinasundstrom commented Aug 22, 2023

@MackinnonBuck This (#50268) seems to be another issue with enhanced navigation and DOM preservation not doing as expected.

It is unable to apply the title from a page that is loaded with soft navigation when having mixed render modes and an HeadOutlet with render mode Auto.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components enhancement This issue represents an ask for new feature or an enhancement to an existing one feature-full-stack-web-ui Full stack web UI with Blazor
Projects
None yet
Development

No branches or pull requests

6 participants