PROWAREtech

articles » current » blazor » wasm » datagrid

Blazor: DataGrid Component Example

Example usage of a DataGrid component for displaying data in Blazor WebAssembly (WASM).

This example uses .NET Core 3.1 but is compatible with .NET 5, .NET 6 and .NET 8.

As an example, here is a custom DataGridComponent. It allows for its data to be sorted by clicking on the column heading. It should be run as client-side (WebAssembly) Blazor code.

See DataRepeaterComponent (link) for an example that allows for more customization to allow for deleting data, for example.

@typeparam TItem
@using System.Reflection

@if (Items != null)
{
<div class="@ParentClassName">
	@if (info != null && info.Length > 0)
	{
		<table class="@TableClassName">
			<thead>
				<tr>
					@foreach (var member in info)
					{
						if (member.MemberType == MemberTypes.Property && !hideColumns.Contains(member.Name))
						{
							<th><a href="javascript:;" @onclick="@(()=>Sort(member.Name))">@member.Name</a></th>
						}
					}
				</tr>
			</thead>
			<tbody>
				@for (int index = 0; index < Items.Count; index++)
				{
					var item = Items[index];
					var key = GetDataKey(item);
					<tr data-index="@index">
						@foreach (var member in info)
						{
							if (member.MemberType == MemberTypes.Property && !hideColumns.Contains(member.Name))
							{
								<td data-key="@key">@item.GetType().GetProperty(member.Name).GetValue(item)</td>
							}
						}
					</tr>
				}
			</tbody>
		</table>
	}
</div>
}

@code {

	[Parameter]
	public List<TItem> Items { get; set; }

	[Parameter]
	public string ParentClassName { get; set; }

	[Parameter]
	public string TableClassName { get; set; }

	private string[] hideColumns = { };
	[Parameter]
	public string HideColumns
	{
		get
		{
			return string.Join(',', hideColumns);
		}

		set
		{
			hideColumns = value.Split(',');
		}
	}

	[Parameter]
	public string DataKeyColumn { get; set; }

	private MemberInfo[] info;

	private string GetDataKey(TItem item)
	{
		if (!string.IsNullOrEmpty(DataKeyColumn))
		{
			foreach (var member in info)
			{
				if (member.MemberType == MemberTypes.Property && member.Name == DataKeyColumn)
					return item.GetType().GetProperty(member.Name).GetValue(item).ToString();
			}
		}
		return string.Empty;
	}

	protected override void OnParametersSet()
	{
		base.OnParametersSet();
		if (Items != null && Items.Count > 0)
		{
			Type type = Items[0].GetType();
			info = type.GetMembers();
		}
	}
	private string sortedField;
	private bool descending = true;
	private void Sort(string field)
	{
		if (sortedField == field)
			descending = !descending;
		sortedField = field;
		if (descending)
		{
			Items.Sort((a, b) =>
			{
				var obj = typeof(TItem).GetProperty(field).GetValue(a);
				if (obj is DateTime)
				{
					DateTime dta = (DateTime)typeof(TItem).GetProperty(field).GetValue(a);
					DateTime dtb = (DateTime)typeof(TItem).GetProperty(field).GetValue(b);
					return dta.ToString("yyyyMMddHHmmssfff").CompareTo(dtb.ToString("yyyyMMddHHmmssfff"));
				}
				else if (obj is byte || obj is sbyte || obj is short || obj is ushort || obj is int)
				{
					int ia = (int)typeof(TItem).GetProperty(field).GetValue(a);
					int ib = (int)typeof(TItem).GetProperty(field).GetValue(b);
					return ia - ib;
				}
				else
				{
					var ta = typeof(TItem).GetProperty(field).GetValue(a);
					var tb = typeof(TItem).GetProperty(field).GetValue(b);
					return ta.ToString().CompareTo(tb.ToString());
				}
			});
		}
		else
		{
			Items.Sort((b, a) =>
			{
				var obj = typeof(TItem).GetProperty(field).GetValue(a);
				if (obj is DateTime)
				{
					DateTime dta = (DateTime)typeof(TItem).GetProperty(field).GetValue(a);
					DateTime dtb = (DateTime)typeof(TItem).GetProperty(field).GetValue(b);
					return dta.ToString("yyyyMMddHHmmssfff").CompareTo(dtb.ToString("yyyyMMddHHmmssfff"));
				}
				else if (obj is byte || obj is sbyte || obj is short || obj is ushort || obj is int)
				{
					int ia = (int)typeof(TItem).GetProperty(field).GetValue(a);
					int ib = (int)typeof(TItem).GetProperty(field).GetValue(b);
					return ia - ib;
				}
				else
				{
					var ta = typeof(TItem).GetProperty(field).GetValue(a);
					var tb = typeof(TItem).GetProperty(field).GetValue(b);
					return ta.ToString().CompareTo(tb.ToString());
				}
			});
		}
	}
}

Here is a razor page that uses the DataGridComponent. Contrary to the page for the DataRepeaterComponent (link), the code is very simple.

@page "/datagrid"
@using BlazorExample.Shared
@inject HttpClient Http
@using BlazorExample.Client.Components

<h1>Customers</h1>

@if (custs == null)
{
	<p><em>Loading...</em></p>
}
<DataGridComponent ParentClassName="table-responsive" TableClassName="table table-hover table-striped" HideColumns="id" DataKeyColumn="id" Items="custs"></DataGridComponent>

<form class="mt-5" onsubmit="return false;">
	<div class="form-group form-check">
		<input type="checkbox" class="form-check-input" id="vipCheck" @bind-value="customer.vip">
		<label class="form-check-label" for="vipCheck">VIP?</label>
	</div>
	<div class="input-group input-group-md mb-2">
		<span class="input-group-text">Name</span>
		<input type="text" class="form-control" autocomplete="off" required @bind-value="customer.name" />
	</div>
	<div class="input-group input-group-md mb-2">
		<span class="input-group-text">Address</span>
		<input type="text" class="form-control" autocomplete="off" required @bind-value="customer.address" />
	</div>
	<div class="input-group input-group-md mb-2">
		<span class="input-group-text">Zip</span>
		<input type="text" class="form-control" autocomplete="off" required @bind-value="customer.zip" />
		<button class="btn btn-success" @onclick="Add">Add</button>
	</div>
</form>

@code {

	private List<Customer> custs;
	private Customer customer = new Customer();

	protected override async Task OnInitializedAsync()
	{
		await base.OnInitializedAsync();

		custs = await Http.GetFromJsonAsync<List<Customer>>("/api/customers");
	}

	private async Task Add()
	{
		using(var msg = await Http.PostAsJsonAsync<Customer>("/api/customers", customer, System.Threading.CancellationToken.None))
		{
			if (msg.IsSuccessStatusCode)
			{
				custs.Add(await msg.Content.ReadFromJsonAsync<Customer>());
				//StateHasChanged();
			}
		}
	}
}

Here is the Customer definition.

using System;

namespace BlazorExample.Shared
{
	[Serializable]
	public class Customer
	{
		public string id { get; set; }
		public string name { get; set; }
		public string address { get; set; }
		public string zip { get; set; }
		public bool vip { get; set; }
	}
}

Here is the RESTful API implementation used by the above page that does not rely upon a database.

 // CustomersController.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
using BlazorExample.Shared;

namespace BlazorExample.Server.Controllers
{
	[ApiController]
	[Route("api/[controller]")]
	public class CustomersController : ControllerBase
	{
		private string DataFolder { get; }
		private IWebHostEnvironment env { get; }
		public CustomersController(IWebHostEnvironment env)
		{
			this.env = env;
			DataFolder = env.ContentRootPath + Path.DirectorySeparatorChar + "CustomersData";
			try
			{
				if (!System.IO.Directory.Exists(DataFolder))
					System.IO.Directory.CreateDirectory(DataFolder);
			}
			catch { }
		}

		[HttpPut("{id}")]
		public async Task<Customer> Put(string id, [FromBody] Customer cust)
		{
			return await Task.Run(() =>
			{
				try
				{
					cust.id = id;
					System.IO.File.WriteAllText(DataFolder + Path.DirectorySeparatorChar + id + ".json", JsonSerializer.Serialize(cust));
					return cust;
				}
				catch { return null; }
			});
		}

		[HttpPatch("{id}")]
		public async Task<Customer> Patch(string id, [FromBody] Customer update)
		{
			return await Task.Run(() =>
			{
				try
				{
					var cust = JsonSerializer.Deserialize<Customer>(System.IO.File.ReadAllText(DataFolder + Path.DirectorySeparatorChar + id + ".json"));
					cust.name = (update.name == null) ? cust.name : update.name;
					cust.address = (update.address == null) ? cust.address : update.address;
					cust.zip = (update.zip == null) ? cust.zip : update.zip;
					System.IO.File.WriteAllText(DataFolder + Path.DirectorySeparatorChar + id + ".json", JsonSerializer.Serialize(cust));
					return cust;
				}
				catch { return null; }
			});
		}

		[HttpDelete("{id}")]
		public async Task<Customer> Delete(string id)
		{
			return await Task.Run(() =>
			{
				var di = new DirectoryInfo(DataFolder);
				try
				{
					string file = DataFolder + Path.DirectorySeparatorChar + id + ".json";
					var cust = JsonSerializer.Deserialize<Customer>(System.IO.File.ReadAllText(file));
					System.IO.File.Delete(file);
					return cust;
				}
				catch { return null; }
			});
		}

		[HttpPost]
		public async Task<Customer> Post([FromBody] Customer cust)
		{
			return await Task.Run(() =>
			{
				var di = new DirectoryInfo(DataFolder);
				string id = Guid.NewGuid().ToString("N");
				cust.id = id;
				if (cust.name == null)
					cust.name = string.Empty;
				else
					cust.name = cust.name.Substring(0, cust.name.Length > 100 ? 100 : cust.name.Length);
				if (cust.address == null)
					cust.address = string.Empty;
				else
					cust.address = cust.address.Substring(0, cust.address.Length > 100 ? 100 : cust.address.Length);
				if (cust.zip == null)
					cust.zip = string.Empty;
				else
					cust.zip = cust.zip.Substring(0, cust.zip.Length > 10 ? 10 : cust.zip.Length);
				try
				{
					System.IO.File.WriteAllText(DataFolder + Path.DirectorySeparatorChar + id + ".json", JsonSerializer.Serialize(cust));
					return cust;
				}
				catch { return null; }
			});
		}

		[HttpGet]
		public async Task<IEnumerable<Customer>> Get()
		{
			return await Task.Run(() =>
			{
				var di = new DirectoryInfo(DataFolder);
				FileInfo[] fi = di.GetFiles("*", SearchOption.TopDirectoryOnly);
				var q = from f in fi orderby f.CreationTime descending select f;
				var custs = new List<Customer>();
				for (int i = 0; i < fi.Length; i++)
					custs.Add(JsonSerializer.Deserialize<Customer>(System.IO.File.ReadAllText(fi[i].FullName)));
				return custs;
			});
		}

		[HttpGet("{id}")]
		public async Task<Customer> Get(string id)
		{
			return await Task.Run(() =>
			{
				var di = new DirectoryInfo(DataFolder);
				try
				{
					return JsonSerializer.Deserialize<Customer>(System.IO.File.ReadAllText(DataFolder + Path.DirectorySeparatorChar + id + ".json"));
				}
				catch { return null; }
			});
		}
	}
}

Coding Video

https://youtu.be/3o2Mavt3woM


This site uses cookies. Cookies are simple text files stored on the user's computer. They are used for adding features and security to this site. Read the privacy policy.
CLOSE