Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Angular + PrimeNG table with advanced filters, delegating all logic to a .NET (ASP.NET) backend. Designed for SQL Server, easily adaptable to other databases.

License

Notifications You must be signed in to change notification settings

AIO1/ECSPrimengTable

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NuGet Version NuGet Downloads

npm version npm downloads

ECS PrimeNG Table

A solution created by Alex Ibrahim Ojea that enhances the PrimeNG table with advanced filters and extended functionality, delegating all query and filtering logic to the database engine. The frontend is built with Angular 20 and PrimeNG 20 components, while the backend is a .NET 8 (ASP.NET) API connected to Microsoft SQL Server, easily adaptable to other databases. This approach prevents server and frontend overload by handling filtering and paging dynamically in the database, and includes features such as column visibility, column filters, custom views, and more.




Introduction

Hello! My name is Alex Ibrahim Ojea.

This project was created to provide an efficient and reusable PrimeNG table solution for Angular applications. Unlike the default PrimeNG approach, which requires loading all data into the frontend, this implementation delegates filtering, sorting, and pagination logic directly to the database engine, making it highly performant on large datasets.

The goal is to make it simple to integrate a powerful, flexible, and good-looking table into your applications without overloading either the frontend or the server.

Some of the key features included are:

  • Dynamic pagination with lazy loading
  • Multi-column sorting
  • Advanced and predefined filters
  • Global search
  • Column resizing, reordering, toggling, and descriptions
  • Customizable cells (alignment, overflow, tooltips, ...)
  • Conditional row styling
  • Table views for saving configurations
  • And much more!

This is an example of the final solution:

Example table




1 Required software

To run this project, you will need:

  • Visual Studio Code – for frontend development.
  • Visual Studio 2022 – for backend API development with ASP.NET Core. Make sure to install the ASP.NET workload and .NET 8 framework.
  • Node.js – to run the Angular application. Managing Node versions with NVM is recommended.
  • Microsoft SQL Server – the database engine used for queries. Optional, can be replaced with other engines with minor code adjustments.
  • (Optional) DBeaver – A GUI for database management that works with multiple engines. You can use other tools, but this is the one I normally use.





2 Setup the environment to try the demo

2.1 Database (MSSQL)

This example has been set up using MSSQL. Other database engines should work with some modifications, but this guide only covers MSSQL. First, create a new database named primengtablereusablecomponent. The database should have a schema named dbo. You can use a different database or schema name, but you will need to adapt the backend and database scripts accordingly. Once the database and schema are ready, download all the database scripts located under this path. Execute the scripts in order (starting with 00):

  • 00 Create EmploymentStatusCategories.sql: Creates the table EmploymentStatusCategories, which contains all possible employment categories used in the predefined filter example.
  • 01 Populate EmploymentStatusCategories.sql: Inserts initial records into the EmploymentStatusCategories table.
  • 02 Create TestTable.sql: Creates the table used for testing, containing the main data displayed in the frontend.
  • 03 Populate TestTable.sql: Inserts sample data into TestTable. This script can be slightly modified to generate different random data.
  • 04 FormatDateWithCulture.sql (optional): Creates a database function used by the backend to allow global search on date columns, formatting them as text with the same mask, timezone, and locale as in the frontend.
  • 05 SaveTableViews.sql: Creates an example table to store user-defined table views. This is only needed if you are using the database to save views instead of browser or session storage.

After executing all scripts successfully, you should have:

  • Two populated tables (EmploymentStatusCategories and TestTable).
  • One empty table (TableViews).
  • One function (FormatDateWithCulture).

The following image shows the ER diagram of all the tables:

ER diagram of example project



2.2 Backend (API in ASP.NET)

Note

You can use other .NET versions with the corresponding packages. The solution should still work without issues.

2.2.1 Open the project

Using Visual Studio 2022, open the backend solution located in this path. Make sure the ASP.NET workload and .NET 8 framework are installed. If any component is missing, use the Visual Studio Installer to add it.



2.2.2 Update the database connection string

Note

If you followed the default MSSQL installation and configured the database as primengtablereusablecomponent with a schema named dbo and no authentication, you can skip this step. Otherwise, follow these instructions carefully to avoid connection issues.

Next, update the database configuration for your backend API. Open the appsettings.Development.json file and ensure that the connection string under "DB_primengtablereusablecomponent" matches your setup. If you change the identifier name of the connection string in appsettings.json, remember to update it accordingly in Program.cs.



2.2.3 Scaffolding the database

Note

This step is optional and only needed if you modify the database structure, want to generate the DbContext or models in a different location, or plan to use a database engine other than MSSQL.

To perform scaffolding, open the Package Manager Console in Visual Studio and navigate (cd) to the root folder of the project (where the .sln file is located). Once in the project folder, run the following command (assuming your database is named primengtablereusablecomponent, you are using SQL Server, and you want to place the DbContext and models in the same locations as in the example code):

dotnet ef dbcontext scaffold name=DB_primengtablereusablecomponent Microsoft.EntityFrameworkCore.SqlServer --output-dir Models --context-dir DBContext --namespace Models.PrimengTableReusableComponent --context-namespace Data.PrimengTableReusableComponent --context primengTableReusableComponentContext -f --no-onconfiguring

These are the common changes you may need to make in the command:

  • name=DB_primengtablereusablecomponent: Change only if you modified the connection string name in appsettings.Development.json.
  • Microsoft.EntityFrameworkCore.SqlServer: Change this to the appropriate provider package if you are using a different database engine.
  • --output-dir: Specifies where the models will be generated. In this example, they will be generated in the Models folder (created automatically if it does not exist).
  • --context-dir: Specifies where the DbContext will be generated. Here it will be created in a folder named DBContext (created automatically if it does not exist).
  • --namespace and --context-namespace: Set the namespaces for the models and the DbContext, respectively.
  • --context: Sets the name of the DbContext. In this example, it will be primengTableReusableComponentContext.
  • -f: Forces overwriting existing files.
  • --no-onconfiguring: Tells the scaffolding process not to configure the connection in the DbContext. In this example, the connection is managed through the appsettings.Development.json file.



2.2.4 API first run

After completing the previous steps, you should now be able to run the API and verify that everything works before moving to the frontend. In Visual Studio 2022, click the green Play button on the top bar. The API will start, and after a few moments, a webpage should appear. If everything is working correctly, you should see the Swagger-generated API documentation with some test endpoints. Below, there is a Schemas section showing all schemas detected by Swagger during documentation generation. To test that the API endpoints and database communication are working, perform a quick test with the Main/GetEmploymentStatus GET method (it is easy to test and requires no parameters):

  1. Click Try out under the method.
  2. Click Execute. Upon execution, you should receive a 200 response with a body similar to the following:
[
  { "statusName": "Contract", "colorR": 100, "colorG": 200, "colorB": 0 },
  { "statusName": "Freelance", "colorR": 0, "colorG": 150, "colorB": 0 },
  { "statusName": "Full-time", "colorR": 0, "colorG": 200, "colorB": 0 },
  { "statusName": "Intern", "colorR": 0, "colorG": 150, "colorB": 0 },
  { "statusName": "Military", "colorR": 0, "colorG": 200, "colorB": 100 },
  { "statusName": "On leave", "colorR": 200, "colorG": 200, "colorB": 0 },
  { "statusName": "Other", "colorR": 200, "colorG": 125, "colorB": 0 },
  { "statusName": "Part-time", "colorR": 50, "colorG": 200, "colorB": 0 },
  { "statusName": "Retired", "colorR": 0, "colorG": 50, "colorB": 0 },
  { "statusName": "Self-employed", "colorR": 0, "colorG": 200, "colorB": 50 },
  { "statusName": "Student", "colorR": 0, "colorG": 100, "colorB": 0 },
  { "statusName": "Temporary", "colorR": 150, "colorG": 200, "colorB": 0 },
  { "statusName": "Unemployed", "colorR": 200, "colorG": 0, "colorB": 0 },
  { "statusName": "Volunteer", "colorR": 0, "colorG": 200, "colorB": 50 }
]

If you see these results, it means your API is running correctly and communicating with the database, as these GET endpoints retrieve data directly from it. Take note of the port number in the API URL, as it will be needed later to configure the frontend.



2.3 Frontend (Angular project using PrimeNG components)

Note

You can use other Angular and PrimeNG versions by updating the corresponding package.json dependencies. The solution should still work, but be aware that PrimeNG could introduce breaking style changes that may affect the component's appearance or behavior.

This section assumes you have completed the previous steps to set up the database and API. Before proceeding, ensure that Node.js is installed (via the .msi or .exe installer, or using NVM), as it is required to run the frontend application locally.

To run the frontend demo, open the frontend folder in Visual Studio Code. Make sure your API is running on the expected port (as noted in the previous steps).

To confirm that the frontend points to the correct API endpoint, open constants.ts and check the function getApiBaseUrl. In development mode, it should return something like:

"https://localhost:7020/"

Ensure that the port matches your API. If it differs, update the value and save the file.

Important

Always verify that getApiBaseUrl points to the correct API port before continuing with this section.

From within Visual Studio Code, open a new terminal (make sure it is using CMD and not PowerShell or another shell) and navigate to the root folder of the frontend project using the cd command. Once in the correct folder, run the following command:

npm install

Tip

You can add the --verbose flag at the end (npm install --verbose) to get more detailed output during the installation process.

This command will download all required dependencies for the frontend project. Once it has finished executing and if everything went OK, ensure your API is running correctly, then execute the following command in the terminal:

ng build ecs-primeng-table

This command will use ng-packagr to build a local package in the dist folder, based on the contents of projects\ecs-primeng-table (the reusable table component).

Once the package has been successfully built, you can start the web application by running the following command in the terminal:

ng serve -o

Tip

ng serve without the -o flag also works, but it won't open a browser tab automatically. You will need to navigate manually to the URL where the webpage is served.

After a few seconds, a new tab in your web browser should open, displaying the table fully functional.

If you have reached this step, congratulations! You have successfully set up and started the demo project! 😄





3 Integrating into a project

This section provides a step-by-step guide on how to integrate the ECS PrimeNG Table into either a new or an existing project.

3.1 Backend requirements

Note

The ECS PrimeNG Table package is built for .NET 8, but it should also work seamlessly with newer .NET versions.

If you are already working on a .NET 8 project (or higher), you will need to install the backend compiled package from NuGet (we recommend downloading the latest version):
ECS.PrimeNGTable on NuGet

In addition, make sure the following required dependencies are installed:

  • ClosedXML (>= 0.104.0)
  • LinqKit (>= 1.3.0)
  • Microsoft.EntityFrameworkCore (>= 8.0.0)
  • System.Linq.Dynamic.Core (>= 1.6.0)

Tip

You can always check the latest dependency versions by visiting:
https://www.nuget.org/packages/ECS.PrimeNGTable/<version>#dependencies-body-tab
(Replace <version> with the specific package version you are downloading, e.g., 8.0.1).

With these dependencies in place and the package installed, your backend is ready to use the ECS PrimeNG Table.



3.2 Frontend requirements

3.2.1 Installing the package and peer dependencies

Note

The ECS PrimeNG Table package is built for Angular 20 with PrimeNG 20 components. While it may work with newer versions, compatibility is not guaranteed, as PrimeNG frequently introduces breaking changes to its components.

If you are already working on an Angular 20 project, you can check the frontend compiled package on NPM here:
@eternalcodestudio/primeng-table on NPM

To install the package, open a terminal in the root folder of your project and run the following command (we recommend installing the latest version):

npm install @eternalcodestudio/primeng-table

In addition, make sure the following required dependencies are installed in your project:

  • @angular/common (>=20.0.0)
  • @angular/core (>=20.0.0)
  • primeng (>=20.0.0)
  • primeicons (>=7.0.0)

Caution

These are peer dependencies and are not installed automatically. If your project doesn't already include them, you must install them separately using NPM.



3.2.2 Configure Angular locales

The ECS PrimeNG Table component relies on Angular's DatePipe to render date cells.
To ensure correct formatting, you must import and register the locale(s) you plan to use in your application.

Example for English locale (en):

import { DatePipe, registerLocaleData } from '@angular/common';
import en from '@angular/common/locales/en';

registerLocaleData(en);

Remeber to include also DatePipe in your providers.

This step is required before using the table. If the locale is not correctly registered, rendering date cells may fail and prevent the table from displaying properly.

You can include this configuration at the global level (e.g., app.module.ts or app.config.ts) or at a more local level, depending on your application structure.



3.2.3 Required services for ECS PrimeNG Table

The ECS PrimeNG Table package defines two abstract services that you need to implement in your project:

  • ECSPrimengTableHttpService: handles HTTP requests for the table (GET and POST).
  • ECSPrimengTableNotificationService: handles notifications (toasts) for the table.

These services are abstract, meaning the package does not know how you want to handle HTTP requests or notifications in your project. You need to create your own implementations.



Example: HTTP service

In your project, create a class that extends ECSPrimengTableHttpService and implements its abstract methods.

In this example, the implementation uses the main services provided by SharedService.

import { Injectable } from '@angular/core';
import { HttpHeaders, HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ECSPrimengTableHttpService } from '@eternalcodestudio/primeng-table';
import { SharedService } from './shared.service';

@Injectable({ providedIn: 'root' })
export class HttpService extends ECSPrimengTableHttpService {
  constructor(private sharedService: SharedService) {
    super();
  }

  handleHttpGetRequest<T>(
    servicePoint: string,
    responseType: 'json' | 'blob' = 'json'
  ): Observable<HttpResponse<T>> {
    return this.sharedService.handleHttpGetRequest(servicePoint, null, true, null, false, responseType);
  }

  handleHttpPostRequest<T>(
    servicePoint: string,
    data: any,
    httpOptions: HttpHeaders | null = null,
    responseType: 'json' | 'blob' = 'json'
  ): Observable<HttpResponse<T>> {
    return this.sharedService.handleHttpPostRequest(servicePoint, data, httpOptions, true, null, false, responseType);
  }
}



Example: Notification service

Similarly, you need to create a class that extends ECSPrimengTableNotificationService and implements its abstract methods.

In this example, the implementation relies on the main services provided by SharedService.

import { Injectable } from '@angular/core';
import { ECSPrimengTableNotificationService } from '@eternalcodestudio/primeng-table';
import { SharedService } from './shared.service';

@Injectable({ providedIn: 'root' })
export class NotificationService extends ECSPrimengTableNotificationService {
  constructor(private sharedService: SharedService) {
    super();
  }

  showToast(severity: string, title: string, message: string): void {
    this.sharedService.showToast(severity, title, message, 5000, false, false, false);
  }

  clearToasts(): void {
    this.sharedService.clearToasts();
  }
}



Registering the services

Finally, register your implementations in your dependency injection system (for example, in app.config.ts):

import { ECSPrimengTableHttpService, ECSPrimengTableNotificationService } from '@eternalcodestudio/primeng-table';
export const appConfig: ApplicationConfig = {
  providers: [
    ...
    { provide: ECSPrimengTableNotificationService, useClass: NotificationService },
    { provide: ECSPrimengTableHttpService, useClass: HttpService },
    ...
  ]
}

This tells the ECS PrimeNG table package to use your custom services for handling HTTP requests and notifications.





4 Functional overview

The goal of this section is to provide a user-level overview of all the features included in the ECS PrimeNG table. It allows you to quickly understand what the table can offer and how these functionalities can be utilized in your projects. This section provides a clear, at a glance view of everything available without diving into code.



4.1 Planning your table

Before diving into advanced features, it’s essential to start with the basics and carefully plan your table design. This will ensure that the table fits your users needs and your application’s requirements. Use the following questions as a guide:

Columns

  • Which columns do I want to include in the table?
  • Should all columns be visible by default, or will some be hidden initially?
  • Are there columns that must always remain visible and cannot be hidden?
  • What horizontal and vertical alignment should each column have?
  • How should content overflow be handled in each column (e.g., wrap, truncate)?
  • Which columns should allow sorting: all, some, or none?
  • Which columns should allow filtering: all, some, or none?
  • Can users change the position of columns via drag-and-drop?
  • Are any columns going to be frozen (fixed) on the left or right side?

Rows

  • Do any rows need conditional formatting based on the values of a specific column?
  • What actions should I allow per row? Are action buttons enabled or disabled based on certain conditions?
  • Can rows be selected (and an action performed on select)?
  • Can users select multiple rows, filter by selected rows, or perform actions on multiple selections (row checkbox selector)?

Global table features

  • Are there any table-level actions needed, such as creating records?
  • How will dates be displayed in the table?
  • Will users be able to customize the date format?
  • Will a global filter be available for the table?
  • Should the table support exporting data to Excel?
  • Will users be able to save their table configuration? If so, should it be persistent across sessions or only for the current session?

Don’t worry if some of these concepts are unclear at this point, each feature will be explained individually in detail in the following sections.

Note

This solution works only to data that is already persisted in the database. It is not intended to handle data currently being edited in memory on the front end and not yet saved to the database.



4.2 Date formatting

At first glance, date formatting might seem simple, but it can easily confuse end users if not carefully considered from the start.

The ECS PrimeNG table component allows you to control how dates are displayed in each table, letting you customize:

  • Format: This defines how the date and time will be displayed to the user.
    For example, "dd-MMM-yyyy HH:mm:ss zzzz" means:
    • dd → day of the month (01-31).
    • MMM → short name of the month (Jan, Feb, etc.).
    • yyyy → full year (2025).
    • HH:mm:ss → hours, minutes, and seconds in 24-hour format.
    • zzzz → time zone name or offset.
  • Time zone: This specifies the time zone that will be used to display the date/time.
    For example, "+00:00" is UTC (Coordinated Universal Time). Changing this will adjust the displayed time to the desired zone.
  • Culture: This determines the language and formatting conventions for the date, such as month names, day names, and the order of day/month/year. Default "en-US" uses English (United States) conventions. Using "es-ES" would show month and day names in Spanish, for example.

You can configure this customization per table, with several possible approaches:

  • Static: Use the default values or hardcode alternative values if they suit your needs.
  • Server-based: Use the configuration of the server environment where your application is deployed.
  • Per-user: Save each user's preferred configuration, allowing users to choose how dates are displayed in their tables. This requires additional setup but provides maximum flexibility.

Note

While per-table customization is possible, it is recommended to set a global configuration for all tables. Individual table settings are mainly useful for specific scenarios, but managing a global configuration is easier and more consistent.



4.3 Column configurations

The ECS PrimeNG Table allows you to define a variety of settings that control how each column behaves when displayed to users and what they are allowed to do with them.

4.3.1 Data type

Columns can be configured to define how cell data is displayed and treated. The ECS PrimeNG Table supports five main data types, and choosing the appropriate type is important, as it also affects the filtering options available (column filtering is explained in later sections):

  • Text: For data that should be treated as plain text.
  • Numeric: For numerical values.
  • Boolean: For yes/no (true/false) values.
  • Date: For date values. The display format is controlled via the date formatting configuration described in previous sections.
  • List: A specialized text variant designed for columns containing data separated by ";". This type is mainly intended for predefined filters. If not configured, the raw text will simply be displayed (predefined filters are explained in later sections).

Note

All data types support null (empty) values, allowing cells to remain blank if no data is available.



4.3.2 Visibility

By default, all columns are visible. However, showing too many columns at once may overwhelm users, so you may want to hide some of them initially. This can be configured in the table setup.

The table includes a built-in column properties menu (enabled by default), which allows users to show or hide columns at any time without needing to reload or reconfigure the table. This menu is accessible directly from the table interface and provides a simple checklist of all available columns. (Explained in more detail in later sections.)

You can also restrict visibility changes for specific columns. For example, some columns can be marked as always visible, preventing users from hiding them.

Additionally, developers can define utility columns that remain hidden from the user interface. These columns (such as row IDs or internal references) are not only invisible to the end user but also excluded from the column properties menu, ensuring they remain hidden while still being available for internal logic or processes.



4.3.3 Horizontal and vertical alignment

Each column can be configured to control how the data inside its cells is aligned, both horizontally and vertically.

Horizontal alignment options:

  • Left: Aligns the content to the left side of the cell. Commonly used for text values.
  • Center: Centers the content in the cell. Default option.
  • Right: Aligns the content to the right side of the cell. Typically used for numeric data.

Vertical alignment options:

  • Top: Aligns the content to the top of the cell.
  • Middle: Centers the content vertically. Default option.
  • Bottom: Aligns the content to the bottom of the cell.

By default, columns are set to center horizontally and middle vertically.

Users can change the alignment of any column using a dedicated column properties menu (explained in later sections). You can restrict this behavior in two ways:

  • Restrict per column: Prevent users from changing the horizontal and/or vertical alignment for specific columns.
  • Disable globally: Turn off the entire Column Properties menu so users cannot adjust alignment or any other column settings.



4.3.4 Overflow behaviour

When the content of a cell exceeds the available space, the overflow behaviour determines how the data is displayed. The available options are:

  • Hidden: Extra content is clipped and not displayed. This avoids breaking the table layout but may hide part of the information.
  • Wrap: The content automatically continues on a new line within the same cell, ensuring all data is visible but potentially increasing the row height.

By default, the overflow behaviour for all columns is set to Hidden.

Users can adjust the overflow behaviour of each column through the column properties menu (explained in later sections). This feature can be controlled in two ways:

  • Restrict per column: Prevent users from changing the overflow behaviour for specific columns.
  • Disable globally: Turn off the entire column properties menu so users cannot modify overflow behaviour or any other column settings.



4.3.5 Column properties menu

By default, the table includes a column properties button located at the top-left corner. This button opens a modal that allows users to customize how columns are displayed and formatted.

Modify column properties button

This menu can be disabled globally if you do not want users to make any modifications to column properties or visibility.

When enabled, clicking the button opens a modal window that provides the following features:

  • Column list: Displays all available columns in the table (excluding utility columns).
  • Search bar: A global search input to filter columns by name. Columns are listed alphabetically (A–Z).
  • Editable properties (if not locked for the column):
    • Visibility (show/hide columns).
    • Horizontal alignment.
    • Vertical alignment.
    • Cell overflow behaviour.

At the bottom-right of the modal, users can either Cancel or Apply their changes:

  • If visibility changes are applied, the table will refresh data and reset filters and sorting.
  • If only formatting changes (alignment or overflow) are applied, the table will preserve filters and sorting without refreshing data.

Modify column properties menu



4.3.6 Resize

By default, all columns can be resized by the user. This feature can also be disabled for specific columns if desired.

To resize a column, the user must move the cursor to the left edge of the column header. When the resize icon appears (resize icon), the user can press and hold the mouse button, then drag horizontally to adjust the column’s width.

Some important design aspects to take into account:

  • Columns cannot be resized if they are not visible.
  • By design, resized columns will always maintain a minimal width (about 18px).
  • This ensures that users cannot make a column completely disappear by dragging it below this threshold.
  • Frozen columns cannot be resized.

resize example



4.3.7 Reorder

The ECS PrimeNG Table also includes the ability for users to reorder the columns displayed in the table. This feature can be enabled or disabled per column.

How it works is as follows:

  1. The user clicks and holds the header of the column they want to move. Important: The click must be on the main header area (not on icons and not on the edges, otherwise it will be detected as a resize action instead).
  2. When the action is done correctly, a semi-transparent copy of the column header (a "ghost" header) will appear and follow the mouse pointer.
  3. While holding down the mouse button, the user can drag this ghost header horizontally to the desired location.
  4. To place the column:
    • The ghost header must be aligned to the left side of the column where the user wants to insert it.
    • When the position is valid, two arrows (one above and one below) will appear as indicators.
  5. Once the arrows are visible, releasing the mouse button will reorder the column to the new position.

Tip

As a design suggestion, it is recommended to keep column reordering consistent across the table:

  • Either disable reordering for all columns, or allow it for all.
  • If you need to restrict specific columns, it is best to apply this only to frozen columns.

column reorder example



4.3.8 Frozen

Some columns can be configured as frozen, depending on the table design.

Frozen columns are always placed at one of the table edges: either on the left side or on the right side.

These columns remain visible at all times, even when the user scrolls the table horizontally.

frozen columns example



4.3.9 Descriptions

Columns can include a description to provide additional context. When a column has a description, an information icon (info icon) will appear on the right side of the column header.

If the user hovers the mouse over this icon, a tooltip will be displayed showing the column’s description.

This feature is especially useful for columns that may require extra details to help users better understand the data being presented.

column description example



4.3.10 Cell tooltip

By default, each cell in all columns will display a tooltip when the mouse hovers over it, except for columns configured with a boolean data type. The tooltip content will be the same as the cell’s value.

It is also possible to configure the tooltip to display the value from another column (which also works with columns with boolean data type). This can be useful, for example, when a column only shows an icon to indicate whether an upload was successful or not: if the upload failed, hovering the mouse over the icon can show the corresponding error message in the tooltip.

image

Caution

Keep in mind that when referencing other columns, you can only access data from columns that are currently visible in the table. Therefore, avoid mapping tooltips to columns that users can hide, and instead use utility columns that remain always available.



4.3.11 Sorting

By default, all columns are sortable. You can disable sorting on specific columns if you do not want users to sort them.

How sorting works:

  • Click a column header once to sort in ascending order.
  • Click the same header a second time to sort in descending order.
  • Click a third time to sort ascending again.

If a different column is clicked while another column is already sorted, the new column will be sorted in ascending order, and the previous column will have its sorting cleared.

The table supports multi-column sorting: users can hold the Ctrl key while clicking multiple column headers to sort by several columns simultaneously.

You can also define a default sorting for one or more columns when the user has not applied any sorting.

In the top-left corner of the table, there is a button to clear all sorting applied by the user. This button is enabled only when at least one user-applied sorting is active.

Clear sorting button

Note

If no columns allow sorting, you may hide this button. However, it is not recommended to hide it if some columns are sortable, as this could confuse users by preventing them from resetting the sorting.



4.3.12 Filtering

By default, all columns in the table support filtering. This feature can also be disabled for specific columns if required.

How filtering works:

  • Each column header includes a filter icon.
  • When the user clicks this icon, a filter menu appears.
  • The type of filter shown depends on the column’s data type or on a predefined filter (predefined filters explained in later sections).

At the top of every filter menu (except for boolean types), the user can choose between:

  • Match all (default): only records that satisfy all rules defined for that column are returned.
  • Match any: records that satisfy at least one of the rules are returned.

Users can define up to two rules per column, except for boolean columns.

The available rules per data type are as follows:

  • Text

    • Starts with
    • Contains
    • Does not contain
    • Ends with
    • Equals
    • Does not equal
  • Numeric

    • Equals
    • Does not equal
    • Less than
    • Less than or equal to
    • Greater than
    • Greater than or equal to
  • Boolean

    • A simple true/false selector
  • Date

    • Date is
    • Date is not
    • Date is before
    • Date is after
  • List

    • If not setup as a predefined filter, it will work as the text filter.

For the date data type, filters consider only the date portion and ignore the time. For example, if the user applies the filter Date is with the value 23-Sep-2024, all records with a date between 23-Sep-2024 00:00:00 and 23-Sep-2024 23:59:59 will be returned.

Timezone conversion is automatically handled by the table. For instance, if the table is configured to use GMT+02:00 and a user applies the filter 23-Sep-2024, the query will correctly filter records between 23-Sep-2024 02:00:00 UTC and 24-Sep-2024 01:59:59 UTC.

In the top-left corner of the table, there is a button to clear all active filters. This button is enabled only when at least one filter has been applied by the user.

Button delete filters

Note

If no columns allow filtering, you may hide the "Clear filters" button. However, if some columns are filterable, it is recommended to keep this button visible to avoid confusing users and to provide a quick way to reset filters.

An example for a filter menu for text data type:

Filter menu example for text



4.3.13 Predefined filters

Caution

Avoid using this feature on columns that can have a large number of different values, as it may cause performance issues. Predefined filters are intended for columns with a limited set of known values.

Predefined filters are a special type of filter where, instead of letting the user input any value, you limit the selection to a dropdown with known values for that column.

Additionally, predefined filters allow you to customize how values are displayed in a cell. Supported display formats include:

  • Plain text
  • Tags, where you can personalize the tag color.
  • Icons, where you can customize the icon color and size. Icons can come from multiple libraries, such as PrimeNG icons, Font Awesome, Material Icons, etc...
  • Images, which will be displayed directly in the cell. The table manages image loading and displays a skeleton while downloading. Images can be hosted locally on your server or come from external URLs.

The same formatting applied to the cell will also appear in the dropdown for filtering.

The dropdown includes a global search bar and allows the user to select one or more items simultaneously (the filter applied is of type "OR").

Predefined filters also have a special use case for columns of type list. In this case, all elements in the column are separated by ;, allowing multiple items of your list to be displayed simultaneously applying the defined format.

Tip

You can combine formats in predefined filters. For example, you could display an image and plain text together.

Predefined filter example



4.4 Row configurations

The ECS PrimeNG Table allows you to configure various settings that control how each row behaves when displayed to users, as well as the actions associated with them.

4.4.1 Single select

The ECS PrimeNG Table allows enabling single row selection for rows. When enabled, this feature lets the user select a single row.

You can associate actions when a user selects or unselects a row. Additionally, you can access the currently selected row at any time and perform actions.

Users can also unselect a previously selected row. There are two ways to configure this behavior:

  • CTRL + Click (default): Hold down the CTRL key and click the already selected row to unselect it.
  • Click only: Simply click the already selected row to unselect it, without needing to press CTRL.

Single row select example

Note

On mobile devices (phones or tablets), the CTRL key configuration is ignored. Users can unselect a previously selected row by simply clicking it, as mobile devices do not have a CTRL key.

Caution

If the default behavior (holding down CTRL to unselect) is enabled, repeatedly clicking the same row without holding CTRL will count as multiple selections.
This can cause unintended behavior if actions are triggered on each selection, so plan your row actions accordingly.



4.4.2 Checkbox select

If you need users to select multiple rows simultaneously, you can enable the checkbox select feature.

When enabled:

  • A new column appears in the table for checkboxes.
  • This column cannot be hidden or have its alignment changed via the column properties menu.
  • The column also includes a filter, allowing users to filter between selected and unselected rows.

You can associate actions when a user selects or unselects a row using the checkbox. Additionally, you can access the currently selected rows at any time and perform actions based on them.

The checkbox select column has this additional customizable options:

  • Header title: Default is "Selected"
  • Alignment: Default is left
  • Width: Default is 150px
  • Frozen column: Default is true
  • Resizable by user: Default is false

Checkbox row select example



4.4.3 Dynamic styling

The ECS PrimeNG Table allows you to apply dynamic styling to specific rows, making them visually distinct from other rows or even changing their appearance at runtime.

The most common use case is to change the text format or background of a row based on a specific column value.

In the example below:

  • If the column Employment status list contains the value Full-time, the row will display its text in bold and italic.
  • If the value Unemployed exists in the column Employment status list, the row’s background color will change to light red, the text color to dark red, and the font weight will be bold.

Dynamic row styling

Tip

A row can combine multiple styles simultaneously, applied from different rules.



4.5 Action buttons

The ECS PrimeNG table allows you to define action buttons both in the table header and within each row.

The key difference between both of them:

  • Header action buttons do not have access to row data.
  • Row action buttons can access the data of the row in which they are clicked, allowing you to perform actions on a specific record.

The customizable properties for action buttons are:

  • Icon: You can optionally display an icon and customize its color and size. Icons can come from multiple libraries, such as PrimeNG icons, Font Awesome, Material Icons, etc...
  • Icon position: By default left, but the icon can be displayed to the right, bottom or top of the label.
  • Label: Text to display on the button.
  • Rounded: If the button should have round corners or not.
  • Raised: If active, adds a shadow to indicate elevation in the button.
  • Variant: The default is a normal button, but you can also have buttons that are text or outlined.
  • Color: The button color.
  • Style: Additional styles to add to the button.
  • Condition: Allows disabling the button if a specific condition is not met.
  • Hide if condition not met: By default if a specified condition is not met, the button will be disabled, but you can also hide it completely instead.
  • Action: The function or operation to execute when the button is clicked.
  • Tooltip: The text to be displayed when hovering the button.

An example of a header action button:

Header action buttons

When at least one row action button is defined, a new column automatically appears to display the buttons for each row. This column has the following customizable options:**

  • Header title: Default is "Actions"
  • Alignment: Default is right
  • Width: Default is 150px
  • Frozen column: Default is true
  • Resizable by user: Default is false

An example of the actions columns with row action buttons:

Row action buttons



4.6 Global filter

The global filter is enabled by default for all columns and appears in the top-right corner of the table. It allows users to search for a keyword across all visible columns of the table simultaneously.

This feature can be disabled:

  • Globally, by hiding the global filter input box.
  • Per column, excluding specific columns from global search.

The global filter does not apply to columns with a boolean data type, since there is no keyword to match.

How it works:

  • When a user types a value in the global filter input box, the table:
    • Returns all rows that contain the keyword in any of the displayed columns.
    • Highlights the matched text in yellow.
  • The keyword can match any part of the text.
  • If the global filter input box contains a value, an "X" icon appears on the right, allowing the user to clear the filter with a single click.

An example of the global filter:

Global filter example

Caution

Using the global filter may significantly affect performance, especially in large datasets.
Consider the following best practices to keep it efficient:

  • Limit the maximum number of columns that can be visible at the same time, since the global filter runs across all displayed columns.
  • Be cautious with date columns, as the global filter converters dates to strings and it is more expensive.
  • Optimize your backend for keyword searches (e.g., using proper indexes).
  • Limit the total number of records retrieved at once if performance is a concern.



4.7 Pagination and record count

The ECS PrimeNG table automatically manages both pagination and record counting for you. This means only the data required for the current page is loaded on the front-end, optimizing performance and minimizing the amount of information transferred.

At the bottom of the table you will find two main areas:

  • Left side: shows a message like "Showing X records of X available".

    • If filters are applied, the "Showing X" part reflects only the filtered results.
    • The "of X available" part always displays the total number of records in the dataset, regardless of filters.
  • Right side: contains the pagination controls.

    • Users can navigate pages by clicking a page number or using the arrows.
    • A single arrow moves one page forward or backward.
    • A double arrow jumps directly to the first or last page.

Additionally, to the right of the pagination, there is a dropdown menu that lets users change how many items are displayed per page. The available options are fully customizable.

An example of the pagination and record count:

Pagination and record count example

Caution

Avoid allowing very high numbers of items per page, as this may reduce performance.



4.8 Copy cell content

This feature is enabled by default and can be configured per table.

It allows users to press and hold on a cell to copy its raw content directly to the clipboard.

You can also customize:

  • The duration of the press before the copy action is triggered.
  • Or disable the feature entirely if it is not needed.



4.9 Dynamic height

Enabled by default, this feature automatically adjusts the maximum height of the table to fit its container.

The vertical scroll bar will appear inside the table to navigate records on the current page, while the header and paginator remain visible at all times.



4.10 Deferred startup

By default, when you access a page containing an ECS PrimeNG table, the table automatically loads its configuration and data.

This behavior can be deferred if needed. For example, in any of these scenarios:

  • The user must perform an action before retrieving data.
  • Some data needs to be fetched first to populate predefined filters.
  • Any other scenario you might have.

Once ready, the table can be manually updated through external calls.



4.11 Changing the data endpoint dinamically

It is possible to change the data source of a table while it is in use, without needing to reload or download the entire table configuration again.

This feature is especially useful in scenarios where:

  • The columns (and even the views) remain the same.
  • You only need to adjust where the table fetches its data from.

A common example is when you apply a higher-level filter.

For instance, switching between different clients: the table keeps the same structure, but the data source changes so you can view information for the selected client across the whole application.



4.12 Excel report

With minimal setup, you can allow users to export table data through an interactive menu with multiple export options.

If enabled, an Excel icon will appear at the top right of the table. Clicking it opens a modal window like this:

Excel report example

Users can customize the export with the following options:

  • Report filename: Can be prefilled with a name (default: "Report"). You can also prevent users from changing it.
  • Include timestamp: By default enabled. Adds the current time in the format _{year}{month}{day}_{hours}{minutes}{seconds}_UTC to the filename. Users can disabled it if they want to.
  • Export columns: Choose whether to export only visible columns or all columns (default: visible only).
  • Filters to apply: Decide whether table filters should apply to the export (default: not applied). If the Selected rows option is enabled and not set to "All rows," this option becomes mandatory applying the current filters.
  • Sorts to apply: Include table sorting in the export (default: not applied).
  • Selected rows: Appears only if the table has the row checkbox selector enabled. Users can export selected rows, unselected rows, or all rows regardless of selection.

Once satisfied with the configuration, users can click Export to generate the Excel file, which will be automatically downloaded to their device.



4.13 Views

As seen in previous sections, users have many options to customize how data is displayed in the table.

Sometimes users want to save all these customizations so they don’t have to remember or reapply them each time.

The ECS PrimeNG Table provides this feature through "Views".

Views are saved per table key and user. To enable this, you just need to:

  • Assign a unique key to each table that should support views.
  • Choose how views are stored. Available options:
    • Session storage: Views are kept only during the session. Closing the browser tab or browser will remove the views.
    • Local storage: Views are stored locally in the browser. They persist longer but can be lost if the user clears browser data or switches devices.
    • Database storage: The most versatile option. Views are stored in a database, allowing users to keep them permanently and access them across different browsers or devices. Requires additional setup.

Caution

Table keys must be unique when using views. Otherwise, views from one table could appear in another, causing errors in your application.

If views are enabled, users will see the following menu in the middle of the table header:

Views top menu

This menu shows the currently applied view:

  • A text showing "---Select a view---" displays the name of the currently applied view (if a view is being applied).
  • The replay button reapplies the last configuration of the selected view.
  • The eraser button resets the table to its original state, as if no views were applied.

When the text of the menu is pressed, the following modal is shown:

Views menu

In the modal, users can manage table views and create new ones. For each view, the available options are:

  • Load on startup
  • Apply the view
  • Update the view
  • Change the view alias
  • Delete the view

Rules and limitations of views:

  • There can't be two views in the same table with the same alias.
  • By default, a table can have up to 10 views (configurable if needed).
  • Load on startup: If checked for a view, it will automatically apply the next time the table loads. Only one view can have this enabled at a time.





5 Feature-to-Code mapping

The purpose of this section is to provide a table that maps the features described earlier to their corresponding technical implementation. Use the table below as a reference to perform this mapping:

Scope Functional feature Technical implementation
Table 4.1 Planning your table 6.1 Understanding the basics
Table 4.2 Date formatting 6.2 Configuring date formats
Columns 4.3.1 Data type 6.3.1 Choosing the appropriate data type
Columns 4.3.2 Visibility 6.3.2 Configuring visibility and order
Columns 4.3.3 Horizontal and vertical alignment 6.3.3 Horizontal and vertical alignment
Columns 4.3.4 Overflow behaviour 6.3.4 Overflow behaviour
Columns 4.3.5 Column properties menu 6.3.5 Column properties menu
Columns 4.3.6 Resize 6.3.6 Resize
Columns 4.3.7 Reorder 6.3.7 Reorder
Columns 4.3.8 Frozen 6.3.8 Frozen
Columns 4.3.9 Descriptions 6.3.9 Descriptions
Columns 4.3.10 Cell tooltip 6.3.10 Cell tooltip
Columns 4.3.11 Sorting 6.3.11 Sorting
Columns 4.3.12 Filtering 6.3.12 Filtering
Columns 4.3.13 Predefined filters 6.3.13 Predefined filters
Rows 4.4.1 Single select 6.4.1 Single select
Rows 4.4.2 Checkbox select 6.4.2 Checkbox select
Rows 4.4.3 Dynamic styling 6.4.3 Dynamic styling
Table 4.5 Action buttons 6.5 Setting up row and header action buttons
Table 4.6 Global filter 6.6 Configuring the global filter
Table 4.7 Pagination and record count 6.7 Pagination properties
Table 4.8 Copy cell content 6.8 Copy cell content
Table 4.9 Dynamic height 6.9 Dynamic height
Table 4.10 Deferred startup 6.10 Deferred startup
Table 4.11 Changing the data endpoint dinamically 6.11 Changing the data endpoint dinamically
Excel report 4.12 Excel report 6.12 Configuring Excel reports
Views 4.13 Views 6.13 Setting up views





6 Technical overview

The goal of this section is to provide a technical overview of the ECS PrimeNG table. It dives into the configuration options and implementation details, giving developers a clear understanding of how the table works under the hood. This section helps you grasp the mechanics, and integrate the table efficiently into your projects.



Recommended architecture

When building endpoints that involve business logic, data access, or complex operations, it is recommended to follow a layered architecture to promote separation of concerns, testability, and maintainability.

A typical structure (and the one used in the example project) could look like this:

Controller
└─> IService (Interface)
    └─> Service (Implementation)
        └─> IRepository (Interface)
            └─> Repository (Implementation, Data Access)

Explanation of each layer:

  • Controller:

    • Handles HTTP requests from the front-end.
    • Validates input parameters.
    • Calls the corresponding service interface method.
  • IService (Interface):

    • Defines the contract for your service.
    • Ensures consistency and makes it easier to mock or replace the service in unit tests.
  • Service (Implementation):

    • Implements the business logic.
    • Receives requests from the controller and transforms them into repository queries.
    • Handles additional logic such as mapping DTOs, filtering, sorting, and pagination.
  • IRepository (Interface):

    • Defines the contract for data access operations.
    • Provides abstraction over the underlying data source (e.g., EF Core, external APIs).
    • Makes it easier to mock or swap implementations in unit tests.
  • Repository:

    • Directly interacts with the database or other data sources.
    • Executes queries and returns raw data.
    • Keeps data access logic separated from business logic for maintainability.

Tip

This layered approach ensures separation of concerns, testability, and scalability.
The Controller only orchestrates requests, the Service handles business rules, and the Repository deals with raw data. Separating layers into different projects is optional but recommended for larger applications.
This structure promotes clean architecture and makes it easier to scale or replace parts independently.



6.1 Understanding the basics

For every ECS PrimeNG table you want to use, even when displaying simple data without additional features like custom buttons or specific data types (e.g., date, list), the following minimum setup is required:

  • Backend:

    • DTO: A Data Transfer Object decorated with a special attribute that specifies the parameters used to configure the table columns.
    • Endpoints: Two endpoints are required. One provides the table configuration, and the other retrieves paginated data while applying all sorting and filtering rules.
  • Frontend:

    • Component: Import the ECSPrimengTable in the component that will display the table.
    • Template: Use the <ecs-primeng-table> element in the component's HTML and configure the minimal required properties.

In this section, we will focus on creating a table that displays simple data without row actions or other advanced features.



6.1.1 Backend

6.1.1.1 Setting up the DTO

First, you always need a DTO that represents the full table, including all possible columns, both internal-use columns and the columns displayed to the user (columns that are always visible or optionally hideable).

Your DTO must include a RowID of type Guid with this exact name, as many of the table's advanced features rely on it (e.g., the row selector). The values in this column should be unique.

Here is an example DTO with a RowID and three basic data types: text, numeric, and boolean:

public class TestDto {
	[ColumnAttributes(sendColumnAttributes: false)]
	public Guid RowID { get; set; }

	[ColumnAttributes("Username")]
	public string Username { get; set; } = string.Empty;

	[ColumnAttributes("Money", dataType: DataType.Numeric)]
	public decimal Money { get; set; }

	[ColumnAttributes("Has a house", dataType: DataType.Boolean)]
	public bool House { get; set; }
}

From the above DTO, we have the basics for a table with three visible columns and a fourth column (RowID) that is hidden from end-users but available in the front-end fo us.

Some important points about this example DTO:

  • Every property intended as a column has a ColumnAttributes decorator. This signals to ECS PrimeNG table that it should include this property as a column. Properties without this decorator are ignored and will not appear in the table.
  • The RowID column has sendColumnAttributes set to false, ensuring its data is always available in the front-end while remaining hidden in the table. Users cannot toggle its visibility. This option can be applied to any column you want to keep hidden from users but accessible in the front-end.
  • The first parameter of ColumnAttributes specifies the column name displayed in the front-end. In this example, the visible columns will be "Username", "Money", and "Has a house".
  • It is important to correctly handle nullable and non-nullable properties to ensure proper mapping. For instance, if "Username" cannot be null in your dataset, declare it as string and initialize it with string.Empty. If it can be null, use string? without assigning a default value.
  • By default, columns are treated as Text. To use other data types, specify the dataType parameter in ColumnAttributes.
  • The order of the properties in the class determines their left-to-right order in the final table on the front-end. Exceptions are frozen columns: those set to the left appear before all other columns, and those set to the right appear at the end of the table.

Note

The possible data types are:

  • Text: For string values.
  • Numeric: For numbers like int, long, decimal, etc.
  • Boolean: For bool values.
  • Date: For DateTime values. Additional customization options are explained in later chapters.
  • List: A specialized Text type used with predefined filters to represent data separated by ;, including tags, icons, images, or text.

Important

Always include a RowID property in your class with a ColumnAttributes decorator and sendColumnAttributes set to false. This column is required by the table for rendering performance and for advanced features. Ensure that the values in this column are unique.

Tip

All properties intended to be table columns should have a ColumnAttributes decorator. This tells ECS PrimeNG table to include them as columns. Properties without this decorator are ignored and will not appear in the table.

Caution

Avoid adding a property named Selector in your class, especially if you plan to use the row selection feature. This name is reserved as a virtual column used internally by the table, and using it can cause conflicts.



6.1.1.2 Setting up the endpoints
Creating the table configuration endpoint

The first required endpoint is the table configuration endpoint, which provides the minimum configuration needed for the table to work. This endpoint should be a GET method.

Together with the data endpoint, it forms the core setup required to get the table running. Additional endpoints and logic can be added later as needed.

This endpoint should return a TableConfigurationModel, which can be obtained by calling in your service:

EcsPrimengTableService.GetTableConfiguration<T>();

Where T is your DTO class.

The GetTableConfiguration method automatically inspects the ColumnAttributes defined in the DTO (T) and builds a TableConfigurationModel containing:

  • Column definitions: Metadata about each column (name, type, visibility, order, frozen state, etc.).
  • Allowed items per page: The pagination options available for the table.
  • Date format: The default format for displaying date values.
  • Timezone: The timezone used for date/time rendering.
  • Culture: The culture used for numeric and date formatting.
  • Max allowed views: The maximum number of views a user can save.

By default, the following values are applied if no overrides are provided:

internal class TableConfigurationDefaults {
    public static readonly int[] AllowedItemsPerPage = [10, 25, 50];
    public static readonly string DateFormat = "dd-MMM-yyyy HH:mm:ss zzzz";
    public static readonly string DateTimezone = "+00:00";
    public static readonly string DateCulture = "en-US";
    public static readonly byte MaxViews = 10;
}

Example

Below is a minimal working example showing how to implement the table configuration endpoint and its corresponding service assuming that you use the TableConfigurationDefaults. There is no need for the repository since there is no data access needed in this endpoint.

The table configuration endpoint in your controller might look like this:

[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase {
    private readonly ITestService _service;

    public TestController(ITestService service) {
        _service = service;
    }

    [HttpGet("[action]")]
    public IActionResult GetTableConfiguration() {
        try {
            return Ok(_service.GetTableConfiguration()); // Delegates the configuration retrieval to the service
        } catch (Exception ex) {
            return StatusCode(StatusCodes.Status500InternalServerError, 
                $"An unexpected error occurred: {ex.Message}");
        }
    }
}

The GetTableConfiguration from your service might look like this (minimal service definition):

using ECSPrimengTable.Services;
using ECSPrimengTableExample.DTOs;
using ECSPrimengTableExample.Interfaces;

namespace ECSPrimengTableExample.Services {
    public class TestService : ITestService {

        public TableConfigurationModel GetTableConfiguration() {
            return EcsPrimengTableService.GetTableConfiguration<TestDto>();
        }
    }
}

Tip

Replace TestDto with the DTO used for your table. The service will automatically read its column definitions and build the configuration accordingly.



Creating the table data endpoint

The second required endpoint is the table data endpoint, which provides the minimum data needed to populate the table. This endpoint should be a POST method.

This endpoint, together with the table configuration endpoint, forms the core setup required for the table to function. It handles data retrieval, filtering, sorting, and pagination, ensuring that the table displays the correct rows based on user interaction and query parameters. Additional endpoints or business logic can be added later as needed.

This endpoint should return a TablePagedResponseModel, which can be obtained by calling in your service:

EcsPrimengTableService.PerformDynamicQuery(inputData, baseQuery);

Where:

  • inputData is a TableQueryRequestModel sent by the ECS PrimeNG table in the request body.
  • baseQuery is an IQueryable built on a DTO class decorated with ColumnAttributes.
    This must be the same DTO used in the table configuration endpoint (example from previous section).

The PerformDynamicQuery method executes a sequence of operations (detailed below) and returns a TablePagedResponseModel containing:

  • Current page: The page number where the user is currently located.
  • Total records: The total number of records after filters are applied.
  • Unfiltered total records: The total number of records before any filters are applied.
  • Data: The actual page content, represented as dynamic data.

The EcsPrimengTableService.PerformDynamicQuery() method performs the following steps in order:

  1. Sorting: Applies the sorting rules defined in the TableQueryRequestModel:
    • If rules are provided, they are applied.
    • If no rules are provided and a defaultSortColumnName is specified, that default sort will be applied.
    • If neither is provided, no sorting is performed.
  2. Count before filtering: Delegates a COUNT operation to the database engine to determine the total number of records before filters are applied.
  3. Global filter: If specified in the TableQueryRequestModel, applies the global filter to all eligible columns.
  4. Column filters: Applies all per-column filter rules from the TableQueryRequestModel, by adding them to the IQueryable.
  5. Count after filtering: Delegates a COUNT operation to the database engine to determine the total number of records after filters are applied.
  6. Pagination check: Calculates the total number of pages based on items per page and filters. If the current page exceeds the available page count (e.g., user was on page 100 but filters reduce the dataset to 7 pages), the current page is adjusted to the last available page and the frontend then handles moving the user accordingly.
  7. Dynamic select: Projects only the required columns by adding a SELECT to the IQueryable.
    • The query is then materialized using ToDynamicList(), delegating execution to the database.
    • The result is a list containing only the requested columns, including apart from the requested, those that have the sendColumnAttributes set to false.
  8. Return result: Finally, returns a TablePagedResponseModel with the processed data to the frontend.

Important

Some important aspects to allways consider are as follows:

  • Validation: Always validate the page size and requested columns before calling PerformDynamicQuery, using ValidateItemsPerPageAndCols.
  • Performance: Ensure that the IQueryable uses AsNoTracking() since no entity tracking is needed. This also improves performance.
  • Reusability: Define a private method in the service that builds the base IQueryable. This allows reusing the same query for both the table data endpoint and features like Excel export, ensuring data consistency.

Example

Below is a simplified setup showing how to implement the table data endpoint, its service, and repository.

The table data endpoint in your controller might look like this:

[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase {
    private readonly ITestService _service;

    public TestController(ITestService service) {
        _service = service;
    }

    [HttpPost("[action]")]
    public IActionResult GetTableData([FromBody] TableQueryRequestModel inputData) {
        try {
            (bool success, TablePagedResponseModel data) = _service.GetTableData(inputData);
            if(!success) {
                return BadRequest("Invalid items per page");
            }
            return Ok(data);
        } catch (Exception ex) {
            return StatusCode(StatusCodes.Status500InternalServerError, 
                $"An unexpected error occurred: {ex.Message}");
        }
    }
}

The GetTableData method in your service can be implemented like this (minimal example). It includes a private GetBaseQuery method that centralizes the base query logic, allowing you to reuse it for features such as Excel export, ensuring consistency across endpoints. This service implementation also makes use of a repository to access the underlying data:

using ECSPrimengTable.Services;
using ECSPrimengTableExample.DTOs;
using ECSPrimengTableExample.Interfaces;

namespace ECSPrimengTableExample.Services {
    public class TestService : ITestService {
        private readonly ITestRepository _repo;

        public TestService(ITestRepository repository) {
            _repo = repository;
        }

        public (bool success, TablePagedResponseModel data) GetTableData(TableQueryRequestModel inputData) {
            if(!EcsPrimengTableService.ValidateItemsPerPageAndCols(inputData.PageSize, inputData.Columns)) { // Validate the items per page size and columns
                return (false, null!);
            }
            return (true,EcsPrimengTableService.PerformDynamicQuery(inputData, GetBaseQuery());
        }

        private IQueryable<TestDto> GetBaseQuery() {
            return _repo.GetTableData()
                .Select(u => new TestDto {
                    RowID = u.Id,
                    Username = u.Username,
                    Money = u.Money,
                    House = u.House
                });
        }
    }
}

The repository that your service accesses could look like this:

using System.Linq;
using Microsoft.EntityFrameworkCore;

namespace ECSPrimengTableExample.Repository {
    public class TestRepository {

        private readonly primengTableReusableComponentContext _context;

        public TestRepository(primengTableReusableComponentContext context) {
            _context = context;
        }

        public IQueryable<TestTable> GetTableData() {
            return _context.TestTables
                   .AsNoTracking();
        }
    }
}

With this setup, your table data endpoint is fully functional and ready to integrate with the ECS PrimeNG table frontend.



6.1.2 Frontend

Assuming you have completed all the steps in the setup section and you are using standalone components in your frontend, this section will guide you on how to implement a basic ECS PrimeNG table that simply displays data.

In your desired component TypeScript file, a minimal definition should look like this (assuming the component is named Home):

import { Component } from '@angular/core';
import { ECSPrimengTable, ITableOptions, createTableOptions } from '@eternalcodestudio/primeng-table';

@Component({
  selector: 'ecs-home',
  standalone: true,
  imports: [
    ECSPrimengTable
  ],
  templateUrl: './home.html'
})
export class Home {
  tableOptions: ITableOptions = createTableOptions({
    urlTableConfiguration: "Test/GetTableConfiguration",
    urlTableData: "Test/GetTableData"
  });
}

Caution

It is important that the variable you use as tableOptions is initialized using createTableOptions. This ensures that the table is created with the necessary base configuration, preventing errors or unexpected behaviour.

In this component, we only define the paths to your API endpoints. It is assumed that the ECS PrimeNG table component has already been provided with the base URL of your API in the http service injection.

In your component's HTML, the table can be displayed as follows:

<ecs-primeng-table [tableOptions]="tableOptions"/>

You only need to pass the tableOptions property.

Once you start up your API and serve the frontend, you should be able to see the table rendered on the page if everything is set up correctly.

Additionally, you can subscribe to table lifecycle events to detect when a data fetch has been completed. This is useful, for example, when the user changes the page, applies a filter, or triggers any action that requires reloading the table data if you need to listen to it.

  • onDataEndUpdate: Emitted after the table has finished fetching and updating its data. This event does not provide any payload (type: void).

An example of this subscription could be:

In your desired component TypeScript file:

import { Component } from '@angular/core';
import { ECSPrimengTable, ITableOptions, createTableOptions } from '@eternalcodestudio/primeng-table';

@Component({
  selector: 'ecs-home',
  standalone: true,
  imports: [
    ECSPrimengTable
  ],
  templateUrl: './home.html'
})
export class Home {
  tableOptions: ITableOptions = createTableOptions({
    urlTableConfiguration: "Test/GetTableConfiguration",
    urlTableData: "Test/GetTableData"
  });

  onDataUpdated(): void {
    console.log("Table data has been refreshed.");
    // Add your custom logic here (e.g., update UI state, log analytics, etc.)
  }
}

In your HTML:

<ecs-primeng-table [tableOptions]="tableOptions" (onDataEndUpdate)="onDataUpdated()"/>



6.2 Configuring date formats

In your service where EcsPrimengTableService.GetTableConfiguration is called, you can pass additional parameters to customize how dates are displayed.

The ECS PrimeNG table will automatically handle the date conversion, and the configurations you define in the backend will be reflected in the frontend when rendering table cells.

For maximum flexibility, it is recommended to store the user’s preferences for:

  • Date format: A string, e.g. "dd-MMM-yyyy HH:mm:ss zzzz"
  • Date timezone: A string, e.g. "+00:00"
  • Date culture: A string, e.g. "en-US"

Let’s assume that these values are stored in a variable named userPreferences and that you are working with a TestDto.

An example service implementation could look like this:

using ECSPrimengTable.Services;
using ECSPrimengTableExample.DTOs;
using ECSPrimengTableExample.Interfaces;

namespace ECSPrimengTableExample.Services {
    public class TestService : ITestService {

        public TableConfigurationModel GetTableConfiguration() {
            var userPreferences = getUserDataFromDatabase(); // Get the user preferences from the database
            return EcsPrimengTableService.GetTableConfiguration<TestDto>(null, userPreferences.dateFormat,
                userPreferences.dateTimezone, userPreferences.dateCulture);
        }
    }
}

Note

If no arguments are specified, the defaults format for dates are:

  • Date format: "dd-MMM-yyyy HH:mm:ss zzzz"
  • Date timezone: "+00:00"
  • Date culture: "en-US"



6.3 Columns

6.3.1 Choosing the appropriate data type

By default, all columns in your table are treated as text. However, as described in the related functional documentation, there are five main data types available.

Choosing the correct data type is important because:

  • It influences how cells are rendered in the table.
  • It affects how certain ECS PrimeNG table features behave (e.g., filtering, sorting, formatting).

Additionally, it is crucial to declare whether a column is nullable. If this is not correctly defined, dynamic queries may fail, causing your table data endpoint to break in certain scenarios.

An enum named DataType is available, which defines the five possible data types you can associate with a column.

To set the data type, simply define it in the dataType property of the ColumnAttributes applied to your DTO properties.

Example

Consider a DTO with:

  • A row identifier (GUID)
  • Two strings (one nullable, one not)
  • A numeric value
  • A nullable date
  • A boolean

The DTO declaration would look like this:

public class TestDto {
	[ColumnAttributes(sendColumnAttributes: false)]
	public Guid RowID { get; set; }

	[ColumnAttributes("Username")]
	public string Username { get; set; } = string.Empty;

	[ColumnAttributes("Alias")]
	public string? Alias { get; set; }

	[ColumnAttributes("Money", dataType: DataType.Numeric)]
	public decimal Money { get; set; }

	[ColumnAttributes("Birthday", dataType: DataType.Date)]
	public DateTime? Birthday { get; set; }

	[ColumnAttributes("Has a house", dataType: DataType.Boolean)]
	public bool House { get; set; }
}

Important

Always select the appropriate DataType for your columns to ensure correct rendering and reliable query handling.

Caution

If a column can contain null values, you must explicitly mark the property as nullable in C# by appending ? after the type.



6.3.2 Configuring visibility and order

Visibility

By default, all columns are visible. You can control column visibility using the ColumnAttributes of your DTO by configuring the following options:

  • sendColumnAttributes

    • Use this when you need a column for frontend functionality but don’t want it visible to the user.
    • If set to false, the column will always remain hidden, and all other visibility options (canBeHidden, startHidden) will be ignored.
    • Example use case: internal IDs, keys, or technical references required by the application logic.
  • canBeHidden

    • Determines whether the user can toggle the visibility of the column in the column editor menu.
    • Default: true (users can toggle the visibility).
    • If set to false, the column will always remain visible, and the user cannot hide it.
  • startHidden

    • Defines whether the column is visible when the table first loads.
    • Default: false (the column is visible initially).
    • If set to true, the column will start as hidden, but the user can make it visible through the column editor menu.
    • Important: if canBeHidden is false, the startHidden option is ignored, and the column will always be visible.



Order

By default, columns are displayed in the frontend following the order of their declaration in your DTO class:

  • The first property in the class corresponds to the leftmost column.
  • The last property corresponds to the rightmost column.

There are two exceptions to this rule:

  1. Frozen columns: These can be pinned to the left or right side of the table, and they will always remain fixed in that position regardless of DTO order.
  2. Selector and row action columns (explained in later sections):
    • If frozen, they remain at the extreme sides of the table.
    • If not frozen, they are positioned at the sides, directly before any frozen columns.



6.3.3 Horizontal and vertical alignment

By default, all columns are aligned horizontally at Center and vertically at Middle, which means that the content of each cell is displayed at its center.

Users are allowed to change both the horizontal and vertical alignment through the column editor menu by default.

If different initial alignments are required, or if user customization must be restricted, the ColumnAttributes of the DTO provides the following configuration options:

  • Initial alignment

    • dataAlignHorizontal: Uses the DataAlignHorizontal enum. Default value is Center. Possible values are Left, Center and Right.
    • dataAlignVertical: Uses the DataAlignVertical enum. Default value is Middle. Possible values are Top, Middle and Bottom.
  • Restricting alignment changes

    • dataAlignHorizontalAllowUserEdit: By default set to true. If set to false, the user cannot change the horizontal alignment of the column in the editor menu.
    • dataAlignVerticalAllowUserEdit: By default set to true. If set to false, the user cannot change the vertical alignment of the column in the editor menu.



6.3.4 Overflow behaviour

All columns come with a default cell overflow behaviour of Hidden.

By default, users are allowed to change the overflow behaviour of a column through the column properties menu.

If you need a different default behaviour, or if you want to prevent users from modifying it, the ColumnAttributes of your DTO provides the following options:

  • cellOverflowBehaviour: Defines how the content of the cell is handled when it exceeds the column width. It uses the CellOverflowBehaviour enum, which has two possible values:

    • Hidden (default): exceeding the width of the column will be truncated and not displayed.
    • Wrap: Excess content is wrapped onto new lines, allowing the row to expand vertically as needed to fit all content.
  • cellOverflowBehaviourAllowUserEdit: Controls whether users can change the column’s overflow behaviour through the column editor menu. Default is true. Set it to false to prevent users from modifying this setting.

Note

When the column's data type is boolean, the cellOverflowBehaviour setting will be automatically overridden to Hidden.



6.3.5 Column properties menu

As described in the functional documentation for this section, the table includes a button in the top-left corner by default. When clicked, this button opens the Column properties menu.

You can customize the column properties menu behavior through the following table options:

  • selectorEnabled: (default: true) Controls the visibility of the top-left button.

    • true: Button is visible and users can open the column properties menu.
    • false: Button is hidden.
  • selectorIcon: (default: PrimeNG icon pi pi-pen-to-square) Specifies the icon displayed on the button. You can replace it with any icon from PrimeNG or other libraries such as Font Awesome or Material Icons.



6.3.6 Resize

By default, all columns are resizable. The only exception is frozen columns, which cannot be resized.

To prevent users from resizing a specific non-frozen column, you can use the following option in the column's ColumnAttributes within the DTO:

  • canBeResized: Defaults to true. If set to true, the column can be resized by the user. Set to false to disable resizing for that column.

Additionally, there is a property called initialWidth that can be used to set a column's initial width in pixels.



6.3.7 Reorder

By default, all columns are reorderable, except for frozen columns which cannot be moved.

To prevent users from reordering a specific non-frozen column, use the following option in the column's ColumnAttributes within the DTO:

  • canBeReordered: Defaults to true. When true, the user can drag and drop the column to change its position. Set to false to disable this functionality for that column.



6.3.8 Frozen

Enabling a frozen column in your table that follows horizontal scrolling is straightforward.

In your column's ColumnAttributes within the DTO, the following properties determine the behavior of the frozen column:

  • frozenColumnAlign: An enum of type FrozenColumnAlign, defaulting to None. The available values are:
    • None: The column will not be frozen and will not follow horizontal scrolling.
    • Left: The column is frozen to the left, remaining visible on the left side while the table scrolls horizontally.
    • Right: The column is frozen to the right, remaining visible on the right side while the table scrolls horizontally.

When a column has a frozenColumnAlign value other than None, the following rules apply:

  • canBeResized is automatically overridden to false. Frozen columns cannot be resized.
  • canBeReordered is automatically overridden to false. Frozen columns cannot be reordered.
  • initialWidth is set to 100px by default when freezing the column. You can adjust this value if needed by specifying an initialWidth greater than 0 to override this default value.



6.3.9 Descriptions

If you want a column to include a description, it must be defined in the backend within your DTO class by modifying the column's ColumnAttributes. The relevant property is:

  • columnDescription: Defaults to an empty string. If a value is provided, an info icon will appear in the column header. When the user hovers over this icon, a tooltip will be displayed showing the specified description in the frontend.



6.3.10 Cell tooltip

By default, all cells (except those with a boolean data type) display a tooltip with their value when the user hovers over the cell.

If you want to disable this behavior, or display a tooltip based on a different column, you can configure the following properties in the ColumnAttributes of your DTO class:

  • dataTooltipShow: Defaults to true. When set to false, no tooltip will be displayed when the user hovers over a cell in that column.
  • dataTooltipCustomColumnSource: Defaults to an empty string. Allows you to map the tooltip content to another column.

Using dataTooltipCustomColumnSource to reference a column with sendColumnAttributes set to false can be particularly useful, since this ensures data is always available in the frontend for tooltips, even if it is not directly displayed in the table.

Important

The column referenced in dataTooltipCustomColumnSource must have the same name as the property in the class. The first character may be uppercase or lowercase, since it will automatically be converted to lowercase for the frontend.



6.3.11 Sorting

All columns in the table (except for the actions column and the row selector column) can be sorted by the user.

To disable sorting for a specific column, configure it in the backend by modifying the column's ColumnAttributes in your DTO class:

  • canBeSorted: Defaults to true. When set to false, the column cannot be sorted by the user.

You can also define a default sorting order that will be applied when the user has not set any sort.

To do so, you must create two lists of the same length:

  • A list of column names (as strings), where each name matches the property name of your DTO class.
  • A list of sort orders (using the ColumnSort enum provided by the ECS PrimeNG table) corresponding to each column.

Both lists must then be passed as arguments to EcsPrimengTableService.PerformDynamicQuery in your service and the sorting will be applied in the order of your lists.

An example of a service applying default sorting to Age and EmploymentStatusName, one in descending order and the other one in ascending order would look like this:

using ECSPrimengTable.Services;
using ECS.PrimengTable.Enums;
using ECSPrimengTableExample.DTOs;
using ECSPrimengTableExample.Interfaces;

namespace ECSPrimengTableExample.Services {
    public class TestService : ITestService {
        private readonly ITestRepository _repo;

        private readonly List<string> columnsToOrderByDefault = ["Age", "EmploymentStatusName"];
        private readonly List<ColumnSort> columnsToOrderByOrderDefault = [ColumnSort.Descending, ColumnSort.Ascending];

        public TestService(ITestRepository repository) {
            _repo = repository;
        }

        public (bool success, TablePagedResponseModel data) GetTableData(TableQueryRequestModel inputData) {
            if(!EcsPrimengTableService.ValidateItemsPerPageAndCols(inputData.PageSize, inputData.Columns)) { // Validate the items per page size and columns
                return (false, null!);
            }
            return (true,EcsPrimengTableService.PerformDynamicQuery(inputData, GetBaseQuery(), null, columnsToOrderByDefault, columnsToOrderByOrderDefault);
        }

        private IQueryable<TestDto> GetBaseQuery() {
            return _repo.GetTableData()
                .Select(u => new TestDto {
                    RowID = u.Id,
                    Username = u.Username,
                    Money = u.Money,
                    House = u.House
                });
        }
    }
}

Warning

Both default sort lists must have the same length.

In the frontend, you have two configuration options related to sorting. Inside the header entry of your component’s variable that holds the ITableOptions, the following properties are available:

  • clearSortsEnabled: Defaults to true. When set to false, the clear sorts button will be hidden.
  • clearSortsIcon: Allows customization of the clear sorts button icon. By default, it uses pi pi-sort-alt-slash from the PrimeNG icon library. You may use any other icons from PrimeNG or third-party providers such as Material Icons or Font Awesome.



6.3.12 Filtering

All columns in the table (except for the actions column) can be filtered by the user.

To disable filtering for a specific column, configure it in the backend using the column's ColumnAttributes:

  • canBeFiltered: Defaults to true. When set to false, the filter option will not be available for that column.

In the frontend, two configuration options related to filtering are available under the header entry of your component’s variable that holds the ITableOptions:

  • clearFiltersEnabled: Defaults to true. When set to false, the clear filters button will be hidden.
  • clearFiltersIcon: Allows customization of the clear filters button icon. By default, it uses pi pi-filter-slash from the PrimeNG icon library. Other icons from PrimeNG or third-party libraries (e.g., Material Icons, Font Awesome) may also be used.

Tip

The filter menu displayed depends on the data type configured for the property in your DTO class on the backend.



6.3.13 Predefined filters

Caution

Do not use this feature on columns that may contain a large number of distinct values, as it could lead to performance issues. This feature is intended for columns with a small, limited set of values.

In some scenarios, you may want to restrict the available filter options for a column to a predefined list of possible values.

There are two strategies for defining predefined filters:

  • Hardcoding the list in the frontend: Suitable when you have a small, fixed list of values (e.g., "Open", "Closed").
  • Fetching the list from the backend: The frontend can call an endpoint before loading the table. The table supports a deferred startup process, ensuring it doesn’t attempt to load data until these values are ready (see the Deferred startup section).

Regardless of the strategy, predefined filters must first be defined in the TypeScript of your component. To do this, create a dictionary and provide the required number of predefined lists to be used.

Note

If a column can contain null values, you do not need to add null to the IPredefinedFilter array. The table will simply render no option for null values.

Note

The tooltip for a predefined value will display the content of its value property.

Note

If you are using the data type List, it will render each possible value separated with ";".

Tip

A single element of the IPredefinedFilter array can use multiple representations at the same time (for example, combining an icon with text). You can also mix different representations within the same array: one value could be displayed with an icon, while another could be shown as a tag.

Example

Managing two predefined filter lists in the same table (assuming the component is named Home and is a standalone component):

import { Component } from '@angular/core';
import { ECSPrimengTable, ITableOptions, createTableOptions, IPredefinedFilter } from '@eternalcodestudio/primeng-table';

@Component({
  selector: 'ecs-home',
  standalone: true,
  imports: [
    ECSPrimengTable
  ],
  templateUrl: './home.html'
})
export class Home {
  listOfPredifinedValues1: IPredefinedFilter[] = [];
  listOfPredifinedValues2: IPredefinedFilter[] = [];
  myPredifinedFiltersCollection: { [key: string]: IPredefinedFilter[] } = {
    'nameOfList1': this.listOfPredifinedValues1,
    'nameOfList2': this.listOfPredifinedValues2
  };

  tableOptions: ITableOptions = createTableOptions({
    urlTableConfiguration: "Test/GetTableConfiguration",
    urlTableData: "Test/GetTableData",
    predefinedFilters: this.myPredifinedFiltersCollection
  });
}

At this point, the table has two predefined filter lists available: nameOfList1 and nameOfList2.

To associate columns with the predefined lists, configure them in the DTO using ColumnAttributes:

public class TestDto {
	[ColumnAttributes(sendColumnAttributes: false)]
	public Guid RowID { get; set; }

	[ColumnAttributes("Example column 1", filterPredefinedValuesName: "nameOfList1", ...)]
	public string? Column1 { get; set; }

	[ColumnAttributes("Example column 2", filterPredefinedValuesName: "nameOfList2", ...)]
	public string? Column2 { get; set; }
}

The value of filterPredefinedValuesName must match the dictionary key created in the frontend component.

Once this is done, predefined filters will work as soon as you populate them with data.

For the table to match cell values with predefined filter options, the value returned by the backend for a cell must match the value property of one of the items in the IPredefinedFilter array.

The next sections describe the different representations of a predefined filter, which must be configured when populating the IPredefinedFilter list.

If filtering is enabled in a column where a predefined filter has been configured, when the user presses the filter button a modal will appear showing the available options. The user can select one or more values, and a search bar will also be available for convenience.



Plain text

To display an element as plain text in a predefined filter, you need to define in each IPredefinedFilter entry at least these properties:

  • value: Must match the underlying value of the cell, so that the table can map it properly.
  • name: The text shown in the cell.
  • displayName: Set to true so the value in name is actually displayed.

Example

Suppose you have the following possible values in a column that you wish to represent as plain text:

  • Ok
  • Warning
  • Critical

Your IPredefinedFilter list in TypeScript could look like this:

examplePredfinedFilter: IPredefinedFilter[] = [
    {
        value: "backendValueForOK",
        name: "OK",
        displayName: true
    }, {
        value: "backendValueForWarning",
        name: "Warning",
        displayName: true
    }, {
        value: "backendValueForCritical",
        name: "Critical",
        displayName: true
    }
];

Important

It is recommended in this scenario that in the IPredefinedFilter array, the properties value and name contain the same text. This ensures that the global filter works as expected, since the UI displays the name but the global filter internally uses the value.



Tag

To display an element as a tag in a predefined filter, you need to define in each IPredefinedFilter entry at least these properties:

  • value: Must match the underlying value of the cell, so that the table can map it properly.
  • name: The text shown in the tag.
  • displayTag: Set to true so the a tag is displayed containing as text name.
  • tagStyle (optional): If you want to apply a style to the tag, like for example, changing its color.

Example

Suppose you have the following possible values in a column that you wish to represent in a tag with the following colors:

  • Ok → green
  • Warning → orange
  • Critical → red

Your IPredefinedFilter list in TypeScript could look like this:

examplePredfinedFilter: IPredefinedFilter[] = [
    {
        value: "backendValueForOK",
        name: "OK",
        displayTag: true,
        tagStyle: {
            background: 'rgb(0, 255, 0)'
        }
    }, {
        value: "backendValueForWarning",
        name: "Warning",
        displayTag: true,
        tagStyle: {
            background: 'rgb(255, 130, 30)'
        }
    }, {
        value: "backendValueForCritical",
        name: "Critical",
        displayTag: true,
        tagStyle: {
            background: 'rgb(255, 0, 0)'
        }
    }
];

Important

It is recommended in this scenario that in the IPredefinedFilter array, the properties value and name contain the same text. This ensures that the global filter works as expected, since the UI displays the name but the global filter internally uses the value.



Icon

To display an element as an icon in a predefined filter, you need to define in each IPredefinedFilter entry at least these properties:

  • value: Must match the underlying value of the cell, so that the table can map it properly.
  • icon: Specifies the icon to display. You can use icons from PrimeNG or other libraries, such as Font Awesome or Material Icons.
  • iconColor (optional): Defines the color of the icon.
  • iconStyle (optional): Allows you to specify additional CSS styles for the icon, such as font size.

Example

Suppose you have the following possible values in a column that you wish to represent with an icon with the following options:

  • Ok → Uses pi-check with color green.
  • Warning → Uses pi-exclamation-triangle with color orange.
  • Critical → Uses pi-times with color red and a font size of size 1.5 rem.

Your IPredefinedFilter list in TypeScript could look like this:

examplePredfinedFilter: IPredefinedFilter[] = [
    {
        value: "backendValueForOK",
        icon: "pi pi-check",
        iconColor: "green"
    }, {
        value: "backendValueForWarning",
        icon: "pi pi-exclamation-triangle",
        iconColor: "orange"
    }, {
        value: "backendValueForCritical",
        icon: "pi pi-times",
        iconColor: "red",
        iconStyle: "font-size: 1.5rem"
    }
];

Tip

If you are using a PrimeNG icon, you can add pi-spin to make it spin (e.g., pi pi-spin pi-spinner). Note that in newer versions of PrimeNG, the spinning effect may not appear if animations are disabled in the OS or browser.

Important

If a predefined filter displays only icons, it is recommended to disable the global filter for that column in your DTO class in your backend. This prevents the global filter from attempting to filter by a column without text, which could be confusing for users.



Image

There are three ways to display an image in a predefined filter:

  • Provide the image directly through a URL.
  • Pass the image as a Blob.
  • Pass an endpoint from which the image Blob can be fetched.

We will now cover the three different methods.

Important

If a predefined filter displays only images, it is recommended to disable the global filter for that column in your DTO class in your backend. This prevents the global filter from attempting to filter by a column without text, which could be confusing for users.


Using a URL

To fetch an image from a URL, you need to define in each IPredefinedFilter entry at least these properties:

  • value: Must match the underlying value of the cell, so that the table can map it properly.
  • imageURL: The URL from which the image will be retrieved.

Example

Suppose a column can have the following states, each represented by an image:

  • Ok → https://somesite.com/imageOk.png
  • Warning → https://somesite.com/imageWarning.png
  • Critical → https://somesite.com/imageCritical.png

Your IPredefinedFilter list in TypeScript could look like this:

examplePredfinedFilter: IPredefinedFilter[] = [
    {
        value: "backendValueForOK",
        imageURL: "https://somesite.com/imageOk.png"
    }, {
        value: "backendValueForWarning",
        imageURL: "https://somesite.com/imageWarning.png"
    }, {
        value: "backendValueForCritical",
        imageURL: "https://somesite.com/imageCritical.png"
    }
];

Using a Blob

To display an image from a Blob provided directly to the frontend, you need to define in each IPredefinedFilter entry at least these properties:

  • value: Must match the underlying value of the cell, so that the table can map it properly.
  • imageBlob: A valid image Blob (e.g., PNG, JPEG) to be displayed.

Example

Suppose a column can have the following states, each represented by a Blob variable:

  • Ok → blobOk
  • Warning → blobWarning
  • Critical → blobCritical

Your IPredefinedFilter list in TypeScript could look like this:

examplePredfinedFilter: IPredefinedFilter[] = [
    {
        value: "backendValueForOK",
        imageBlob: blobOk
    }, {
        value: "backendValueForWarning",
        imageBlob: blobWarning
    }, {
        value: "backendValueForCritical",
        imageBlob: blobCritical
    }
];

Fetching a Blob from an Endpoint

The ECS PrimeNG table supports fetching images automatically from an endpoint that returns a valid image Blob. Once configured, the component will request the blob, handle the response, and display the image without additional setup.

To achieve this, define the following properties in each IPredefinedFilter entry:

  • value: Must match the underlying value of the cell so the table can map it correctly.
  • imageBlobSourceEndpoint: The backend endpoint from which the image blob will be fetched.

When the fetch process starts, a skeleton placeholder will be shown until the blob is retrieved. If the request succeeds, the table will automatically populate the imageBlob property of the corresponding IPredefinedFilter entry with the retrieved Blob.

If the request fails, the property imageBlobFetchError will be set to true for that entry, allowing you to detect and handle errors gracefully.

Example

Suppose a column can take the following states, each associated with the following endpoints to fetch an image blob:

  • Ok → https://somesite.com/api/getBlob/ok
  • Warning → https://somesite.com/api/getBlob/warning
  • Critical → https://somesite.com/api/getBlob/critical

Your IPredefinedFilter list in TypeScript could look like this:

examplePredfinedFilter: IPredefinedFilter[] = [
    {
        value: "backendValueForOK",
        imageBlobSourceEndpoint: "https://somesite.com/api/getBlob/ok"
    }, {
        value: "backendValueForWarning",
        imageBlobSourceEndpoint: "https://somesite.com/api/getBlob/warning"
    }, {
        value: "backendValueForCritical",
        imageBlobSourceEndpoint: "https://somesite.com/api/getBlob/critical"
    }
];



6.4 Rows

6.4.1 Single select

The single row selection feature uses the RowID column, which must be defined in the DTO class used with your table.

By default, this feature is disabled. To enable it, configure it from the frontend. In your component's ITableOptions configuration, inside the rows property, you can use the singleSelector object with the following properties:

  • enabled (Default: false): If set to true, users can click a row to select it. You can then subscribe to selection events to execute custom actions.
  • metakey (Default: true): When true, users must hold CTRL and click on a selected row to unselect it. When false, users can unselect a row simply by clicking it again.

You can subscribe to changes in row selection using the following event emitters:

  • onRowSelect: Triggered when a row is selected.
  • onRowUnselect: Triggered when a row is unselected.

Both emitters provide an object with the following structure:

  • rowID: The unique row identifier, provided by the backend through the RowID property in the DTO class.
  • rowData: The raw row data, containing all the columns currently available on the frontend.

Note

On mobile devices (phones or tablets), the CTRL key configuration is ignored. Users can unselect a previously selected row by simply clicking it, as mobile devices do not have a CTRL key.

Caution

If the default behavior (holding down CTRL to unselect) is enabled, repeatedly clicking the same row without holding CTRL will count as multiple selections.
This can cause unintended behavior if actions are triggered on each selection, so plan your row actions accordingly.

Example

For enabling the single row selector and subscribing to changes, in your desired component TypeScript file, a minimal definition should look like this (assuming the component is named Home):

import { Component } from '@angular/core';
import { ECSPrimengTable, ITableOptions, createTableOptions } from '@eternalcodestudio/primeng-table';

@Component({
  selector: 'ecs-home',
  standalone: true,
  imports: [
    ECSPrimengTable
  ],
  templateUrl: './home.html'
})
export class Home {
  tableOptions: ITableOptions = createTableOptions({
    urlTableConfiguration: "Test/GetTableConfiguration",
    urlTableData: "Test/GetTableData",
    rows: {
      singleSelector: {
        enabled: true,
        // metakey: false // Uncomment to allow unselecting rows by clicking them directly
      }
    }
  });

  onRowSelect(event: { rowID: any, rowData: any }){
    // You can access the event.rowID or event.rowData here of the selected row
    console.log("Row selected:", event.rowID, event.rowData);
  }
  onRowUnselect(event: { rowID: any, rowData: any }){
    // You can access the event.rowID or event.rowData here of the unselected row
    console.log("Row unselected:", event.rowID, event.rowData);
  }
}

And in your HTML:

<ecs-primeng-table [tableOptions]="tableOptions" (onRowSelect)="onRowSelect($event)" (onRowUnselect)="onRowUnselect($event)"/>



6.4.2 Checkbox select

The checkbox row selection feature relies on the RowID column, which must be defined in the DTO class used with your table.

By default, this feature is disabled. To enable it, configure it from the frontend.

In your component's ITableOptions configuration, inside the rows property, use the checkboxSelector object with the following options:

  • enabled (Default: false): If true, a new column with checkboxes will be displayed. Users can select or unselect rows using these checkboxes. Additionally an option to filter by this column will be enabled.
  • header (Default: "Selected"): The header label for the checkbox selection column.
  • alignmentRight (Default: false): If true, the column will appear on the right side of the table. Otherwise, it will appear on the left.
  • width (Default: 150): The fixed column width in pixels.
  • frozen (Default: true): If true, the column remains visible when horizontally scrolling the table.
  • resizable (Default: false): If true, users can resize the column.

You can subscribe to changes in row checkbox selection using:

  • onRowCheckboxChange: Triggered whenever a row checkbox is selected or unselected. The emitted object has the following structure:
    • rowID: The unique row identifier, provided by the backend via the RowID property in the DTO class.
    • selected: true if the row is selected, false if unselected.

At any time, you can access the component’s selectedRowsCheckbox property, which contains an array of currently selected rows (rowID).

Note

When the checkbox selection column is aligned to the left, it will always appear after the action column (if the action column is also aligned left).

When aligned to the right, it will always appear before the action column (if the action column is visible and aligned right).

This behavior is consistent only if both action and checkbox row selector columns are frozen at the same time(or if they are unfrozen at the same time).

Example

To enable the checkbox row selector, subscribe to selection changes, and access the table's selectedRowsCheckbox property, your component TypeScript file can have a minimal setup like this (assuming the component is named Home):

import { Component } from '@angular/core';
import { ECSPrimengTable, ITableOptions, createTableOptions } from '@eternalcodestudio/primeng-table';

@Component({
  selector: 'ecs-home',
  standalone: true,
  imports: [
    ECSPrimengTable
  ],
  templateUrl: './home.html'
})
export class Home {
  @ViewChild('dt') dt!: ECSPrimengTable; // Get the reference to the object table

  tableOptions: ITableOptions = createTableOptions({
    urlTableConfiguration: "Test/GetTableConfiguration",
    urlTableData: "Test/GetTableData",
    rows: {
      checkboxSelector: {
        enabled: true,
        // header: "Selected", // Uncomment to change header
        // alignmentRight: false, // Uncomment to change the location of the column
        // width: 150, // Uncomment to change column width in px
        // frozen: true, // Uncomment to change frozen status
        // resizable: false // Uncomment to change column resize behaviour
      }
    }
  });

  onRowCheckboxChange(event: { rowID: any, selected: boolean }){
    // You can access the event.rowID or event.selected here of the selected row
    console.log("Row checkbox change:", event.rowID, event.selected);
    // Also you could access the array of selected rowID
    console.log("Row checkbox currently selected:", this.dt.selectedRowsCheckbox);
  }
}

And in your HTML (note the #dt template reference to access the table instance from TypeScript):

<ecs-primeng-table #dt [tableOptions]="tableOptions" (onRowCheckboxChange)="onRowCheckboxChange($event)"/>

With this setup:

  • The #dt template reference allows you to access the table component instance directly in TypeScript. You can use it to read the selectedRowsCheckbox array at any time.
  • The (onRowCheckboxChange) event binding ensures that your onRowCheckboxChange method is called whenever a row checkbox is selected or unselected, giving you access to the rowID and selected of the affected row.



6.4.3 Dynamic styling

The dynamic styling feature allows you to customize the appearance of rows either by applying inline styles or by adding CSS classes.

Both approaches receive the rowData object as input, giving you access to all the values currently held in the row. This enables you to define rules based on column values and dynamically adjust the styling.

In your component's ITableOptions configuration, inside the rows property, you can define:

  • style: A function that returns an object containing inline CSS styles to apply when a condition is met.
  • class: A function that returns one or more CSS class names to be injected when a condition is met.

Both style and class functions are evaluated dynamically and can be updated at runtime, allowing styling rules to react to data changes.

Note

The rowData passed to the style and class functions contains the data currently available in the frontend for the specific row being processed.

Example

Assume you have a column of type List, where values are stored as a semicolon-separated string (e.g., "Full-time; Remote; Contract"). You want to apply:

  • A specific inline style if the list contains the value "Full-time".
  • A specific CSS class if the list contains the value "Unemployed".

Your component TypeScript file could be defined as follows (assuming the component is named Home):

import { Component } from '@angular/core';
import { ECSPrimengTable, ITableOptions, createTableOptions } from '@eternalcodestudio/primeng-table';

@Component({
  selector: 'ecs-home',
  standalone: true,
  imports: [
    ECSPrimengTable
  ],
  templateUrl: './home.html'
})
export class Home {
  tableOptions: ITableOptions = createTableOptions({
    urlTableConfiguration: "Test/GetTableConfiguration",
    urlTableData: "Test/GetTableData",
    rows: {
      style: (rowData: any) => {
        const list = rowData?.employmentStatusNameList?.split(';').map((s: string) => s.trim()) || [];
        if (list.includes("Full-time")) {
          return { fontWeight: 'bold', fontStyle: 'italic' };
        }
        return {};
      },
      class: (rowData: any) => {
        const classes = [];
        const list = rowData?.employmentStatusNameList?.split(';').map((s: string) => s.trim()) || [];
        if(list.includes("Unemployed")){
          classes.push('exampleClass');
        }
        return classes;
      }
    }
  });
}

And in your HTML:

<ecs-primeng-table [tableOptions]="tableOptions"/>

Because the class is pushing the exampleClass, you have to declare the CSS class at a component or global level so it can be rendered properly. Assuming you define it at a global level, your styles.scss could look like this:

.exampleClass {
  background-color: #ffcccc !important;
  color: #990000 !important;
  font-weight: bold !important;
}



6.5 Setting up row and header action buttons

Button definitions

Action buttons can be used either in the table header or as row action buttons. Both types of buttons share the same set of properties.

The main difference is that row action buttons receive the rowData object, which contains the data currently held in the frontend for that specific row.

The available properties are:

  • icon: Optional. The icon to display on the button. Should be a valid icon name from PrimeNG, Material Icons, Font Awesome, or similar libraries.
  • iconPos: Optional. The position of the icon relative to the button label. Defaults to "left". Possible values: "left", "right", "top", "bottom".
  • label: Optional. The text label displayed on the button.
  • rounded: Optional. If true, the button will be round. Defaults to false.
  • raised: Optional. If true, adds a shadow to indicate elevation. Defaults to false.
  • variant: Optional. Specifies the variant of the button. Can be null (default), "text", or "outlined".
  • color: Optional. The CSS class to apply for button styling. Example: "p-button-success" or "custom-class".
  • style: Optional. Additional inline CSS styles for the button.
  • condition: Optional. A function that determines whether the button should be displayed for a given row.
    • rowData parameter: The row data object (null for header buttons).
    • Returns true if the button should be visible; false otherwise.
  • conditionFailHide: Optional. Controls behavior when condition returns false.
    • If true, the button will be hidden when the condition is not met.
    • If false or undefined, the button will remain visible but disabled.
  • action: Optional. The action to execute when the button is clicked.
    • rowData parameter: The row data object of the clicked row (null for header buttons).
  • tooltip: Optional. Tooltip text to display when the user hovers over the button.

The recommended approach is to define two separate arrays of ITableButton: one for header action buttons and one for row action buttons.

  • Header action buttons:
    The array of ITableButton should be assigned to the buttons property inside the header object of your ITableOptions configuration.
    These buttons are displayed in the table header and typically trigger actions that are not specific to a single row (e.g., creating a new record).

  • Row action buttons:
    The array of ITableButton should be assigned to the buttons property inside the actions object of the rows object in your ITableOptions configuration. These buttons are displayed for each row and can access the rowData of the corresponding row. They usually trigger actions that operate on that specific row (e.g., edit, delete). It is recommended to use the rowID when performing any backend actions on that record.

Tip

Buttons added to the ITableButton array and passed to the table are always rendered from left to right. The first button in the array appears at the far left, while the last button appears at the far right.

Important

For row action buttons, if you depend on any element of the row data, remember that only data from hidden columns and columns that cannot be hidden by the user is guaranteed to be available. Do not rely on data from user-hideable columns, as it may not always be accessible in the frontend.

Caution

Never assume that a button visible to the user can be safely executed based on frontend conditions alone. Always perform a final validation on the backend, because any data or state exposed in the frontend can be easily tampered with.

Example

Assume you want to have both header and row action buttons:

  • Header action buttons:
    • A button to add a new record.
  • Row action buttons:
    • A button to delete a record (only available to authorized users).
    • A button to edit a record.

Your component TypeScript file could be defined as follows (assuming the component is named Home):

import { Component } from '@angular/core';
import { ECSPrimengTable, ITableOptions, createTableOptions, ITableButton } from '@eternalcodestudio/primeng-table';

@Component({
  selector: 'ecs-home',
  standalone: true,
  imports: [
    ECSPrimengTable
  ],
  templateUrl: './home.html'
})
export class Home {
  headerActionButtons: ITableButton[] = [
    {
      icon: 'pi pi-plus',
      color: 'p-button-success',
      action: () => {
        // Action to execute when clicked.
        // Example: Open a modal to create a new record.
      },
      label: "CREATE",
      tooltip: "Create new record"
    }
  ];
  rowActionButtons: ITableButton[] = [
    {
      icon: 'pi pi-trash',
      tooltip: 'Delete record',
      color: 'p-button-danger',
      action: (rowData) => {
        // Action to execute when clicked, only if condition evaluates to true.
        // Example: Open a confirmation modal before deleting the record.
        // Use rowData.rowID to identify the record in the backend.
      },
      condition: (rowData) => (rowData.canBeDeleted === true)
    }, {
      icon: 'pi pi-file-edit',
      tooltip: 'Edit record',
      color: 'p-button-primary',
      action: (rowData) => {
        // Action to execute when clicked.
        // Example: Open a modal to edit the record.
        // Use rowData.rowID to identify the record in the backend.
      }
    }
  ];

  tableOptions: ITableOptions = createTableOptions({
    urlTableConfiguration: "Test/GetTableConfiguration",
    urlTableData: "Test/GetTableData",
    header: {
      buttons: this.headerActionButtons
    },
    rows: {
      action: {
        buttons: this.rowActionButtons
      }
    }
  });
}

And in your HTML:

<ecs-primeng-table [tableOptions]="tableOptions"/>



Row actions column

If at least one row action button is provided, an additional column will be added to the table to display these buttons.

Some properties of this column can be customized via the actions object inside the rows object of your ITableOptions configuration. The available options are:

  • header (Default: "Actions"): The header label for the row actions column.
  • alignmentRight (Default: true): If true, the column will appear on the right side of the table. Otherwise, it will appear on the left.
  • width (Default: 150): The fixed column width in pixels.
  • frozen (Default: true): If true, the column remains visible when horizontally scrolling the table.
  • resizable (Default: false): If true, users can resize the column.

Note

When the row actions column is aligned to the left, it will always appear at the beginning of the table.

When aligned to the right, it will always appear at the end of the table.



6.6 Configuring the global filter

WIP

Take into account that the global filter is one of the most costly operations launched to the database engine, since basically it performs a LIKE = '%VALUE_PROVIDED_BY_USER%' to each column.



6.7 Pagination properties

The ECS PrimeNG table manages pagination automatically. There is no need for additional frontend configuration.

The only customization available is defined in the backend, where you specify which page sizes are allowed for the user.

This is done in the method EcsPrimengTableService.GetTableConfiguration.

Example

The following minimal service configuration allows 10, 20, 30, 40 and 50 items per page:

using ECSPrimengTable.Services;
using ECSPrimengTableExample.DTOs;
using ECSPrimengTableExample.Interfaces;

namespace ECSPrimengTableExample.Services {
    public class TestService : ITestService {

        public static readonly int[] AllowedItemsPerPage = [10, 20, 30, 40, 50];

        public TableConfigurationModel GetTableConfiguration() {
            return EcsPrimengTableService.GetTableConfiguration<TestDto>(AllowedItemsPerPage);
        }
    }
}



6.8 Copy cell content

By default, the copy cell content feature is enabled.

When a user holds down the mouse on a cell for a certain duration, the cell’s content is automatically copied to the clipboard.

You can adjust this behavior or disable it completely via the ITableOptions configuration in your frontend component.

  • copyToClipboardTime: Defines the number of seconds the user must hold the mouse button on a cell before its content is copied. Set to <= 0 to turn off this feature entirely.

Example

For setting the copy cell content to copy after holding the mouse down for 0.8 seconds, in your desired component TypeScript file, a minimal definition should look like this (assuming the component is named Home):

import { Component } from '@angular/core';
import { ECSPrimengTable, ITableOptions, createTableOptions } from '@eternalcodestudio/primeng-table';

@Component({
  selector: 'ecs-home',
  standalone: true,
  imports: [
    ECSPrimengTable
  ],
  templateUrl: './home.html'
})
export class Home {
  tableOptions: ITableOptions = createTableOptions({
    urlTableConfiguration: "Test/GetTableConfiguration",
    urlTableData: "Test/GetTableData",
    copyToClipboardTime: 0.8
  });
}



6.9 Dynamic height

WIP



6.10 Deferred startup

You can defer the initialization of the table by setting the isActive property to false in your ITableOptions configuration.

The isActive flag controls whether the table should fetch its column configuration and data:

  • true (default): the table automatically fetches configuration and data when the component starts.
  • false: the table will not perform any requests until you explicitly activate it again using the function updateData() of the table.

This is particularly useful if you want to delay table loading until specific conditions are met, such as retrieving some initial data that's required before the table can be shown.

Example

Assume that you want to prevent your table from fetching its configuration and data on component startup. Your component TypeScript file could be defined as follows (assuming the component is named Home):

import { Component } from '@angular/core';
import { ECSPrimengTable, ITableOptions, createTableOptions } from '@eternalcodestudio/primeng-table';

@Component({
  selector: 'ecs-home',
  standalone: true,
  imports: [
    ECSPrimengTable
  ],
  templateUrl: './home.html'
})
export class Home {
  tableOptions: ITableOptions = createTableOptions({
    urlTableConfiguration: "Test/GetTableConfiguration",
    urlTableData: "Test/GetTableData",
    isActive: false
  });
}

And in your HTML:

<ecs-primeng-table [tableOptions]="tableOptions"/>



6.11 Changing the data endpoint dinamically

You can dynamically change the data source endpoint of your table by temporarily disabling it with isActive and then updating the urlTableData property in your ITableOptions configuration. To do so:

  1. Set isActive to false to prevent the table from updating.
  2. Update the urlTableData property with the new endpoint.
  3. Wait at least one Angular cycle before reactivating the table (to allow change detection to propagate).
  4. Finally, call updateData() on the table instance to fetch data from the new endpoint.

Important

  • If the table has already been initialized, changing urlTableConfiguration will have no effect. The ECS PrimeNG table only fetches configuration once during its first load.
  • This feature is intended for scenarios where the column configuration stays the same and only the data source changes.

Example

Assume that you want to update the data endpoint from your component.

If your ITableOptions variable is named tableOptions and you are using #dt as the template reference for the table, you could do the following:

updateTableEndpoint(newEndpoint: string){
    this.tableOptions.isActive = false;
    this.tableOptions.urlTableData = newEndpoint;
    setTimeout(() => {
        this.dt.updateData();
    }, 1);
}

You can also reset filters and sorts while updating the endpoint if desired:

updateTableEndpoint(newEndpoint: string){
    this.tableOptions.isActive = false;
    this.tableOptions.urlTableData = newEndpoint;
    this.dt.clearFilters(this.dt, true); // Clear all active filters
    this.dt.clearSorts(this.dt, true); // Clear all active sorts
    setTimeout(() => {
        this.dt.updateData();
    }, 1);
}



6.12 Configuring Excel reports

WIP



6.13 Setting up views

WIP





7 Component reference

WIP





8 Editing ECS PrimeNG table and integrating locally

WIP




4.2 Date formating

From the setup steps for implementing this reusable component, you might remember that there you had to created the database function 04 FormatDateWithCulture.txt. This is actually not needed, since its only use is for being able to use the golbal filter functionality on columns that have the date type. The global filter tries to search things as a string, so this function makes a conversion of your date to a format that matches the date as you are showing it to the user in the frontend, taking into account the date format, timezone offset and culture that you wish to use. The database function needs to be exposed in the backend (as explained in previous sections) so that when the global filter is used, this function can be called with no issues. If for any reasons you were unable to use this function, the global filtered can be disabled in the date type columns to avoid errors when filtering.

4.9 Global filter

The global filter is enabled by default in all columns of your table, except for bool data types and the actions column were this filter will be never applied.

When the user writes a value in the global filter text box, after a brief delay of the user not changing the value, a filter rule will be launched to the table were basically, the global filter will try to filter each individual column perfoming a LIKE '%VALUE_INTRODUCED_BY_USER%', which basically means that any match of that value introduced by the user (doesn't matter in which position of the cell) will be returned. When a value is written to the global filter, at the left of the text box an "X" icon will appear, that when pressed by the user, it will clear the global filter.

Additionally, as seen in the previous image, the global filter will underline with yellow each part of the cell were the value that is introduced by the user matches.

As in the column filter feature, the user has also the option to clear all filters by pressing the clear filters button when it is enabled (it is enabled if at least a column filter, a predifined filter or a global filter is active).

If for any reason, you want to hide the global filter search bar, you can do so by in in your component HTML that is using the table, setting the variable "globalSearchEnabled" to false.

<ecs-primeng-table #dt
    ...
    [globalSearchEnabled]="false"
    ...>
</ecs-primeng-table>

The properties that you can modify in the HTML related to the global filter are the following:

  • globalSearchEnabled (boolean): By default true. If true, the global search text box will be shown in the top right of your table. If false, it won't be shown.
  • globalSearchMaxLength (number): By default 50. The maximun number of characters that the user can introduce in the global search text box.
  • globalSearchPlaceholder (string): by default "Search keyword". This is a placeholder text shown when the user hasn't introduced any value to the global filter yet.

If you wish for a column to ignore the global filter, you can do so by modifying your DTO in the back-end. For the specific column that you wish to ignore the global filter, in the "PrimeNGAttribute" you just have to give a value of false to "canBeGlobalFiltered" as shown in the next example:

[PrimeNGAttribute("Example column", canBeGlobalFiltered: false, ...)]
public string? ExampleColumn { get; set; }

By doing this, the column won't take into account any global filters that should be applied to it. For the bool data type or columns that are hidden (or that have the "sendColumnAttributes" to false), this property is always false and they will never be affected by the global filter.

Note

The global filter search is case insensitive.

Important

The global filter is very useful for users, but if has a downside. Since it performs a LIKE query per column (with the % at the start and at the end) which is one of the heaviest filters to perform in SQL, the more columns that there are shown at a given time (and that can be global filtered), the more time it will require to update the data shown when the global filter is updated.

Caution

For date data types to work properly using the global filter, you need to have properly setup the database function 04 FormatDateWithCulture.sql explained in previous sections and you also need to have permission of function execution in your database for the user that is going to execute the query. This is needed because said function, transforms the date to a string that can be filtered by. If for any reason you don't want dates to be global filtered, for each individual column that is of type date, you must set in the DTO the "canBeGlobalFiltered" to "false".

About

Angular + PrimeNG table with advanced filters, delegating all logic to a .NET (ASP.NET) backend. Designed for SQL Server, easily adaptable to other databases.

Topics

Resources

License

Stars

Watchers

Forks