[Validation] data-permanent preserves the marked element's own attributes
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 290
Description
**Scenario contact:** @javiercn
## Scenario
**This scenario validates that a reported bug is fixed.** Enhanced navigation keeps the existing document and patches it, and `data-permanent` marks the parts that must survive untouched.
In .NET 10 the attribute only protected an element's **descendants**. Changes made to the marked element itself, such as a class added by script, were lost on the next enhanced navigation. Moving the attribute to a wrapper made it work, which is what made the behavior confusing to diagnose. ([#51021](https://github.com/dotnet/aspnetcore/issues/51021))
.NET 11 uses `data-permanent` to decide whether to synchronise the element's own attributes as well as whether to recurse into it.
## Minimum build
.NET 11 Preview 7 or later.
## Configurations to cover
* Blazor Web App
* [x] Static SSR
* [ ] Interactive Server
* [ ] Interactive WebAssembly
* [ ] Interactive Auto
* [ ] Standalone WebAssembly
* [ ] Hybrid (MAUI)
The DOM merge runs the same way whatever render mode the pages use, and the reported bug reproduces with statically rendered pages.
## Also exercise
* [ ] Published output
* [x] An existing .NET 10 app upgraded to .NET 11
* [ ] Trimming or ahead-of-time compilation
* [ ] More than one server instance, or a proxy in front
* [ ] Hot Reload
* [ ] An IDE as well as the command line
* [ ] Container
## What to build
Two statically rendered pages with links between them, and a layout rendered on both. In the layout put three elements:
```html
```
Add a script that runs after load and changes each element **itself**, not only its children:
```js
for (const id of ['keep', 'control', 'wrapped']) {
const el = document.getElementById(id);
el.classList.add('expand');
el.setAttribute('data-count', '2');
el.textContent = 'mutated';
}
```
`#keep` is the case that was broken. `#control` is unmarked, so it should be reset. `#wrapped` is protected by a marked parent, which is the arrangement that already worked in .NET 10.
## Things to try
* Navigate to the other page and back, then inspect all three elements in developer tools. This is the reported repro.
* Repeat the round trip several times without a full reload.
* Repeat on .NET 10 to see the original failure.
## Expected behavior
An element marked `data-permanent` survives a navigation exactly as it was, including changes made to the element itself.
### Must hold
* After navigating away and back, `#keep` still has the `expand` class, still has `data-count="2"`, and still reads `mutated`.
* `#control` is reset to its original markup, confirming the marking is what made the difference rather than the navigation being a no-op.
* `#wrapped` keeps its changes too, so the arrangement that worked before still works.
* Repeating the round trip several times produces no accumulating errors in the console.
## Evidence to capture
The markup of `#keep` and `#control` from developer tools, before and after the navigation, so the two can be compared.
## Documentation to use
* [Enhanced navigation and form handling](https://learn.microsoft.com/aspnet/core/blazor/fundamentals/navigation?view=aspnetcore-11.0#enhanced-navigation-and-form-handling)
## What to report
Report results using the format described in the [validation testing manual](https://github.com/dotnet/aspnetcore/issues/68479). Include link to a repository with the test app.
Contributor guide
Assessment
This issue has not been assessed yet.