I'm trying to wrap an external JavaScript library (a drag & drop one) which makes DOM modifications, but I don't really understand how blazor interacts this way.
A simple example:
JavaScript:
function ModifyDOM(dotNetHelper, element)
{
element.innerText = "hello";
dotNetHelper.invokeMethodAsync("Update");
}
Blazor:
@inject IJSRuntime JS
<div @ref="list">
@foreach (var color in colors)
{
<div @key="color">@color</div>
}
</div>
@code
{
ElementReference list;
List<string> colors = new() { "primary", "secondary", "success", "danger", "warning", "info", "light", "dark" };
private DotNetObjectReference<FetchData>? dotNetHelper;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender) {
dotNetHelper = DotNetObjectReference.Create(this);
await JS.InvokeVoidAsync("ModifyDOM", dotNetHelper, list);
}
}
[JSInvokable]
public async Task Update()
{
@* await Task.Yield(); *@
await InvokeAsync(StateHasChanged); // here I'm expecting to reload de original DOM, but it's not happening
}
public void Dispose()
{
dotNetHelper?.Dispose();
}
}
When I call StateHasChanged, I was expecting to Blazor to redraw the original RenderTree, but it's not happening. It's keeping the "hello" innerText.
--- UPDATE ---
This is a complete example, I want to wrap SortableJS for sorting lists because there is not similar implementation in Blazor
But as you can see in the video below seems that Blazor is messing up with refreshing the component (on the console log i'm writing the Blazor current lists)
You should make all the modification of the DOM in you Blazor app. Blazor has its own internal binary representation of the DOM. When it renders something, it calculates diff of current DOM representation and the new one. You change the DOM in browser, but its representaion in Blazor remains unchanged. That's why StateHashChanged() has no effect in your example.
<div @ref="list">
@foreach (var color in colors)
{
<div @key="color">@color</div>
}
</div>
In this case, the collection colors doesn't change and so "Blazor thinks" there are no changes to be applied to the DOM.
The Blazor rendering code has no idea you changed the text in JavaScript.
...how blazor interacts this way.
Blazor has its own copy of the Dom (like a 'shadow Dom' but afaik using its own implementation).
On a re-render it builds a new (partial) copy and compares that to the old one. Any differences are propagated to the actual DOM.
So when JS makes a change to the actual DOM this is 'out of sight' for Blazor and those changes will stay (usually a plus) until you F5 or make Blazor think it has changed.
A best practice would be to keep Blazor and JS away from each other, but when you want to reset your list: