Skip to content

Commit

Permalink
Sorting in axo data (#410)
Browse files Browse the repository at this point in the history
* Create draft PR for #404

* Changed TakeLast to Order().Take

* Changed Excel exporter

* Improved sorting in data exporters

* Added sorting option.
Added using nested properties in custom columns.

* Remove useless link

* Update sorting in InMemory, Json and Raven repositories

* Test repair

* Added documentation.
Custom columns are automatically added into sort elements if sort is enabled

---------

Co-authored-by: IX-BOT <137874481+IX-BOT@users.noreply.github.com>
Co-authored-by: Peter Kurhajec <61538034+PTKu@users.noreply.github.com>
  • Loading branch information
3 people authored Jun 25, 2024
1 parent 3c23453 commit 3991413
Show file tree
Hide file tree
Showing 26 changed files with 245 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ public DataBrowser(IRepository<T> repository)

protected IRepository<T> Repository { get; set; }

public void Filter(string identifier, int limit = 10, int skip = 0, eSearchMode searchMode = eSearchMode.Exact)
public void Filter(string identifier, int limit = 10, int skip = 0, eSearchMode searchMode = eSearchMode.Exact, string sortExpresion = "Default", bool sortAscending = false)
{
Records.Clear();

foreach (var item in this.Repository.GetRecords(identifier, limit: limit, skip: skip, searchMode))
foreach (var item in this.Repository.GetRecords(identifier, limit: limit, skip: skip, searchMode, sortExpresion, sortAscending))
{
this.Records.Add(item);
}
Expand Down Expand Up @@ -374,7 +374,7 @@ public static class DataBrowser
public interface IDataBrowser
{
IList<object> Records { get; }
void Filter(string identifier, int limit, int skip, eSearchMode searchMode);
void Filter(string identifier, int limit, int skip, eSearchMode searchMode, string SortExpresion, bool sortAscending);
object FindById(string id);
IEnumerable<object> FindByCreatedRange(DateTime start, DateTime end);
IEnumerable<object> FindByModifiedRange(DateTime start, DateTime end);
Expand Down
2 changes: 1 addition & 1 deletion src/base/src/AXOpen.Base.Abstractions/Data/IRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public interface IRepository<T> where T : IBrowsableDataObject
void Delete(string identifier);
bool Exists(string identifier);
long FilteredCount(string id, eSearchMode searchMode = eSearchMode.Exact);
IEnumerable<T> GetRecords(string identifier = "*", int limit = 100, int skip = 0, eSearchMode searchMode = eSearchMode.Exact);
IEnumerable<T> GetRecords(string identifier = "*", int limit = 100, int skip = 0, eSearchMode searchMode = eSearchMode.Exact, string sortExpresion = "Default", bool sortAscending = false);
T Read(string identifier);
void Update(string identifier, T data);
OnCreateDelegate<T> OnCreate { get; set; }
Expand Down
6 changes: 3 additions & 3 deletions src/base/src/AXOpen.Base.Abstractions/Data/RepositoryBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ public ValidateDataDelegate<T> OnRecordUpdateValidation
/// <param name="limit">Limit of documents to retrieve.</param>
/// <param name="skip">Number of documents to be skipped.</param>
/// <returns></returns>
protected abstract IEnumerable<T> GetRecordsNvi(string identifierContent, int limit, int skip, eSearchMode searchMode);
protected abstract IEnumerable<T> GetRecordsNvi(string identifierContent, int limit, int skip, eSearchMode searchMode, string sortExpresion, bool sortAscending);

/// <summary>
/// Counts records that contain given string in the id. (Concrete implementation of given repository type)
Expand Down Expand Up @@ -416,11 +416,11 @@ public void Delete(string identifier)
/// <summary>
/// Gets <see cref="IEnumerable{T}"/> of repository entries that match the identifier.
/// </summary>
public IEnumerable<T> GetRecords(string identifier, int limit = 10, int skip = 0, eSearchMode searchMode = eSearchMode.Exact)
public IEnumerable<T> GetRecords(string identifier, int limit = 10, int skip = 0, eSearchMode searchMode = eSearchMode.Exact, string sortExpresion = "Default", bool sortAscending = false)
{
try
{
return GetRecordsNvi(identifier, limit, skip, searchMode);
return GetRecordsNvi(identifier, limit, skip, searchMode, sortExpresion, sortAscending);
}
catch (Exception e)
{
Expand Down
31 changes: 31 additions & 0 deletions src/base/src/AXOpen.Base.Abstractions/PropertyHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Reflection;

namespace AXOpen.Base
{
public static class PropertyHelper
{
public static object? GetPropertyValue(object obj, string propertyPath)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));

if (string.IsNullOrEmpty(propertyPath))
throw new ArgumentNullException(nameof(propertyPath));

string[] properties = propertyPath.Split('.');
foreach (string property in properties)
{
if (obj == null) return null;

PropertyInfo propertyInfo = obj.GetType().GetProperty(property);
if (propertyInfo == null)
throw new ArgumentException($"Property '{property}' not found on '{obj.GetType().Name}'");

obj = propertyInfo.GetValue(obj, null);
}

return obj;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@
</div>
</div>

<div class="card">
<div class="card-header">
<h2>Sorting</h2>
</div>
<div class="card-body">
//<Sorting>
<DataExchangeView Vm="@_vm_columndata" Presentation="Command" EnableSorting="true" SortElements='new() {"SomeData"}'>
<ColumnData HeaderName="Some data" BindingValue="SomeData" Clickable="false" />
<ColumnData HeaderName="A number" BindingValue="SomeNumber" Clickable="false" />
<ColumnData HeaderName="Yes/No" BindingValue="SomeBool" Clickable="false" />
</DataExchangeView>
//</Sorting>
</div>
</div>



//<CustomColumnsCode>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="~/" />
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
<link rel="stylesheet" href="css/open-iconic/font/css/open-iconic-bootstrap.min.css" />
<link rel="stylesheet" href="font/bootstrap-icons.min.css" />
<link href="css/site.css" rel="stylesheet" />
<link href="librarytemplate.blazor.styles.css" rel="stylesheet" />
<link rel="icon" type="image/png" href="favicon.png"/>
Expand Down

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
11 changes: 11 additions & 0 deletions src/data/docs/SORT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Sorting

To enable sorting options, set the attribute `EnableSorting` to true and specify the list `SortElements` with the names of the attributes as strings.

If you are using custom columns and `EnableSorting` is set to true, these columns will be automatically added to `SortElements`.

The example of usage:

[!code-smalltalk[](../app/ix-blazor/librarytemplate.blazor/Pages/Rendering.razor?name=Sorting)]

![Sorting](assets/Sort.png)
Binary file added src/data/docs/assets/Sort.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion src/data/docs/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@
- name: Export/Import
href: EXPORT.md
- name: Modal detail view
href: MODALVIEW.md
href: MODALVIEW.md
- name: Sorting
href: SORT.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@namespace AXOpen.Data
@using AXOpen.Base
@using AXOpen.Data;
@using AXSharp.Connector;
@using CommunityToolkit.Mvvm.ComponentModel;
Expand Down Expand Up @@ -26,7 +27,24 @@ else
<button type="button" class="btn btn-primary ms-2" data-bs-toggle="modal" data-bs-target="#importModal-@ViewGuid" @onclick="async () => { isFileImported = false; await LoadCustomExportDataAsync(); }">@Localizer["Import"]</button>
}
}

<div class="btn-group ms-auto" role="group">
@if (EnableSorting)
{
<div class="btn-group" role="group">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">@Vm.SortExpresion.ToString()</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item @(Vm.SortExpresion == "Default" ? "active" : "")" @onclick='() => setSortExpresionAsync("Default")'>Default</a></li>
@foreach (var element in SortElements)
{
<li><a class="dropdown-item @(Vm.SortExpresion == element ? "active" : "")" @onclick='() => setSortExpresionAsync(element)'>@element</a></li>
}
</ul>
</div>
<button class="btn btn-outline-secondary" type="button" name="sort" aria-label="@(Vm.SortAscending ? "Sort ascending" : "Sort descending")" title="@(Vm.SortAscending ? "Sort ascending" : "Sort descending")" @onclick="() => setSortAscendingAsync()">
<i class="bi bi-filter @(Vm.SortAscending ? "rotate" : "")"></i>
</button>
}
<div class="btn-group" role="group">
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">@Localizer[Vm.SearchMode.ToString()]</button>
<ul class="dropdown-menu">
Expand Down Expand Up @@ -54,9 +72,9 @@ else
<th scope="col">DataEntityId</th>

<CascadingValue Value="this" IsFixed>
@ChildContent
</CascadingValue>
@foreach (ColumnData column in Columns)
@ChildContent
</CascadingValue>
@foreach (ColumnData column in Columns)
{
if (column.HeaderName is null || column.HeaderName == "")
{
Expand Down Expand Up @@ -109,11 +127,11 @@ else
{
if (column.Clickable)
{
<td data-bs-toggle="modal" href="#viewModal-@ViewGuid" style="cursor: pointer" @onclick="() => Vm.SelectedRecord = item">@(item.GetType().GetProperty(column.BindingValue)?.GetValue(item))</td>
<td data-bs-toggle="modal" href="#viewModal-@ViewGuid" style="cursor: pointer" @onclick="() => Vm.SelectedRecord = item">@(PropertyHelper.GetPropertyValue(item, column.BindingValue))</td>
}
else
{
<td>@(item.GetType().GetProperty(column.BindingValue)?.GetValue(item))</td>
<td>@(PropertyHelper.GetPropertyValue(item, column.BindingValue))</td>
}
}
}
Expand All @@ -125,11 +143,11 @@ else
{
if (column.Clickable)
{
<td style="cursor: pointer" @onclick="() => Vm.SelectedRecord = item">@(item.GetType().GetProperty(column.BindingValue)?.GetValue(item))</td>
<td style="cursor: pointer" @onclick="() => Vm.SelectedRecord = item">@(PropertyHelper.GetPropertyValue(item, column.BindingValue))</td>
}
else
{
<td>@(item.GetType().GetProperty(column.BindingValue)?.GetValue(item))</td>
<td>@(PropertyHelper.GetPropertyValue(item, column.BindingValue))</td>
}
}
}
Expand Down Expand Up @@ -560,7 +578,7 @@ else
{
<AXOpen.Data.Blazor.AxoDataExchange.DataExchangeAccordionComponent AccordionContent="item" Vm="Vm" Fragment="fragment" Parent="_data" ViewGuid="ViewGuid" />
}
}
}
</ul>
</div>
</div>
Expand Down Expand Up @@ -610,7 +628,7 @@ else
<span class="oi oi-warning" aria-hidden="true" b-p1s6lcmqsx=""></span>
<div>
@Localizer["This file may not be able to be imported!"]

</div>
</div>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
using AXOpen.Data.Interfaces;
using AXOpen.Core;
using AXOpen.Data;
using System.Data.Common;

namespace AXOpen.Data;

Expand All @@ -39,8 +40,12 @@ public partial class DataExchangeView : ComponentBase, IDisposable

[Parameter] public bool CanExport { get; set; } = false;

[Parameter] public bool EnableSorting { get; set; } = false;

[Parameter] public RenderFragment ChildContent { get; set; }

[Parameter] public List<string> SortElements { get; set; } = new();

[Inject]
private IAlertService _alertDialogService { get; set; }

Expand All @@ -59,10 +64,12 @@ public partial class DataExchangeView : ComponentBase, IDisposable
public void AddLine(ColumnData line)
{
if (!Columns.Contains(line))
{
Columns.Add(line);
StateHasChanged();
}

if (!SortElements.Contains(line.BindingValue))
SortElements.Add(line.BindingValue);

StateHasChanged();
}

public void RemoveLine(ColumnData line)
Expand All @@ -88,6 +95,20 @@ private async Task setSearchModeAsync(eSearchMode searchMode)
await Vm.FillObservableRecordsAsync();
}

private async Task setSortExpresionAsync(string sortExpresion)
{
Vm.SortExpresion = sortExpresion;

await Vm.FillObservableRecordsAsync();
}

private async Task setSortAscendingAsync()
{
Vm.SortAscending = !Vm.SortAscending;

await Vm.FillObservableRecordsAsync();
}

private async Task setLimitAsync(int limit)
{
var oldLimit = Vm.Limit;
Expand All @@ -109,7 +130,6 @@ protected override async Task OnInitializedAsync()
{
await Vm.FillObservableRecordsAsync();
Vm.StateHasChangedDelegate = StateHasChanged;

}

private string _inputFileId = Guid.NewGuid().ToString();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.rotate {
display: inline-block;
transform: rotate(180deg);
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,11 @@ public Task FillObservableRecordsAsync()
});
}

public IEnumerable<IBrowsableDataObject> Filter(string identifier, int limit = 10, int skip = 0, eSearchMode searchMode = eSearchMode.Exact)
public IEnumerable<IBrowsableDataObject> Filter(string identifier, int limit = 10, int skip = 0, eSearchMode searchMode = eSearchMode.Exact, string sortExpresion = "Default", bool sortAscending = false)
{
Records.Clear();

foreach (var item in this.DataExchange.GetRecords(identifier, limit: limit, skip: skip, searchMode))
foreach (var item in this.DataExchange.GetRecords(identifier, limit: limit, skip: skip, searchMode, sortExpresion, sortAscending))
{
this.Records.Add(item);
}
Expand All @@ -154,7 +154,7 @@ public long CountFiltered(string id, eSearchMode searchMode = eSearchMode.Exact)

public void UpdateObservableRecords()
{
Filter(FilterById, Limit, Page * Limit, SearchMode).ToList();
Filter(FilterById, Limit, Page * Limit, SearchMode, SortExpresion, SortAscending).ToList();
}

public async Task Filter()
Expand Down Expand Up @@ -346,6 +346,8 @@ public Task ImportDataAsync(string path)
public int Limit { get; set; } = 10;
public string FilterById { get; set; } = "";
public eSearchMode SearchMode { get; set; } = eSearchMode.Exact;
public string SortExpresion { get; set; } = "Default";
public bool SortAscending { get; set; } = false;
public long FilteredCount { get; set; }
public int Page { get; set; } = 0;
public string CreateItemId { get; set; }
Expand Down
4 changes: 2 additions & 2 deletions src/data/src/AXOpen.Data/DataExchange/AxoDataExchange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,9 @@ public bool IsHashCorrect(IIdentity identity)

/// <inheritdoc />
public IEnumerable<IBrowsableDataObject> GetRecords(string identifier, int limit, int skip,
eSearchMode searchMode)
eSearchMode searchMode, string sortExpresion, bool sortAscending)
{
return DataRepository.GetRecords(identifier, limit, skip, searchMode).Cast<IBrowsableDataObject>();
return DataRepository.GetRecords(identifier, limit, skip, searchMode, sortExpresion, sortAscending).Cast<IBrowsableDataObject>();
}

/// <inheritdoc />
Expand Down
2 changes: 1 addition & 1 deletion src/data/src/AXOpen.Data/DataExchange/IAxoDataExchange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ public partial interface IAxoDataExchange
/// <param name="searchMode">Set the search mode fot his query. <seealso cref="eSearchMode"/></param>
/// <returns>Records from the associated repository meeting criteria.</returns>
IEnumerable<IBrowsableDataObject> GetRecords(string identifier, int limit, int skip,
eSearchMode searchMode);
eSearchMode searchMode, string sortExpresion, bool sortAscending);

/// <summary>
/// Gets record meeting criteria from the <see cref="Repository"/> associated with this <see cref="IAxoDataExchange"/> where the data entity id matches exactly the argument.
Expand Down
Loading

0 comments on commit 3991413

Please sign in to comment.