So I've added sortablejs to my blazor application, and it works - for the first drag n drop.
First, here's what's happening:
Blazor component code:
<ul class="ulManufacturers">
@foreach (var m in manufacturers)
{
var mGrayedOut = m.Visible ? "" : "grayout";
var toggleButtonImg = showManufacturerBikes[m] ? "up" : "down";
var toggleButtonTitle = showManufacturerBikes[m] ? $"Sakrij {m.Nickname} motore" : $"Prikaži {m.Nickname} motore";
<li data-id="@m.Id">
<div class="divManufacturerHeader">
<div class="buttons">
<img class="zoom-on-hover cursor-grab fit-image grayout drag-handle"
src="/lib/bootstrap-icons-1.5.0/list.svg"
data-toggle="tooltip" data-placement="top"
title="Uhvati i premesti"/>
<img class="zoom-on-hover cursor-pointer fit-image grayout"
src="/lib/bootstrap-icons-1.5.0/plus-circle.svg"
data-toggle="tooltip" data-placement="top"
title="Dodaj novi @m.Nickname motor"
@onclick="()=>AddBike(m)" />
<img class="zoom-on-hover cursor-pointer fit-image grayout"
src="/lib/bootstrap-icons-1.5.0/arrow-@(toggleButtonImg)-circle.svg"
data-toggle="tooltip" data-placement="top"
title="@toggleButtonTitle"
@onclick="()=>ToggleBikes(m)" />
</div>
<div class="divLogoOrName">
@if (string.IsNullOrEmpty(m.LogoUrl))
{
<h1 class="zoom-on-hover cursor-pointer @mGrayedOut"
@onclick="()=>SelectManufacturer(m)">
@m.Nickname
</h1>
}
else
{
<img class="zoom-on-hover cursor-pointer fit-image imgManufacturerLogo @mGrayedOut"
src="@m.LogoUrl"
@onclick="()=>SelectManufacturer(m)" />
}
</div>
</div>
<div class="divBikeThumbnailsContainer sortable-list @(showManufacturerBikes[m] ? "" : "hidden")">
@foreach (var b in m.Bikes)
{
var bGrayedOut = b.Visible ? string.Empty : "grayout";
<div class="sortable-list-grid-item drag-handle @bGrayedOut" @onclick="() => SelectBike(b)">
<img class="zoom-on-hover cursor-grab fit-image grayout"
src="/lib/bootstrap-icons-1.5.0/list.svg"
data-toggle="tooltip" data-placement="top"
title="Uhvati i premesti" />
<BikeThumbnailPage Bike="b" />
</div>
}
</div>
</li>
}
@if (showButtonAddManufacturer)
{
<li>
<div class="divManufacturerHeader">
<img class="zoom-on-hover cursor-pointer fit-image grayout"
src="/lib/bootstrap-icons-1.5.0/plus-circle.svg"
data-toggle="tooltip" data-placement="top"
title="Dodaj novog proizvodjača"
@onclick="AddManufacturer" />
</div>
</li>
}
</ul>
@code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var objRef = DotNetObjectReference.Create(this);
await jsRuntime.InvokeVoidAsync("EnableManufacturerSorting", objRef);
}
await base.OnAfterRenderAsync(firstRender);
}
[JSInvokable]
public void OnManufacturerDragStart()
{
// hide all manufacturer bikes
foreach (var m in manufacturers)
showManufacturerBikes[m] = false;
showButtonAddManufacturer = false;
StateHasChanged();
}
[JSInvokable]
public async void OnManufacturerDragEnd(int[] ids)
{
showButtonAddManufacturer = true;
// set order numbers for manufacturers
for (int orderNo = 0; orderNo < ids.Length; orderNo++)
manufacturers
.First(m => m.Id == ids[orderNo])
.OrderNumber = orderNo;
_manufacturerService.UpdateAll(manufacturers);
//ReloadManufacturers();
//StateHasChanged();
//var objRef = DotNetObjectReference.Create(this);
//await jsRuntime.InvokeVoidAsync("EnableManufacturerSorting", objRef);
}
}
JavaScript function that's fired on
function EnableManufacturerSorting(dotNetHelper) {
$('.ulManufacturers').each(function() {
new Sortable(this, {
group: 'manufacturers',
handle: '.drag-handle',
direction: 'vertical',
animation: 150,
onStart: function () {
console.log("onStart");
dotNetHelper.invokeMethodAsync('OnManufacturerDragStart');
},
onEnd: function () {
console.log("onEnd");
var orderList = [];
$(this.el).children().each(function (index, element) {
orderList.push($(element).data('id'));
});
var params = JSON.stringify({ 'orderList': orderList });
dotNetHelper.invokeMethodAsync('OnManufacturerDragEnd', orderList);
//dotNetHelper.dispose();
//this.destroy();
},
});
});
}
As you can see from comments in JS function and OnManufacturerDragEnd blazor function, I've tried all combinations with and without commented code, but the result is the same
UPDATE 1
I've found out that if I remove
// set order numbers for manufacturers
for (int orderNo = 0; orderNo < ids.Length; orderNo++)
manufacturers
.First(m => m.Id == ids[orderNo])
.OrderNumber = orderNo;
_manufacturerService.UpdateAll(manufacturers);
from public async void OnManufacturerDragEnd(int[] ids) it works fine -> but I end up not saving the new order of items.
Same if I leave that in but remove
showButtonAddManufacturer = true;
StateHasChanged();
That means will either not save the new order or that I can't hide the button 'add new' (the plus circle at the bottom of the gif, at the bininging), and bring it back.
I suspect there's asynchronicity between sortablejs and blazors rendering, I just can't quite figure it out yet. The 'add new' button might have something to do with it
UPDATE 2
Check my answer, I've found a workaround. The question is now -> what is exactly happening on the second drag? I'll accept your answer if you can just explain to me what is happening.
UPDATE 3
I think that this is the step in the right direction. Is is possilbe to make jQuery and Blazor work in harmony? jQuery not working properly with my Blazor app
