Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

https://github.com/Mewriick/Blazor.FlexGrid

GridView component for Blazor
https://github.com/Mewriick/Blazor.FlexGrid

blazor component grid

Last synced: 2 days ago
JSON representation

GridView component for Blazor

Lists

README

        

# Blazor.FlexGrid
GridView component for Blazor

> Easy way for displaying lits of items in table

table_gif

## IMPORTANT!
**Still development not completely finished and rapidly continue. Next versions can contain breaking changes**

**Breaking change after support .NET Core 3.1 Preview 1**
> In Blazor WASM you have to manually add into index.html links to the css and js FlexGrid files
```cs

```

**Version 0.11.0 contains new Triggers feature more info in [wiki](https://github.com/Mewriick/Blazor.FlexGrid/wiki/Triggers)**

**Version 1.0.0 contains breaking change more info in [release](https://github.com/Mewriick/Blazor.FlexGrid/releases/tag/release%2F1.0.0)**

# Installation
[![NuGet Pre Release](https://img.shields.io/badge/nuget-1.0.0-orange.svg)](https://www.nuget.org/packages/Blazor.FlexGrid)

After nuget installation you must create in Blazor.Client app Linker.xml file because nuget uses some features which are not supported in default mono managed interpreter from WebAssembly
(https://github.com/mono/mono/issues/8872)

```cs













```

And Add this into csproj of client project
```cs

```

# Setup
```cs
public void ConfigureServices(IServiceCollection services)
{
services.AddFlexGrid();
}
```

# Setup Blazor App as RazorComponents AKA ServerSide Blazor App
```cs
public void ConfigureServices(IServiceCollection services)
{
services.AddFlexGridServerSide();
}
```
Note: Add the following line to the Configure(...) method of your Startup.cs file.

```cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseFlexGrid(env.WebRootPath);
}
```
From your index.(cs)html add references
```cs

```

For properly working of **LazyLoaded** functionality some services must be registered in IoC. Because in web scenario **FlexGrid** uses **Http** services which are provided in IoC
by default, but in server side scenario you have to provide these services.

```cs
public void ConfigureServices(IServiceCollection services)
{
services.TryAddScoped(typeof(ILazyDataSetLoader<>), typeof(NullLazyDataSetLoader<>));
services.TryAddScoped(typeof(ILazyDataSetItemManipulator<>), typeof(NullLazyDataSetItemManipulator<>));
services.TryAddScoped(typeof(ICreateItemHandle<,>), typeof(NullCreateItemHandler<,>));
}
```

These services are registered by default in IoC if you want for your grid use funtionality like lazy loading data, inline editing or create item form
you have to provide these services for your models.

In your Blazor component add Tag helper and required usings
```cs
@addTagHelper *, Blazor.FlexGrid
```

# Example
```cs
@addTagHelper *, Blazor.FlexGrid
@using Blazor.FlexGrid.Demo.Shared
@using Blazor.FlexGrid.DataAdapters
@inject HttpClient Http
@page "/grid"

Weather forecast

@functions{
CollectionTableDataAdapter dataAdapter;

protected override async Task OnInitAsync()
{
var forecast = await Http.GetJsonAsync("/api/SampleData/WeatherForecastsSimple");
dataAdapter = new CollectionTableDataAdapter(forecast);
}
}
```

Result
table

# Configuration
You do not need to define information about columns and components will render columns by **properties** of object type which is associated
with **table data adapter** which provide *data set* for **Table** component. Value for column is provided by **ToString** method on the property type.
Or you can configure some behavior for tables and columns by using fluent api which is supported in classes where interface **IEntityTypeConfiguration** is implemented.
```cs
public class WeatherForecastGridConfiguration : IEntityTypeConfiguration
{
public void Configure(EntityTypeBuilder builder)
{
builder.Property(e => e.Date)
.HasCaption("Date")
.HasValueFormatter(d => d.ToShortDateString());

builder.Property(e => e.Summary)
.HasCaption("MySummary")
.HasOrder(1)
.HasValueFormatter(s => $"{s}!");

builder.Property(e => e.TemperatureC)
.IsSortable();

builder.Property(e => e.TemperatureF)
.IsSortable();
}
}
```
And provide this configuration
```cs
var serviceProvider = new BrowserServiceProvider(services =>
{
services.AddFlexGrid(cfg =>
{
cfg.ApplyConfiguration(new WeatherForecastGridConfiguration());
});
});
```

# Data Adapters
You can use simple **CollectionTableDataAdapter** which requires collection of items.
```cs
@functions{
CollectionTableDataAdapter dataAdapter;

protected override async Task OnInitAsync()
{
var forecast = await Http.GetJsonAsync("/api/SampleData/WeatherForecastsSimple");
dataAdapter = new CollectionTableDataAdapter(forecast);
}
}
```

Another data adapter type is **LazyLoadedTableDataAdapter** which support lazy loading data from API. This type of adapter
is registered in dependency injection conatiner and you must only provide **LazyLoadingOptins** to table component.
```cs
@addTagHelper *, Blazor.FlexGrid
@using Blazor.FlexGrid.Demo.Shared
@using Blazor.FlexGrid.DataAdapters
@using Blazor.FlexGrid.DataSet.Options
@page "/lazyloadedgrid"
@inject HttpClient Http
@inject LazyLoadedTableDataAdapter forecastAdapter

```
Also you must provide the server side part

```cs
public IActionResult WeatherForecasts(int pageNumber, int pageSize, SortingParams sortingParams)
{
var rng = new Random();

var items = Enumerable.Range(1, 100).Skip(pageSize * pageNumber).Take(pageSize).Select(index =>
new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]

});

items = string.IsNullOrEmpty(sortingParams.SortExpression)
? items
: items.AsQueryable().OrderBy(sortingParams.SortExpression).ToList();

return Ok(
new
{
Items = items,
TotalCount = 100
});
}
```
After that you have fully pageable and sortable table with lazy loaded data after you select new page

# Permission restriction
You can configure permission restricion of showing/editing values for each column in table for current logged user. You only have to do 3 things.
First create class which implements interface **ICurrentUserPermission**. Second provide configuration for permission restriction for exmaple:

```cs
public void Configure(EntityTypeBuilder builder)
{
builder.Property(c => c.Email)
.HasReadPermissionRestriction(perm => perm.IsInRole("Read"))
.HasWritePermissionRestriction(perm => perm.IsInRole("Write"));
}
```

And last thing is register **ICurrentUserPermission** into DI container as **Singleton**.

# Blazor components in column
You can configure column value for rendering **Blazor** component this way:
First add using **Blazor.FlexGrid.Components.Configuration**.
Second inject required service **BlazorComponentColumnCollection** into HTML of component where you use **FlexGrid**
And the last thing you have to provide **RenderFragment** for columns

```cs
@inject BlazorComponentColumnCollection Collection

@{
RenderFragment weatherTemp = (weather) => @@weather.Summary;
Collection.AddColumnValueRenderFunction(w => w.Summary, weatherTemp);
}
```

Also you can use registration of fragment in startup configuration
```cs
RenderFragment customerEmailComponent = (Customer customer) => delegate (RenderTreeBuilder rendererTreeBuilder)
{
var internalBuilder = new BlazorRendererTreeBuilder(rendererTreeBuilder);
internalBuilder
.OpenElement(HtmlTagNames.H4)
.AddContent(customer.Email)
.CloseElement();
};

builder.Property(c => c.Email)
.HasBlazorComponentValueRender(customerEmailComponent);
```

# Filtering
You can define which column can be filtered.

```cs
builder.Property(e => e.TemperatureF)
.IsFilterable();
```

For filter exists two modes, for standard **TableDataSet** is filtering done on client side across whole collection of items.
For **LazyTableDataSet** is filter object sended to the server. More info how it is works is [here](https://github.com/Mewriick/Blazor.FlexGrid/wiki/LazyTableDataSet---Client-Side-Blazor#filtering)

# Grouping
You can enable dynamic grouping functionality for table
```cs
builder.EnableGrouping(options =>
{
options.GroupPageSize = 15;
});
```

You can define page size for grouped items. Grouping works in two modes, for standard **TableDataSet**, grouping is done on client side across whole collection of items.
For **LazyTableDataSet**, **GroupExpression** is sent to the server. More info how it is works is [here](https://github.com/Mewriick/Blazor.FlexGrid/wiki/LazyTableDataSet---Client-Side-Blazor#grouping)

# Create item form
You can very easily have **Create item form** for your models. You can even configure different type of model for create item form
than is used for rendering in **FlexGrid**. You can also specify the return type from your Api which can be different than input.
Now the restriction for model is that have **default constructor**
Configuration example:

```cs
builder.AllowCreateItem(conf =>
{
conf.CreatePermissionRestriction = p => p.IsInRole("TestRole");
conf.CreateUri = "/api/SampleData/WeatherForecast";
});
```

You can also restrict the creation of items only for some users. Important property is **CreateUri** which must be filled. Form also includes
validations which are run after every change. Submit can be done only if form is in **Valid** state. For rendering, two layout types are used.
You can have one column layout or you can have two columns layout. Now the behavior is that every model that have more than 6 properties the
two column layout is used. You can also write your own layout. You only have to inherit from **BaseCreateItemFormLayout** and also you must
create your own layout provider by creating a class which implements **IFormLayoutProvider** and register this provider to IoC.
Properties are in default layouts rendered in order that they are written in class.
After item is succesfully created the **NewItemCreated** event is fired.
You can also change the modal dialog css classes by using:

```
builder.AppendCssClasses(conf =>
{
conf.CreateFormCssClasses.ModalSize = "modal-lg";
});
```

# Master / Detail
You can have multiple **DataSets** related together and rendered only with one **FlexGrid** component. If you have object that have property which is a collection,
the **FlexGrid** component automatically finds this and will render this kind of object as Master / Detail grid.

Example configuration
```cs
public void Configure(EntityTypeBuilder builder)
{
builder.IsMasterTable();
builder.HasDetailRelationship(c => c.Id, o => o.CustomerId)
.HasLazyLoadingUrl("/api/Order/Orders")
.HasUpdateUrl("/api/Order/Update")
.HasCaption("Orders")
.HasPageSize(10);

builder.HasDetailRelationship(c => c.Id, o => o.CustomerId)
.HasCaption("Customer addresses");
}

public void Configure(EntityTypeBuilder builder)
{
builder.IsMasterTable();
builder.HasDetailRelationship(o => o.OrderItems)
.HasCaption("Order products");
}
```

For correct working of Master / Detail grid you must configure relationships between objects. Also you can define some additional options for related grid component.
If you want to use **LazyTableDataSet** you must provide url address for loading. This is not required in ServerSide solution because you have to create your own DataSet
which implements **ILazyDataSetLoader**.

Example page with Master/Detail grid

```cs
@addTagHelper *, Blazor.FlexGrid
@using Blazor.FlexGrid.Demo.Shared
@using Blazor.FlexGrid.DataAdapters
@inject HttpClient Http
@inject MasterTableDataAdapterBuilder MasterAdapterBuilder
@inject LazyLoadedTableDataAdapter ordersAdapter
@page "/masterdetailgrid"

Customers

@functions{
CollectionTableDataAdapter customerDataAdapter;
CollectionTableDataAdapter customerAddressesDataAdapter;
MasterTableDataAdapter customersMasterDataAdapter;

protected override async Task OnInitAsync()
{
var customers = await Http.GetJsonAsync("/api/Customer/Customers");
var customersAddresses = await Http.GetJsonAsync("/api/Customer/CustomersAddresses");
customerDataAdapter = new CollectionTableDataAdapter(customers);
customerAddressesDataAdapter = new CollectionTableDataAdapter(customersAddresses);

customersMasterDataAdapter = MasterAdapterBuilder
.MasterTableDataAdapter(customerDataAdapter)
.WithDetailTableDataAdapter(ordersAdapter)
.WithDetailTableDataAdapter(customerAddressesDataAdapter)
.Build();
}
}
```
For building Master / Detail **TableDataSet** is used **MasterTableDataAdapter** which is registered in DI container.
Master / Detail usage you can find in ServerSide Blazor demo project

# Inline editing
You can use inline editing features by configuring grid
```cs
public void Configure(EntityTypeBuilder builder)
{
builder.AllowInlineEdit();
// Or
builder.AllowInlineEdit(conf =>
{
conf.AllowDeleting = true;
conf.DeletePermissionRestriction = perm => perm.IsInRole("TestRole");
});

builder.DoNotUseDeleteConfirmDialog(); // Disable confirmation dialog before delete operation
}
```
You can also configure which columns will be editable for current logged in user, see **Permission restriction** section. If you are using
**CollectionTableDataAdapter** changes are saved only into local object in list. For saving to the server you have to write your own functionality.
If you are using **LazyLoadedTableDataAdapter** and Client/Server mode you must provide a url for updating item.

```cs

```
The Http request will be sent to the server. For fully working delete feature you have to properly set **DeleteUri** of **LazyLoadingOptions**.
The url contains a template where you must specify the name of object property in { } at the end (for example {Id}) which is the key and this key is sent to the action method on server side.

Or if the Grid is used as detail

```cs
builder.HasDetailRelationship(c => c.Id, o => o.CustomerId)
.HasLazyLoadingUrl("/api/Order/Orders")
.HasUpdateUrl("/api/Order/UpdateOrder")
.HasCaption("Orders")
.HasPageSize(10);
```

# Events
You can subscribe to some events which **FlexGrid** provides. The only requirements are add using
**@using Blazor.FlexGrid.Components.Events** and register **EventHandler** in HTML of Grid component.

**Supported events:**
``SaveOperationFinished``
``DeleteOperationFinished``
``NewItemCreated``
``OnItemClicked``

# Design
You can override some default CssClasses by your own CssClasses by using fluent api configuration.
```cs
public void Configure(EntityTypeBuilder builder)
{
builder.AppendCssClasses(conf =>
{
conf.Table = "my-table";
conf.TableBody = "my-table-body";
conf.TableCell = "my-table-cell";
conf.TableHeader = "my-table-header";
conf.TableHeaderCell = "my-table-header-cell";
conf.TableHeaderRow = "my-table-header-row";
conf.TableRow = "my-table-row";
});
}
```

# Contributions and feedback
Please feel free to use the component, open issues, fix bugs or provide feedback.

## RoadMap
``Create proper Docs``
``Add UnitTests``
``More fluent API configuration``
``Filtration support``