PROWAREtech
Blazor: DataRepeater Component Example - Simple
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 DataRepeaterComponent
. It should be run as client-side (WebAssembly) Blazor code.
See DataGridComponent
(link) for a simplier example that does not allow for so much customization, or see an ADVANCED version of this repeater which allows for editing data.
As can be seen here, the code for the repeater itself is very simple because most of it is done in the page.
@typeparam TItem
@if (Items != null)
{
@foreach (var item in Items)
{
@Row(item)
}
}
@code {
[Parameter]
public RenderFragment<TItem> Row { get; set; }
[Parameter]
public List<TItem> Items { get; set; }
}
Here is the fragment of the razor page that uses the DataRepeaterComponent. A value for the Items
parameter must be supplied and the Context
must be set and then used in the HTML fragment to be rendered.
<div class="table-responsive">
<table class="table table-hover table-striped">
<thead>
<tr><th>name</th><th>address</th><th colspan="2">zip</th></tr>
</thead>
<tbody>
<DataRepeaterComponent Items="custs">
<Row Context="cust">
<tr>
<td>@cust.name</td>
<td>@cust.address</td>
<td>@cust.zip</td>
<td><button class="btn btn-sm btn-danger" @onclick="@(()=> Delete(cust.id))">delete</button></td>
</tr>
</Row>
</DataRepeaterComponent>
</tbody>
</table>
</div>
Here is the whole razor page. Most of the client-side code lies in this page.
@page "/datarepeater"
@using BlazorExample.Shared
@inject HttpClient Http
@using BlazorExample.Client.Components
<h1>Customers</h1>
@if (custs == null)
{
<p><em>Loading...</em></p>
}
<div class="table-responsive">
<table class="table table-hover table-striped">
<thead>
<tr><th>name</th><th>address</th><th colspan="2">zip</th></tr>
</thead>
<tbody>
<DataRepeaterComponent Items="custs">
<Row Context="cust">
<tr>
<td>@cust.name</td>
<td>@cust.address</td>
<td>@cust.zip</td>
<td><button class="btn btn-sm btn-danger" @onclick="@(()=> Delete(cust.id))">delete</button></td>
</tr>
</Row>
</DataRepeaterComponent>
</tbody>
</table>
</div>
<form class="mt-5" onsubmit="return false;">
<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">Zipcode</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();
}
}
}
private async Task Delete(string id)
{
using(var msg = await Http.DeleteAsync($"/api/customers/{id}", System.Threading.CancellationToken.None))
{
if (msg.IsSuccessStatusCode)
{
int i;
for (i = 0; i < custs.Count && custs[i].id != id; i++) ;
custs.RemoveAt(i);
//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; }
}
}
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; }
});
}
}
}