Local service (#6)

* * registered page level services instead scoped

* * warning handling

Co-authored-by: Santiago Cattaneo <santiago@rd-its.com>
This commit is contained in:
elgransan 2022-04-22 19:20:26 -03:00 committed by GitHub
parent ae38664643
commit 844ae5a2d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 114 additions and 52 deletions

View file

@ -3,15 +3,16 @@
<PageTitle>Blazor Reorder Example</PageTitle>
<h1>Callbacks</h1>
<div>@callback: @data</div>
<p>Callbacks and page level service</p>
<div>@callback @data</div>
<div class="row">
<div class="col-4">
<div class="card">
<Reorder Items="list1" TItem="ItemList"
OnStart='(i) => { callback = "OnStart"; data = i.ToString(); }'
OnChange='(i) => { callback = "OnChange"; data = i.ToString(); }'
OnFinish='(i) => { callback = "OnFinish"; data = i.ToString(); }'>
<Reorder Items="list1" TItem="ItemList" ReorderService="rs"
OnStart='(i) => { callback = "OnStart"; data = i.ToString() ?? ""; }'
OnChange='(i) => { callback = "OnChange"; data = i.ToString() ?? ""; }'
OnFinish='(i) => { callback = "OnFinish"; data = i.ToString() ?? ""; }'>
<div class="mb-2 mx-2">
<h5>@context.title</h5>
<p>@context.details</p>
@ -21,7 +22,7 @@
</div>
<div class="col-4">
<div class="card">
<Reorder Items="list2" TItem="ItemList"
<Reorder Items="list2" TItem="ItemList" ReorderService="rs"
OnStart='(i) => i.domClass = "bg-danger"'
OnChange='(i) => i.domClass = "bg-info"'
OnFinish='(i) => i.domClass = "bg-success"'>
@ -34,7 +35,7 @@
</div>
<div class="col-4">
<div class="card">
<Reorder Items="list3" TItem="ItemList">
<Reorder Items="list3" TItem="ItemList" ReorderService="rs">
<div class="mb-2 mx-2">
<h5>@context.title</h5>
<p>@context.details</p>
@ -48,6 +49,25 @@
private string callback = "";
private string data = "";
// You can declare the service at page level (without registering on Progam.cs)
// but yo must include the service in every component you use in the page in order make it work
ReorderService<ItemList> rs = new();
// In this example we are registering a new class inline
public class ItemList
{
public string title { get; set; }
public string details { get; set; }
public string domClass { get; set; }
public ItemList(string a, string c)
{
title = a;
domClass = "";
details = c;
}
}
public List<ItemList> list1 = new()
{
new ItemList("Google", "Again looking for a bug ..."),

View file

@ -1,29 +1,15 @@
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using BlazorReorderExample;
using BlazorReorderList;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped<BlazorReorderList.ReorderService<ListItem>>();
builder.Services.AddScoped<BlazorReorderList.ReorderService<ItemList>>();
builder.Services.AddScoped<IReorderService<ListItem>, ReorderService<ListItem>>();
await builder.Build().RunAsync();
public record ListItem(string title, string url, string details);
public class ItemList
{
public string title { get; set; }
public string details { get; set; }
public string domClass { get; set; }
public ItemList(string a, string c)
{
title = a;
domClass = "";
details = c;
}
}

View file

@ -0,0 +1,25 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
namespace BlazorReorderList
{
public interface IReorderService<TItem> : IAsyncDisposable
{
bool isDragging { get; set; }
TItem? selected { get; set; }
List<TItem>? originItems { get; set; }
int elemIndex { get; set; }
point elemClickPosition { get; set; }
ValueTask<clientRect> getClientRect(ElementReference el);
ValueTask<point> getPoint(double PageX, double PageY, double ClientX, double ClientY);
ValueTask<point> getPosition(ElementReference el);
ValueTask<int> getWidth(ElementReference el);
ValueTask initEvents(DotNetObjectReference<Reorder<TItem>> dotNetInstance);
ReorderService<TItem> InitService(IJSRuntime jsRuntime);
bool isCopy();
ValueTask removeEvents(DotNetObjectReference<Reorder<TItem>> dotNetInstance);
void Reset();
void Set(List<TItem> list, TItem item, int index, point clickPoint, bool copy);
}
}

View file

@ -1,11 +1,12 @@
@typeparam TItem
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.JSInterop
@inject ReorderService<TItem> rs
@inject IServiceProvider ServiceProvider
@inject NavigationManager nav
@inject IJSRuntime JS;
@implements IDisposable
@if(rs.isDragging && elemPosition.x != -1000)
@if(rs != null && rs.selected != null && rs.isDragging && elemPosition.x != -1000)
{
<div class="dragging item"
style="width: @(elemWidth)px; left: @(elemPosition.x)px; top: @(elemPosition.y)px; transform: translateX(@(ghostTrans.x)px) translateY(@(ghostTrans.y)px )">
@ -13,7 +14,7 @@
</div>
}
@if (itemElem != null)
@if (rs != null && rs.selected != null && itemElem != null)
{
<div class="sortable" @ref="sortable">
@foreach (var item in Items.Select((v, i) => (v, i)))
@ -30,7 +31,7 @@
{
[Parameter, EditorRequired] public RenderFragment<TItem> ChildContent { get; set; } = null!;
[Parameter, EditorRequired] public List<TItem> Items { get; set; } = null!;
[Parameter] public Func<TItem, TItem> Copy { get; set; }
[Parameter] public Func<TItem, TItem>? Copy { get; set; }
[Parameter] public EventCallback<TItem> OnStart { get; set; }
[Parameter] public EventCallback<TItem> OnChange { get; set; }
[Parameter] public EventCallback<TItem> OnFinish { get; set; }
@ -38,16 +39,18 @@
[Parameter] public bool DisableDrop { get; set; } = false;
[Parameter] public bool DisableDrag { get; set; } = false;
[Parameter] public bool WithShadow { get; set; } = true;
[Parameter] public ReorderService<TItem>? ReorderService { get; set; }
private bool shouldRender = true; // cancel re-rendering
private DotNetObjectReference<Reorder<TItem>>? dotNetHelper; //js-interop 2-ways
Dictionary<int, ElementReference> itemElem = new();
ElementReference? sortable;
ElementReference sortable;
point elemPosition = new point(-1000, 0);
point ghostTrans = new point(0, 0);
point clickPosition = new point(0, 0);
int elemWidth = 0;
int newElemIndex = -1;
private IReorderService<TItem>? rs;
protected override void OnInitialized()
{
@ -61,7 +64,7 @@
public void HandleLocationChanged(object? sender, LocationChangedEventArgs e)
{
if (dotNetHelper == null) return;
if (dotNetHelper == null || rs == null) return;
base.InvokeAsync(async () =>
{
await rs.removeEvents(dotNetHelper);
@ -73,15 +76,22 @@
{
if (firstRender)
{
// set if is using scoped service or page service
var provider = ServiceProvider.GetService(typeof(IReorderService<TItem>));
if (provider != null)
rs = (IReorderService<TItem>)provider;
if (rs == null && ReorderService != null) rs = ReorderService.InitService(JS);
if (rs == null) return;
dotNetHelper = DotNetObjectReference.Create(this);
await rs.initEvents(dotNetHelper);
StateHasChanged();
}
}
public async Task onPress(MouseEventArgs m, TItem item, int index)
{
if (Disabled || DisableDrag) return;
if (itemElem == null) return;
if (itemElem == null || rs == null) return;
shouldRender = false; // Because the method triggers re-render, the click propagation is canceled, if you have a link/or any click event inside it's going to stop working
var ghostElem = itemElem[index];
@ -96,7 +106,7 @@
public async Task onTouch(TouchEventArgs m, TItem item, int index)
{
if (Disabled || DisableDrag) return;
if (itemElem == null) return;
if (itemElem == null || rs == null) return;
shouldRender = false; // Because the method triggers re-render, the click propagation is canceled, if you have a link/or any click event inside it's going to stop working
var ghostElem = itemElem[index];
@ -112,6 +122,7 @@
[JSInvokable]
public async Task onRelease(MouseEventArgs m)
{
if (rs == null) return;
elemPosition = new point(-1000, 0);
if (rs.isDragging && rs.originItems == Items)
{
@ -134,7 +145,7 @@
[JSInvokable]
public async Task onMove(point pos)
{
if (itemElem == null) return;
if (itemElem == null || rs == null || rs.originItems == null || rs.selected == null) return;
if (rs.isDragging)
{
shouldRender = false;
@ -179,6 +190,7 @@
// checks if mouse x/y is on top of an item
async Task<bool> isOnTop(ElementReference item, point pos)
{
if (rs == null) return false;
var box = await rs.getClientRect(item);
if (box.width < 0) return false;
var isx = (pos.x > box.left && pos.x < (box.left + box.width));

View file

@ -4,22 +4,33 @@ using Microsoft.JSInterop;
namespace BlazorReorderList;
public class ReorderService<TItem> : IAsyncDisposable
public class ReorderService<TItem> : IReorderService<TItem>
{
private readonly Lazy<Task<IJSObjectReference>> moduleTask;
public List<TItem>? originItems;
public int elemIndex = -1;
public TItem selected = default(TItem);
public point elemClickPosition = new point(0, 0);
public bool isDragging = false;
public bool isDragging { get; set; } = false;
public TItem? selected { get; set; } = default;
public List<TItem>? originItems { get; set; }
public int elemIndex { get; set; } = -1;
public point elemClickPosition { get; set; } = new point(0, 0);
private Lazy<Task<IJSObjectReference>>? _moduleTask;
private bool _copy = false;
public ReorderService(IJSRuntime jsRuntime)
{
moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>(
_moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>(
"import", "./_content/BlazorReorderList/ReorderJsInterop.js").AsTask());
}
public ReorderService() { }
public ReorderService<TItem> InitService(IJSRuntime jsRuntime)
{
_moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>(
"import", "./_content/BlazorReorderList/ReorderJsInterop.js").AsTask());
return this;
}
public void Set(List<TItem> list, TItem item, int index, point clickPoint, bool copy)
{
isDragging = true;
@ -41,54 +52,62 @@ public class ReorderService<TItem> : IAsyncDisposable
public void Reset()
{
isDragging = false;
originItems = default(List<TItem>);
selected = default(TItem);
originItems = default;
selected = default;
elemClickPosition = new point(0, 0);
_copy = false;
}
public async ValueTask initEvents(DotNetObjectReference<Reorder<TItem>> dotNetInstance)
{
var module = await moduleTask.Value;
if (_moduleTask == null) throw new Exception("Reorder Component: JS module not initializated");
var module = await _moduleTask.Value;
await module.InvokeVoidAsync("initEvents", dotNetInstance);
}
public async ValueTask removeEvents(DotNetObjectReference<Reorder<TItem>> dotNetInstance)
{
var module = await moduleTask.Value;
if (_moduleTask == null) throw new Exception("Reorder Component: JS module not initializated");
var module = await _moduleTask.Value;
await module.InvokeVoidAsync("removeEvents", dotNetInstance);
}
public async ValueTask<int> getWidth(ElementReference el)
{
var module = await moduleTask.Value;
if (_moduleTask == null) throw new Exception("Reorder Component: JS module not initializated");
var module = await _moduleTask.Value;
return await module.InvokeAsync<int>("getWidth", el);
}
public async ValueTask<point> getPosition(ElementReference el)
{
var module = await moduleTask.Value;
if (_moduleTask == null) throw new Exception("Reorder Component: JS module not initializated");
var module = await _moduleTask.Value;
return await module.InvokeAsync<point>("getPosition", el);
}
public async ValueTask<point> getPoint(double PageX, double PageY, double ClientX, double ClientY)
public async ValueTask<point> getPoint(double pageX, double pageY, double clientX, double clientY)
{
var module = await moduleTask.Value;
return await module.InvokeAsync<point>("getPoint", PageX, PageY, ClientX, ClientY);
if (_moduleTask == null) throw new Exception("Reorder Component: JS module not initializated");
var module = await _moduleTask.Value;
return await module.InvokeAsync<point>("getPoint", pageX, pageY, clientX, clientY);
}
public async ValueTask<clientRect> getClientRect(ElementReference el)
{
var module = await moduleTask.Value;
if (_moduleTask == null) throw new Exception("Reorder Component: JS module not initializated");
var module = await _moduleTask.Value;
return await module.InvokeAsync<clientRect>("getClientRect", el);
}
public async ValueTask DisposeAsync()
{
if (moduleTask.IsValueCreated)
if (_moduleTask == null) throw new Exception("Reorder Component: JS module not initializated");
if (_moduleTask.IsValueCreated)
{
var module = await moduleTask.Value;
var module = await _moduleTask.Value;
await module.DisposeAsync();
GC.SuppressFinalize(this);
}
}
}