Creating Custom Blazor Component Pages in Bloqs
You can create custom pages or components for your Bloqs applications inside the UserInterface Components project. These pages are full Blazor components, meaning you can define routes, inject services, query data, and execute commands just like in any Blazor application.
1. Page Routing
Define a route at the top of your .razor page using the @page directive. Use the following format:
@page "/<your-app-name>/<your-page-name>"For example, a charts page for sample-app would be:
@page "/sample-app/charts"2. Injecting Services
You can inject standard Bloqs services as well as your own custom services:
IQueryDispatcher – for querying data
ICommandDispatcher – for executing commands such as create, save, delete, or operations
IRunContext – for retrieving tenant and runtime context information
Example injection in your page:
@inject IQueryDispatcher queryDispatcher
@inject ICommandDispatcher commandDispatcher
@inject IRunContext runContext
@inject MyCustomService myService3. Querying Data
Use IQueryDispatcher to query entities from your app. Here’s an example for querying Activity entities for the current tenant:
private async Task<List<Activity>> GetActivitiesAsync()
{
var tenantName = runContext.GetActiveTenantName();
// Specify advanced query filter and sorting
var multiQuery = new MultiQuery()
{
ContextEntities = [],
Filter = FilterBuilder.And(
FilterBuilder.Field("Closed").Eq(false),
FilterBuilder.Field("Prio").Gt(0)
),
Sort = SortBuilder.By(SortBuilder.Asc("Name")),
};
var queryResult = await queryDispatcher.QueryAsync<EntityQuery, MultiQueryResult>(
new EntityQuery()
{
AppName = AppConstants.App.Name,
TenantName = tenantName,
EntityClassName = nameof(Activity),
MultiQuery = multiQuery,
}
);
return [.. queryResult.Entities.OfType<Activity>()];
}4. Executing Commands
Use ICommandDispatcher to create, save, delete, or perform other operations on entities:
var tenantName = runContext.GetActiveTenantName();
var newActivity = await commandDispatcher.SendAsync<NewEntityCommand, NewEntityCommandResult>(new NewEntityCommand()
{
AppName = AppConstants.App.Name,
TenantName = tenantName,
EntityClassName = nameof(Activity),
});5. Example: Charts Page
Here is a full example page showing charts using MudBlazor:
@page "/sample-app/charts"
@using Bloqs.App.Engine.Queries
@using Bloqs.UserInterface.Browser
@using MudBlazor
@inject IQueryDispatcher queryDispatcher
@inject IRunContext runContext
<ContentPage>
<h1>Charts Page</h1>
<p>
Number of activities: @_activityCount
</p>
<br />
<MudAlert Severity="Severity.Success">The only way to do great work is to love what you do.</MudAlert>
<br />
<MudGrid>
<MudItem xs="12" sm="6">
<MudPaper Class="pa-4">
<MudChart ChartType="ChartType.Bar" ChartSeries="@_series" @bind-SelectedIndex="_index" XAxisLabels="@_months" Width="@_width" Height="@_height" AxisChartOptions="_axisChartOptions"></MudChart>
</MudPaper>
</MudItem>
<MudItem xs="12" sm="6">
<MudPaper Class="pa-4">
<MudChart ChartType="ChartType.Donut" Width="@_width" Height="@_height" @bind-SelectedIndex="_index" InputData="@_data" InputLabels="@_labels"></MudChart>
</MudPaper>
</MudItem>
<MudItem xs="12" sm="6">
<MudPaper Class="pa-4">
<MudChart ChartType="ChartType.Line" ChartSeries="@_series" @bind-SelectedIndex="_index" XAxisLabels="@_months" Width="@_width" Height="@_height" ChartOptions="@_options" AxisChartOptions="_axisChartOptions" />
</MudPaper>
</MudItem>
<MudItem xs="12" sm="6">
<MudPaper Class="pa-4">
<MudChart ChartType="ChartType.StackedBar" ChartSeries="@_series" @bind-SelectedIndex="_index" LegendPosition="@_legendPosition" XAxisLabels="@_months" Width="@_width" Height="@_height" AxisChartOptions="_axisChartOptions"></MudChart>
</MudPaper>
</MudItem>
</MudGrid>
</ContentPage>using Bloqs.App.Engine.Queries.Query;
using Bloqs.App.Engine.Queries.Query.Filtering;
using Bloqs.App.Engine.Queries.Query.Sorting;
using MudBlazor;
using Templates.SampleApp.Models;
using Templates.SampleApp.Models.Data;
namespace Templates.SampleApp.UserInterface.Components;
public partial class ChartsPage
{
private int _index = -1;
private int _activityCount = 0;
private readonly string _width = "100%";
private readonly string _height = "350px";
private readonly ChartOptions _options = new();
private readonly AxisChartOptions _axisChartOptions = new();
private List<ChartSeries> _series = [];
private readonly Position _legendPosition = Position.Bottom;
private readonly string[] _months =
[
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
];
public double[] _data = [];
public string[] _labels = [];
protected override async Task OnInitializedAsync()
{
var activities = await GetActivitiesAsync();
_activityCount = activities.Count;
_labels = [.. activities.Select(a => a.Name)];
_series = GetSeries(activities);
_data = GetPercentageData(activities);
}
private static List<ChartSeries> GetSeries(List<Activity> activities)
{
var result = new List<ChartSeries>();
if (activities == null || activities.Count == 0)
{
return result;
}
var random = new Random();
const int numberOfDataPoints = 12; // 12 months
foreach (var activity in activities)
{
var seriesName = activity.Name;
// Generate randomized data for the series
var seriesData = new double[numberOfDataPoints];
for (var j = 0; j < numberOfDataPoints; j++)
{
seriesData[j] = Math.Round(random.NextDouble() * 90.0 + 10.0, 0);
}
result.Add(new ChartSeries { Name = seriesName, Data = seriesData });
}
return result;
}
private static double[] GetPercentageData(List<Activity> activities)
{
if (activities == null || activities.Count == 0)
{
return [];
}
var random = new Random();
var rawValues = new double[activities.Count];
var totalRawValue = 0d;
for (var i = 0; i < activities.Count; i++)
{
// Generate a random value between 1.0 and 100.0 (or any suitable positive range)
rawValues[i] = random.NextDouble() * 99.0 + 1.0;
totalRawValue += rawValues[i];
}
// Now, normalize these values so they sum up to 100%
var percentageData = new double[activities.Count];
for (var i = 0; i < activities.Count; i++)
{
percentageData[i] = Math.Round((rawValues[i] / totalRawValue) * 100.0, 1);
}
return percentageData;
}
private async Task<List<Activity>> GetActivitiesAsync()
{
var tenantName = runContext.GetActiveTenantName();
// Specify advanced query filter and sorting
var multiQuery = new MultiQuery()
{
ContextEntities = [],
Filter = FilterBuilder.And(
FilterBuilder.Field("Closed").Eq(false),
FilterBuilder.Field("Prio").Gt(0)
),
Sort = SortBuilder.By(SortBuilder.Asc("Name")),
};
var queryResult = await queryDispatcher.QueryAsync<EntityQuery, MultiQueryResult>(
new EntityQuery()
{
AppName = AppConstants.App.Name,
TenantName = tenantName,
EntityClassName = nameof(Activity),
MultiQuery = multiQuery,
}
);
return [.. queryResult.Entities.OfType<Activity>()];
}
}
Key Points
Routes: Always use
/app-name/page-nameformat.Data Access: Use
IQueryDispatcherandICommandDispatcher.Tenant Awareness: Use
IRunContextto retrieve the current tenant.Custom Services: You can inject and use any service your page requires.
Blazor Features: You can use standard Blazor features like
@code, data binding, lifecycle methods, and component libraries (e.g., MudBlazor).
Last updated