Skip to main content

Blazor View Components

Xomega Framework provides a number of components that are dependent on or used by the framework's classes, which help you build your Blazor views and applications. Below you can find a list of such Blazor components.

Xomega Framework provides a Bootstrap-styled, security-enabled NavMenu component with hierarchical grouping to let you navigate to top-level views. Typically, you would use it within a layout component, where you can add the NavMenu menu component to your sidebar or top-level header for a dropdown menu, as described below.

For using NavMenu in a sidebar, you need to set its Items parameter to the list of your top-level menu items.

MainLayout.razor
@inject MainMenu menu
<div class="sidebar-pane sidebar collapse bg-dark navbar-dark">
<NavMenu Class="navbar-nav" Items="menu.Items" />
</div>

The menu will display as a hierarchical collapsible tree view, as shown below.

Nav menu

note

The NavMenu component is used recursively for any nested groupings within the navigation menu.

The navigation menu structure is defined using a list of hierarchical MenuItem objects, as illustrated below.

using Xomega.Framework.Blazor.Components;

public class MainMenu
{
public List<MenuItem> Items =>
[
new MenuItem()
{
ResourceKey = "HomeView_NavMenu",
IconClass = "bi bi-house-door",
Href = "/"
}
new MenuItem()
{
ResourceKey = "Module_Sales_NavMenu",
IconClass = "bi bi-columns",
Items = new List<MenuItem>()
{
new MenuItem()
{
Text = "Customer List",
IconClass = "bi bi-person-lines-fill",
Href = "CustomerListView"
},
...
}
}, ...
];
}

For any MenuItem that opens a specific top-level view, you need to set the Href property to the relative path of that view's page. NavMenu will highlight the menu item for the currently open view. Alternatively, you can provide a list of child menu items in the Items property, and the current menu item will serve as a grouping item.

note

If you set both Href and Items properties for a grouping item, that item will be displayed as a split-menu. Clicking on the grouping item's text will open up the associated view, while clicking on the toggle next to it will expand/collapse the child items.

To specify the localized text of the menu item, you can set the ResourceKey property for the corresponding resource. You can also set the Text property directly if you don't need localization. If you want to display an additional icon before the text, you can also set the IconClass property for that item.

The NavMenu component allows you to set up security for each menu item and will hide the menus that the user is not authorized to access. If the user doesn't have access to any items in a specific sub-menu, such as Admin, the NavMenu will also hide the corresponding grouping item to avoid showing an empty group.

To configure security for any individual menu item, you can either set its Policy property or assign an array of Roles that the current user should have to be able to access it. If the user's authorization changes in the app, such as after login/logout, the NavMenu will be automatically updated to reflect the new permissions.

tip

To remove security from a menu item, you need to set both the Policy and Roles properties to null. You can also set the Policy to an empty string to restrict it only to authorized users, regardless of their privileges.

For example, let's say that you define a Sales policy in your app configuration as follows.

services.AddAuthorization(o => {
o.AddPolicy("Sales", policy => policy.RequireAssertion(ctx =>
ctx.User.IsEmployee() || ctx.User.IsIndividualCustomer() || ctx.User.IsStoreContact()));
});

To apply it to your menu items, you can either assign it directly on each item using the Policy property or call a recursive function ForEachItem on each top-level item, where you can configure your security based on the Href value, as illustrated below.

foreach (var mmi in menu.Items)
mmi.ForEachItem(mi =>
{
if (mi?.Href == null) return;
if (mi.Href.StartsWith("Sales") || mi.Href.StartsWith("Customer"))
mi.Policy = "Sales";
else mi.Policy = ""; // visible for all authorized users
});
danger

Securing the navigation menu is not enough for client-side security since the user can manually enter the URL for any particular page or pull it up using a bookmark. You also need to secure specific pages using the same policy or roles in the page's Authorize attribute, as follows.

@attribute [Authorize(Policy = "Sales")]

ViewTitle

The ViewTitle component allows you to display the current value of your view model's title as part of your view header. In addition to static text, the title of your view may include dynamic information, such as the key value(s), and a trailing asterisk indicating that the view has been modified, as illustrated below.

View title

To ensure that the title gets updated automatically by the base view, you need to assign your ViewTitle to the inherited TitleComponent property and set the Title parameter to the view model's ViewTitle property as follows.

<div class="modal-header">
<h5 class="modal-title">
<ViewTitle @ref="TitleComponent" Title="@Model?.ViewTitle"></ViewTitle>
</h5>
<XActionButton Class="btn-close" NoText="true" OnClick="OnCloseAsync"
Action="@VM?.CloseAction"></XActionButton>
</div>
tip

The ViewTitle component doesn't offer any customization, but you can wrap it in other elements to style it, such as the h5 element in the above example. You can also include it with other data elements in the header, such as the title of the parent view. Finally, you can develop your own component inherited from the ViewTitle, e.g., a breadcrumb, and use it instead of this one.

TabSet

The TabSet component allows you to group some elements of your view into Bootstrap-styled tabs, as shown below.

TabSet component

For each tab in your TabSet you need to add a TabPage child component, set its Title parameter, and nest the tab's content within that TabPage, as follows.

<TabSet>
<TabPage Title="@VM?.MainObj?.GetChildTitle(SalesOrderObject.Customer)">
<div class="row pt-3">
<fieldset class="@GetPanelCol(2, 2)">
<legend>@VM?.MainObj?.CustomerObject?.GetTitle()</legend>
<div class="row @GetRowCol(2)">
<XDataLabel Class="mb-3" Property="@VM?.MainObj?.CustomerObject?.StoreNameProperty"></XDataLabel>
...
</div>
</fieldset>
...
</div>
</TabPage>
<TabPage Title="@VM?.MainObj?.GetChildTitle(SalesOrderObject.Detail)">[...]
<TabPage Title="@VM?.MainObj?.GetChildTitle(SalesOrderObject.Payment)">[...]
<TabPage Title="@VM?.MainObj?.GetChildTitle(SalesOrderObject.Sales)">[...]
</TabSet>

Localizing the tabs' texts

For localized titles of the tabs, data objects provide a utility method GetChildTitle that reads the localized title using a resource key formatted as {object class}-{child name}_Title. The resources for the titles in the above example would look as follows.

NameValueComment
SalesOrderObject-Customer_TitleCustomer
SalesOrderObject-Detail_TitleDetail
SalesOrderObject-Payment_TitlePayment
SalesOrderObject-Sales_TitleSales

Errors

The Errors component is bound to the ErrorList for the view model's errors that were reported during the last operation. This includes validation errors for individual fields, cross-field validation errors, or any error messages received from the business services.

While individual data controls will also show their client validation error(s), those controls may be in different tabs, and not currently showing on the screen. Therefore, it's important to show the summary of all the errors in a place that's always visible, such as at the top of the view. Below is a sample picture illustrating the Errors component.

Error list

note

If you noticed, the text of the validation errors includes the field label, rather than a generic message, such as This field is required. This allows displaying those errors in the summary Errors control, and point the user to the specific field in error.

The icon next to each text and the color of each message is based on the severity of the message, as per the Bootstrap styles. For example, errors will be displayed in red, as shown above, while warnings will be in yellow, as illustrated below.

Warning list

To add the Errors component to your view, put it at the top of the view or in any other place that is always visible, and set the ErrorList parameter to the @Model?.Errors value, as follows.

<div class="modal-body">
<div class="row g-0">
<Errors Class="mb-3" ErrorList="@Model?.Errors" ViewKey="@Model?.GetResourceKey()"></Errors>
<fieldset>
<div class="row @GetRowCol(2)">
<XInputText Class="mb-3" Property="@VM?.MainObj?.UserNameProperty"></XInputText>
<XInputText Class="mb-3" Property="@VM?.MainObj?.PasswordProperty" Type="password"></XInputText>
...
</div>

The Errors component has a Close button, which will clear all the message in the error list, thereby closing the error summary.

Localizing the errors title

The title text displayed at the top of the error summary comes from the framework's resources using the following keys based on the highest severity of the messages and whether there is a single or multiple messages.

NameValueComment
View_ErrorPlease review the following error.
View_ErrorsPlease review the following errors.
View_WarningPlease review the following warning.
View_WarningsPlease review the following warnings.
View_MessagePlease review the following message.
View_MessagesPlease review the following messages.

You can customize these strings in your application resources or localize it for different languages. You can also define custom resources for your specific view by setting the resource prefix in the ViewKey parameter. For example, if you set it to @Model?.GetResourceKey() from the example above, which evaluates to "Login", you'll be able to override the title text for your view as follows.

NameValueComment
LoginView_ErrorCannot login due to the following error.
LoginView_ErrorsCannot login due to the following errors.

Panel

The Panel component represents a simple Bootstrap card with a title and a Close button that allows closing the panel, as shown below.

Criteria Panel

You can set the title text via the Title parameter and also bind the Collapsed parameter to your own boolean property, which will allow you to control the open/close state of your panel from your code. The following example shows how you can bind those parameters of the search criteria panel to the base properties of the BlazorSearchView.

<Panel Class="me-3" Title="@CriteriaText" @bind-Collapsed="CriteriaCollapsed">
<div>
<XActionButton Action="VM?.List?.ResetAction" OnClick="OnResetAsync"></XActionButton>
<XActionButton Action="VM?.List?.SearchAction" OnClick="OnSearchAsync" IsPrimary="true"
Class="float-end"></XActionButton>
</div>
...
</Panel>

CriteriaEdit

The CriteriaEdit component allows you to dynamically edit criteria in your CriteriaObject by selecting the criteria field from a dropdown list and entering the operator and criteria value(s), as illustrated below.

Criteria edit

The dropdown list is bound to the FieldSelectorProperty of the CriteriaObject, while the operator and value controls are bound to the CriteriaEditObject, which holds a copy of selected criteria's values for editing. This allows you to cancel your edits when clicking the Cancel button, or apply them after successful validation by clicking the Add or Update button. You can also blank out the criteria by clicking the Reset button.

To add the CriteriaEdit component to your view, you need to set the Criteria parameter to your CriteriaObject, and also provide a function that returns the component type for the currently selected criteria field in the ValueComponentType parameter, as illustrated below.

<CriteriaEdit Class="mt-4" Criteria="@VM?.CritObj"
ValueComponentType="@(() => GetValueComponentType())"></CriteriaEdit>
@code {
private Type GetValueComponentType()
{
var fieldName = VM?.CritObj?.FieldSelectorProperty?.EditStringValue;
switch (fieldName)
{
case EmployeeCriteria.JobTitle:
return typeof(XInputText);
case EmployeeCriteria.OrganizationLevel:
return typeof(XNumericBox);
case EmployeeCriteria.HireDate:
case EmployeeCriteria.ModifiedDate:
return typeof(XDatePicker);
case EmployeeCriteria.MaritalStatus:
return typeof(XOptions);
case EmployeeCriteria.Gender:
return typeof(XSelect);
}
return null;
}
}

CriteriaDisplay

The CriteriaDisplay component is typically coupled with the CriteriaEdit component to show the currently selected dynamic criteria and their values with operators, as applicable. The selected criteria are displayed in separate panel stacked one on top of the other, as shown below.

Criteria display

Criteria display multi

If selected criteria uses a multi-value property and has multiple values entered or selected, then, to save the screen space, the panel will only show the first two values and a count of the remaining values, as needed. The user can click on the count to see the remaining values in a popover tooltip, as shown in the second image above.

Clicking the X button on the panel will reset the corresponding criteria, thereby removing it from the display. Clicking on the value or an operator will open the CriteriaEdit component with the selected criteria, and will also highlight the corresponding panel in the CriteriaDisplay component.

To add the CriteriaDisplay component to your view, you just need to set the Criteria parameter to your CriteriaObject, as shown below.

<CriteriaDisplay Class="mt-3" Criteria="@VM?.CritObj"></CriteriaDisplay>

CriteriaBar

CriteriaBar is a specialized component that shows the summary of the currently applied criteria in your search views. It also provides a button to open up the collapsed criteria panel and a button to refresh the data in the results grid by rerunning the current criteria. You would typically place it right above the results grid, as shown below.

Criteria bar

You can bind parameters of the CriteriaBar directly to the corresponding methods and properties provided by the BlazorSearchView base class as follows.

<CriteriaBar Title="@CriteriaText" @bind-CriteriaCollapsed="@CriteriaCollapsed"
AppliedCriteria="@ListObject?.AppliedCriteria"
OnRefresh="@OnRefreshAsync"></CriteriaBar>
<XGrid List="@VM?.ListObj" @bind-CurrentPage="CurrentPage" AllowSelection="true">[...]

Just like the CriteriaDisplay component, the CriteriaBar component will also show only the first two values of the multi-value criteria and the count of the remaining values to save space. The user can click on the count to see the remaining values in a popover tooltip, as illustrated above.

note

While the currently applied criteria are usually the same as the criteria selected in the criteria panel, technically they can be different in cases when the user has modified the criteria in the panel but hasn't applied them yet by clicking on the Search button. Refresh and server-side paging or sorting will use the currently applied criteria, rather than the ones selected in the criteria panel.

CriteriaSummary

The actual summary of applied criteria in the CriteriaBar is rendered using a separate CriteriaSummary component. You can use it independently, if you don't want to use the CriteriaBar component and want to implement custom buttons for opening the criteria panel or refreshing the grid. All you need to do is to set the AppliedCriteria and Title parameters as follows.

<CriteriaSummary AppliedCriteria="@ListObject?.AppliedCriteria" Title="@CriteriaText" />

The localized text for the title comes from the resources using the key View_Criteria. You can also customize it for a specific view by prefixing it with the view model's resource key, as follows.

NameValueComment
View_CriteriaSearch Criteria
SalesOrderView_CriteriaSales Order Criteria

Pager

The Pager component is used as part of the XGrid to display the grid page navigation at the bottom. However, you can also use it to add paging to other list controls, or to add (another) pager at the top of XGrid, as shown below.

Pager

All you need is to bind the ItemsCount, CurrentPage and PageSize parameters, as well as the CurrentPageChanged and PageSizeChanged methods to the properties and methods on your data list object, as illustrated below.

<CriteriaBar Title="@CriteriaText" @bind-CriteriaCollapsed="@CriteriaCollapsed"
AppliedCriteria="@VM.List?.AppliedCriteria"
OnRefresh="@OnRefreshAsync"></CriteriaBar>
<div class="mb-2">
<Pager ItemsCount="@((VM.List?.PagingMode == DataListObject.Paging.Server ?
VM.List?.TotalRowCount : VM.List?.RowCount) ?? 0)"
CurrentPage="@(VM.List?.CurrentPage ?? 0)"
CurrentPageChanged="async (page) => await VM.List?.SetCurrentPage(page)"
PageSize="@(VM.List?.PageSize ?? 0)"
PageSizeChanged="async (size) => await VM.List?.SetPageSize(size)"
ResourceKey="@Model?.GetResourceKey()"/>
</div>
<XGrid List="@VM?.List" ResourceKey="@Model?.GetResourceKey()">[...]
tip

If you want to use a different pagination style, you can create a custom component and inherit it from the Pager component to reuse the pagination logic.

Customizing page sizes

The user can change the number of rows to display on each page using a dropdown list with the page sizes at the bottom right. By default, the list of page sizes is set to the following values: 7, 14, 25, 50, and 100. You can set the PageSize property on the data list object to indicate the initial page size to use, which by default is set to 14.

You can customize the list of page sizes from which the user can select by setting the PageSizes attribute to an array of integers, as illustrated below.

<Pager PageSizes="new [] { 10, 25, 50, 100}" .../>

The above example will result in the following selection of page sizes, with the initial page size being set to 10 (first option).

Page sizes

Customizing page navigation

The pager shows a navigation panel at the bottom center of the grid to allow the user to switch from one page to another in the grid. That panel will enable you to jump to the first or the last pages, turn to the next or previous pages, or pick some pages around the current page, as shown below.

Page navigation

The number of pages around the current page you can jump to is determined by the PagesToShow parameter, which is defaulted to 7. If you want to show more pages, or if your pager doesn't have enough space and you want to show fewer pages, then you can customize this parameter in your pager as follows.

<Pager PagesToShow="5" .../>
tip

You may want to pick an odd number for the PagesToShow value so that the current page would be in the middle when there are more pages to the left and the right.

Localizing pager texts

The strings that the Pager component uses to display on the screen or as a tooltip of the navigation buttons are retrieved from the current resources using the following keys.

NameValueComment
Pager_FirstFirst Page
Pager_LastLast Page
Pager_NextNext Page
Pager_PrevPrevious Page
Pager_PageSizeItems per page
Pager_SummaryShowing {0} to {1} of {2} items.{0}=Lower range boundary, {1}=Upper range boundary, {2}=Total number of items

You can create localized resources for these texts that are translated into the desired language(s). Also, if you would like to change the wording for these default resources, e.g. say rows instead of items, then you can redefine them in a new resource file using hierarchical resources.

If you want to override these labels for a specific grid or view, then you can set the ResourceKey parameter on the pager. For example, the following code sets the ResourceKey parameter to the base name of the view model's class (SalesOrderList).

SalesOrderListView.razor
<Pager ResourceKey="@Model?.GetResourceKey()" .../>

This will allow you to set the text of the labels specifically for that view, saying orders instead of items, as follows.

NameValueComment
SalesOrderListPager_PageSizeOrders per page
SalesOrderListPager_SummaryShowing {0} to {1} of {2} orders.{0}=Lower range boundary, {1}=Upper range boundary, {2}=Total number of items