Introduction
Many Angular grid tools are available from either commercial markets or open-sources. Most of these tools, however, are Javascript API type based, with which developers don't have much flexibility to code the own HTML templates and data binding options for the data grid. For my projects, especially migrating from AngularJS with ngTable to Angular, I need a grid tool that can be initiated from the top-level attribute directive of the table template, similar to the legacy ngTable's scenario and template layout, but entirely in the Angular world. Thus, I created my own data grid, the NgExTable, to display a list of data records with data searching, sorting, and pagination capabilities. As the Angular has been evolved to higher versions, the sample demo application has also been upgraded to using the latest Angular 11 CLI. If you need the source code files with the previous Angular versions, please see the details in the History section by end of the article.
The NgExTable has these features:
- Using basic HTML code, not the JavaScript API, for the
table
tag, thus easy to define any own table attributes, styles, and Angular pipes. - Simple and straightforward data binding to
tr
and td
tags. - Setting column sorting by
ColumnSortComponent
in the th
tag with changeable sorting icons. - Multiple-column sorting feature ready but this article and sample application only present the general data grid workflow with the single-column sorting demo (please see the details of multiple-column sorting implementation and descriptions in the article, Multiple Column Sorting: from Angular NgExTable to Source Data List Management).
- Flexible and customizable pagination components and display.
- More flexible coding and less code lines in the grid tool consumer-side (see the table-hosting components client-paging.component.ts, server-paging.component.ts, and companion HTML templates for details).
- Configurable table parameters, especially for those related to searching, sorting, and page-sizing commands.
- The base library code is within a single folder for easy copying and direct use by any project even on different platforms. Any parts of the code can easily be modified as needed.
Below are all files listed for the NgExTable library and demo project folders:
Set Up and Run Sample Application
The downloaded sources contain two different Visual Studio solution/project types. Please pick up one or both you would like and do the setup on your local machine. You also need the node.js (recommended version 14.x LTS or above) and Angular CLI (recommended version 11.x or above) installed globally on the local machine. Please check the node.js and Angular CLI documents for details.
You may check the available versions of the TypeScript for Visual Studio in the C:\Program Files (x86)\Microsoft SDKs\TypeScript folder. Both ASP.NET and Core types of the sample application set the version of TypeScript for Visual Studio to 4.0 in the TypeScriptToolsVersion
node of SM.NgExTable.Web.csproj file. If you don't have the version 4.0 installed, download the installation package from the Microsoft site or install the Visual Studio 2019 version 16.8.x which includes the TypeScript 4.0.
NgExTable_AspNetCore_Cli
-
You need to use the Visual Studio 2019 (version 16.8.x) on the local machine. The .NET Core 5.0 SDK is included in the Visual Studio installation.
-
Download and unzip the source code file to your local work space.
-
Go to physical location of your local work space, double click the npm_install.bat and ng_build.bat (or ng_build_local.bat if not installing the Angular CLI globally) files sequentially under the SM.NgExTable.Web\AppDev folder.
NOTE: The ng build
command may need to be executed every time after making any change in the TypeScript/JavaScript code, whereas the execution of npm install
is just needed whenever there is any update with the node module packages. I do not enable the CLI/Webpack hot module replacement since it could break source code mapping for the debugging in the Visual Studio.
-
Open the solution with the Visual Studio 2019, and rebuild the solution with the Visual Studio.
-
Click the IIS Express toolbar command (or press F5) to start the sample application.
NgExTable_AspNet_Cli
-
Download and unzip the source code file to your local work space.
-
Go to physical location of your local work space, double click the npm_install.bat and ng_build.bat (or ng_build_local.bat if not installing the Angular CLI globally) files sequentially under the SM.NgExTable.Web\ClientApp folder (also see the same NOTE for setting up the NgTable_AspNetCore_Cli project).
-
Open the solution with Visual Studio 2017 or 2019 and rebuild the solution with the Visual Studio.
-
Click the IIS Express toolbar command (or press F5) to start the sample application.
The NgTable_Ng11_Scripts_AnyPlatform.zip file contains only the client script source code folders and files. If you use the systems other than the Microsoft platform, you can copy the pure Angular source code files to your own website project, perform the necessary settings using the npm
and Angular CLI commands, and then run the sample application on your systems.
The first browser screen of the sample application looks like this:
You don't have to set up the server data source from the data-providing service application for the Server-side Pagination demo. By default, the sample application uses the server-mock-data.service.ts to simulate the server-side data request and response patterns and results. This also benefits the audiences who add the NgExTable_Scripts_AnyPlatform
code files into their website project and run for the NgExTable example on their non-Microsoft platforms.
If you run the sample application with Visual Studio and would like to call the real server data source for the Server-side Pagination demo, I recommend performing these steps:
-
Go to my other article, ASP.NET Core: A Multi-Layer Data Service Application Migrated from ASP.NET Web API and set up the Core API data services following the instructions. Any version of the data service application is a compatible data source provider.
-
Start the data service application with the Visual Studio and keep the running solution with the IIS Express on the background.
-
In the ../app/NgExTableDemo/app.config.ts file, change the ServerPagingDataSource
from mock
to server
.
export const ServerPagingDataSource:
string = 'server';
-
Edit and enable the active service URL if it's not correct for your settings.
export const WebApiRootUrl: string = "http://localhost:7200/api/";
-
Rebuild the Angular CLI by executing the ng_build.bat or ng_build_local.bat.
-
Press F5 to run the NgExTable demo application, select the Server-side Pagination left menu item, and click the Go button on the Search Products panel.
Table Structures and Working Mechanisms
Most other Angular data grids or tables use the table-level and column-level components based on the Angular component tree structures. However, this implementation approach doesn’t seem flexible for the development of web applications. It limits many custom options that can directly be added into the HTML elements, such as attributes, styles, additional elements in the columns, and even Angular pipes for data binding td
tags.
The NgExTable itself is not a top-level Angular component. Instead, it serves as a service that provides add-on services, directives, and unit-processing components. It also relies on events to respond to the user actions and send the parameter data back to the parent table-hosting components. In the sample application, either the ClientPagingComponent
or ServerPagingComponent
is a table-hosting component.
-
The main coding structures related to the base table include the table-main-params
attribute directive for the table
tag, ColumnSort
component (column-sort
tag) for the th
tag, and tableChanged
event triggered from the TableMainDirective
class. Both client-paging.component.html and server-paging.component.html files (table-hosting views) of the sample application show the example of using the directive, component, and events in the HTML template.
<table class="table table-condensed table-striped bottom-border top-space"
[table-main-params]="pagingParams" (tableChanged)="onChangeTable($event)">
<thead>
<tr>
<th>Product Name<column-sort sortBy="ProductName"></column-sort></th>
<th>Category ID<column-sort sortBy="CategoryId"></column-sort></th>
<th>Category Name<column-sort sortBy="CategoryName"></column-sort></th>
<th>Unit Price<column-sort sortBy="UnitPrice"></column-sort></th>
<th>Status<column-sort sortBy="StatusDescription"></column-sort></th>
<th>Available Since<column-sort sortBy="AvailableSince"></column-sort></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of rows; let last = last">
<td>{{item.ProductName}}</td>
<td align="center">{{item.CategoryId}}</td>
<td>{{item.CategoryName}}</td>
<td>{{item.UnitPrice | currency:"USD":true:"1.2-2"}}</td>
<td>{{item.StatusDescription}}</td>
<td>{{item.AvailableSince | date:"MM/dd/yyyy"}}</td>
</tr>
</tbody>
</table>
-
The most important object to pass values back and forth for paging, sorting, and even related to data filtering is the pagingParams
. The object definition is defined in the model-interface.ts:
export interface PagingParams {
pageSize: number;
pageNumber: number;
sortList: Array<SortItem>;
changeType: any;
}
The instance of the PagingParams
is initiated in the ngOnInit
method of the table-hosting component with seeding values. The object instance is then updated from multiple places, including the table-hosting component and its child structures TableMainDirective
and PaginationComponent
, in response to user request actions on the page.
this.pagingParams = {
pageSize: pageSize !== undefined ? pageSize : 10,
pageNumber: 1,
sortList: Array<SortItem>;
changeType: TableChange.init
}
The changeType
values are defined in the TableChange
enum
and updated for corresponding process status.
export const enum TableChange {
init,
search,
pageNumber,
pageSize,
sorting
}
-
The onChangeTable
method in the table-hosting component receives the changes in the pagingParams
, performs some tasks, and then calls to get the filtered, sorted, and paginated data items that will be loaded into the table.
onChangeTable(params: PagingParams ):any {
this.pagingParams = params;
- - -
- - -
}
-
The sortBy
value for the colulmn-sort
tag is passed to the ColumnSortComponent
class in which the sorted column definition and CSS icon are set. The sortBy
and sortDirection
values in the sortItem
element of pagingParams.sortList
array object that has been updated in response to user request actions are also sent back to the table-hosting component relayed by the sortChanged
method and tableChanged
events in the TableMainDirective
class sequentially (see the details in the column-sort.component.ts and table-main.directives.ts if you are interested in the deep levels of the code logic).
-
For any column that should be non-sortable, the column-sort
tag can simply be omitted in the th
tag.
Pagination Component
The pager is a separate component with its view template consisting of detailed HTML elements. It uses the similar workflow as the main table, i.e., passing the pagingParams
object instance as the @Input
to the PaginationComponent
class and sending changed object instance back via the pageChanged
event. The code in the table-hosting view is simple and straightforward:
<pagination *ngIf="pagingEnabled"
[pagingParams]="pagingParams"
[pagedLength]="pagedLength"
(pageChanged)="onChangeTable($event)">
</pagination>
The pageChanged
event is routed to the same target method, onChangeTable
, as the tableChanged
event for the main table due to possible linked code logic among the page number, size, and sorting changes. You can use a different method, such as onChangePage
, for the pager if you would like.
The page number is changed and number button highlighted from two triggering sources.
-
Clicking on any page number. This will directly call the selectPage
method in the PaginationComponent
. The code workflow is all within the pager so that you usually don’t have to care for it in the client calling logic.
In view template:
(click)="selectPage(page.number, $event)"
In PaginationComponent
class:
selectPage(pageNumber: number, event?: Event): void {
if (event) {
this.pagingParams.changeType = TableChange.pageNumber;
}
this.pagingParams.pageNumber = pageNumber;
this.pages = this.getPages();
if (params.changeType == TableChange.pageNumber ||
params.changeType == TableChange.init) {
this.pageChanged.emit(params);
}
}
-
Other operations, such as data filtering, sorting, or page-size changes. The page number should passively be reset and correct number button highlighted. You need to add code into the table-hosting component and execute the code in the corresponding event methods, such as onTableChange
, or in the processing logic after the data access. For example, the below code block in the table-hosting component calls the selectPage
method in the PaginationComponent
through a method of TableMainDirective
for updating the pager (also see details in the later section, Reset Pager Routines):
this.tableMainDirective.updatePagerAfterData(this.pagingParams, this.totalLength);
The change in page size is another user action on the pager. When selecting the number from the page-size dropdown, the onSizeChange
event is triggered and corresponding processes are followed.
In the select
tag of the pagination.component.html template:
(ngModelChange)="onSizeChange($event)">
In PaginationComponent
class:
onSizeChange(event: any) {
this.pagingParams.pageSize = event.value;
this.pagingParams.changeType = TableChange.pageSize;
- - -
this.pageChanged.emit(params);
}
You also don’t have to touch the code within the PaginationComponent
. In the pageChanged
target method of the table-hosting component, however, you may need to call the setPagerForSizeChange
method from the PaginationComponent
in addition to the data access process using the changed page-size value (see the method in the pagination.component.ts file for details if interested).
if (this.pagingParams.changeType == TableChange.pageSize) {
this.paginationComponent.setPagerForSizeChange();
}
The built-in pager HTML template is included in the NgExTable folder. If you would like to update it with different layout and styles, you can directly edit the built-in template or replace it with your own one in the existing NgExTable folder.
Search Component
Strictly speaking, the search component is an application unit separate from the NgExTable
. I did not directly add the filtering feature into the columns since there are limited options when using the in-grid-column data filtering. I'd rather implement a search engine with the Angular component code. The SearchComponent
class and its HTML view template in the sample application demonstrate how the structures and functionalities link to the NgExTable and perform the data filtering for the paginated grid display.
-
Versatile input types are shown including multiple dropdown selections and double date pickers for date range entries. The inputs will be constructed as a JSON string or object which in turn serves as the request parameter object for the data retrieval. The structures and code are actually migrated from the AngulaJS Search Penal to the Angular search component.
-
In the table-hosting view, the search
tag is defined like this:
<search *ngIf="searchEnabled"
[searchType] = "searchType"
(searchChanged)="onChangeSearch($event)">
</search>
-
The searchChanged
event is raised with searchParams
object passed to the target method.
- - -
this.searchChanged.emit(this.searchParams);
-
In the event target method, onChangeSearch
, of the table-hosting component, conduct the processing logic and make the AJAX call for the data result set.
onChangeSearch(searchParams: any) {
this.pagingParams.changeType = TableChange.search;
this.searchParams = searchParams;
- - -
- - -
}
-
Unlike the PaginationComponent
, you are responsible for coding all pieces of the SearchComponent
, its view template, and related parts in the table-hosting component. You also need to take care of some differences of searching logic between the client-side and server-side paginations.
-
As for changes in page number, size, and sorting, changes for the searching also needs to reset the pager before and after the data access. You can call the methods in the TableMainDirective
as described in the later section, Reset Pager Routines.
Column-Sorting Component
The ColumnSortingComponent
object instance is created one for each column on which the sorting is enabled by specifying the <column-sort>
tag for the column. The component sets the sorting icons on the column header. Clicking the icon will send the changing sorting command for the ascending, descending, and no-sorting operations. The toggleSort
method in the component class handles these operations.
toggleSort() {
let pThis: any = this;
switch (this.sortableItem.sortDirection) {
case "asc":
this.sortableItem.sortDirection = "desc";
break;
case "desc":
this.sortableItem.sortDirection = this.toggleWithOriginalDataOrder ? "" : "asc";
break;
default:
this.sortableItem.sortDirection = "asc";
break;
}
- - -
}
The styles of sorting icons are also changed with the user actions by calling the refreshSortingIcon
method in the TableMainDirective
class.
refreshSortingIcon() {
if (this.sortableItem.sequence == -1) {
this.sortableItem.sortDirection = '';
}
if (this.sortableItem.sortDirection == '') {
this.renderer.removeClass(this.sortIcon.nativeElement, this.config.sortingAscIcon);
this.renderer.removeClass(this.sortIcon.nativeElement, this.config.sortingDescIcon);
this.renderer.addClass(this.sortIcon.nativeElement, this.config.sortingBaseIcon);
}
else if (this.sortableItem.sortDirection == 'asc') {
this.renderer.removeClass(this.sortIcon.nativeElement, this.config.sortingBaseIcon);
this.renderer.removeClass(this.sortIcon.nativeElement, this.config.sortingDescIcon);
this.renderer.addClass(this.sortIcon.nativeElement, this.config.sortingAscIcon);
}
else if (this.sortableItem.sortDirection == 'desc') {
this.renderer.removeClass(this.sortIcon.nativeElement, this.config.sortingBaseIcon);
this.renderer.removeClass(this.sortIcon.nativeElement, this.config.sortingAscIcon);
this.renderer.addClass(this.sortIcon.nativeElement, this.config.sortingDescIcon);
}
}
The NgExTable since Angular version 8 supports the multiple-column sorting feature. The NgExTable library and demo project in Angular version 8 or beyond contain all components and options for processing the multiple-column sorting logic and operations. This article here, however, still focuses on the general workflow and processing logic of the base data grid tool. The companion sample application to this article also sets for the single-column sorting type demo only. Detailed discussions and feature demonstrations on the multiple-column sorting is in another dedicated article, Multiple Column Sorting: from Angular NgExTable to Source Data List Management.
Client-Side Pagination Workflow
The client-side pagination pattern loads all data records to the client cache and displays one page at a time based on the page-size setting. In the sample application, the client-paging.component.ts and associated files and structures show how this pattern works to obtain and display the filtered, sorted, and paginated data.
-
The AJAX call occurs at the very first time for all needed data list items without using the search component. The local data source in the local-data-products.json file is used for the demo (see the getData
method for details).
-
Call the clientPaginationService.processData
method with passing the pagingParams
object instance to get the data rows from the cached data sources for the active page, and then set the paged data rows that are loaded to the table.
let rtn = this.clientPaginationService.processData
(this.pagingParams, this.currentDataList);
this.rows = rtn.dataList;
-
When the user performs a filter operation, the filter criteria is passed to the searchChanged
event target method in which the logic basically repeats the step #2 described above. The paged data rows are refreshed based on the filter criteria.
-
The pager buttons and labels will then be refreshed accordingly with the data row changes.
-
When the user clicks another page number button, changes column sorting, or selects a different page size, the updated information in the pagingParams
object instance is passed to the tableChanged
event target method. The logic in the method then repeats similar processes to those in the steps #2 - 4 above. The table rows and pager are then refreshed again for the new request.
Server-Side Pagination Workflow
For applications, especially enterprise business applications that use databases in large or dynamically increased size, the server-side pagination pattern is the practical and optimal solution due to its chunked data processing and transfer per request. The server-paging.component.ts and associated files and structures in the sample application show how this pattern works to obtain and display the filtered, sorted, and paginated data.
-
The pagingParams
and/or searchParams
with default or updated values, are always set and used for each AJAX call to obtain the paged data rows.
-
The request object, input
, is constructed before making any data access call (see the getProductListRequest
method for details).
let input = this.getProductListRequest();
-
The AJAX call is executed at the beginning and whenever the user performs the search action, changes the page number, applies the column sorting, or selects a different page-size. The returned response object, data
, includes the data list only for the active page and the total count of the data rows in the database based on the search criteria. The sample application can use the mock server data by calling the serverMockDataService.getPagedDataList()
method which retrieves the data from the local data source with the same server-side pagination pattern. Here is the conditional code logic:
let pThis = this;
if (ServerPagingDataSource == 'server') {
let input = this.getProductListRequest();
this.httpDataService.post(ApiUrlForProductList, input)
.subscribe(data => {
pThis.processDataReturn(pThis, data);
},
- - -
}
else if (ServerPagingDataSource == 'mock') {
this.serverMockDataService.getPagedDataList(this.searchParams, this.pagingParams)
.subscribe(data => {
pThis.processDataReturn(pThis, data);
},
- - -
}
-
The pager buttons and labels will then be refreshed accordingly with the data row changes.
Reset Pager Routines
Resetting paging parameters and pager for the change in page number can directly be done within the PaginationComponent
. However, for other operations, such as searching, sorting, and even for page size changes, the code logic in the table-hosting component side needs to cooperate with those child components, which could make the code bulky and repeated in the client callers. To resolve the issue, and rather than creating an additional Angular service, the centralized methods are added into the TableMainDirective
class for communications between the table-hosting components and different child components.
There are two methods that need to be called before and after the data access, respectively.
-
The setPagingParamsBeforeData()
method for re-setting pagingParams
based on some conditions and configuration values.
setPagingParamsBeforeData(pagingParams?: PagingParams) {
- - -
if (pagingParams.changeType == TableChange.search) {
if (this.config.pageNumberWhenSearchChange != -1) {
pagingParams.pageNumber = this.config.pageNumberWhenSearchChange;
}
if (this.config.sortingWhenSearchChange != "current") {
this.resetToInitSortList();
}
- - -
}
else if (pagingParams.changeType == TableChange.sorting) {
if (this.config.pageNumberWhenSortingChange != -1) {
pagingParams.pageNumber = this.config.pageNumberWhenSortingChange;
}
}
else if (pagingParams.changeType == TableChange.pageSize) {
if (this.config.sortingWhenPageSizeChange != "current") {
this.resetToInitSortList();
}
- - -
}
}
-
The updatePagerAfterData
method for updating the pageNumber
based on obtained the data returns.
updatePagerAfterData(pagingParams: PagingParams, totalLength: number) {
if (pagingParams.changeType == TableChange.search) {
if (totalLength &&
(totalLength <= pagingParams.pageSize && pagingParams.pageNumber != 1)) {
pagingParams.pageNumber = 1;
}
this.updatePagerForChangeType(pagingParams.changeType, pagingParams.pageNumber);
}
else {
if (pagingParams.changeType == TableChange.sorting ||
pagingParams.changeType == TableChange.pageSize) {
this.updatePagerForChangeType
(pagingParams.changeType, pagingParams.pageNumber);
}
}
}
private updatePagerForChangeType(changeType: TableChange, pageNumber: number) {
let paramesToUpdatePager: ParamsToUpdatePager = {
changeType: changeType,
pageNumber: pageNumber
};
this.messageService.broadcast
('tableMain_paginationComponent', paramesToUpdatePager);
}
Note that the rxjs
Subject
and Observable
based messageService
(message-transfer.service.ts) is used to access any other components, such as PaginationComponent
, from the TableMainDirective
.
As a general rule, you need to call these two methods only at two places in a table-hosting component:
-
onChangeSearch
event handler method called before data access:
onChangeSearch(searchParams: any) {
- - -
this.tableMainDirective.setPagingParamsBeforeData(this.pagingParams);
}
Note that you don't have to call this method in the onChangeTable
event handler method. The setPagingParamsBeforeData()
is internally called during the table-changed processes in the TableMainDirective
class itself.
-
The place after having obtained data and known the total record length of the dataset
:
this.tableMainDirective.updatePagerAfterData(this.pagingParams, this.totalLength);
Configurations
The sample application shows two-levels of configurations for using the NgExTable
.
- Library-level: NgExTable/ngex-table.config.ts
- Consumer-level: NgExTableDemo/app.config.ts
The configuration items are merged in starting AppComponent
class by assigning the consumer-level TableConfig
object to the library-level NgExTableConfig.base
object. If there are the same key names, the consumer-level settings always take precedence.
The code in the app.component.ts:
this.ngExTableConfig.appConfig = TableConfig;
The code in the ngex-table.config.ts:
private _appConfig: any = {};
get appConfig(): any {
return this._appConfig;
}
set appConfig(v: any) {
this._appConfig = v;
this.main = Object.keys(this._appConfig).length ?
Object.assign(this.base, this._appConfig) : this.base;
}
Below is the entire TableConfig
object for the configurations in the consumer-level. The comment lines illustrate all details of configuration items. Usually, you just need to make any value change in this file if you would like. You don’t have to modify any default setting in the library-level NgExTableConfig
class.
export const TableConfig: any = {
pageSize: 10,
toggleWithOriginalDataOrder: true,
previousText: "«",
nextText: "»",
paginationMaxBlocks: 5,
paginationMinBlocks: 2,
pageNumberWhenPageSizeChange: -1,
pageNumberWhenSortingChange: 1,
sortingWhenPageSizeChange: "current",
pageNumberWhenSearchChange: 1,
sortingWhenSearchChange: "",
sortingIconCssLib: "fa",
sortingAscIcon: "fa-chevron-up",
sortingDescIcon: "fa-chevron-down",
sortingBaseIcon: "fa-sort",
sortingIconColor: "#c5c5c5"
}
Guidelines for Styles
Since the updates in June 2019, the NgExTable uses the bootstrap.css version 4.3.1 as the base styles for both the grid and pager. It also uses the application-level custom site.css to overwrite some default styles from the bootstrap.css, or add some new styles when needed (see details in the site.css file). In the sample application, those css files are imported to the style.css file defined by the Angular CLI. The style class code with Scalable Vector Graphics (SVG) icon libraries are bundled to the dist directory after the build.
/* You can add global styles to this file, and also import other style files */
@import "~bootstrap/dist/css/bootstrap.css";
@import "~font-awesome/css/font-awesome.css";
@import "assets/site.css";
You can either modify the site.css or add your own global or local (component-level) css files for your needs. The typical example is to set the background color for alternative rows in the table. The bootstrap.css sets the default class and values like this:
.table-striped > tbody > tr:nth-of-type(odd) {
background-color: #f9f9f9;
}
You can copy this class code to the site.css or local CSS file, and replace the “odd
” with the “even
” to apply the alternative color to the even numbers of rows. You may change to different color for the alternative rows by modifying the background-color
value in the global or local CSS file.
Summary
The NgExTable is the directive-initiated and HTML template-based Angular grid tool. It's practical and optimal for business web applications, especially those using AngularJS ngTable that would be migrated to the Angular versions. The discussions in this article and the demo of the sample application can help developers better understand the structures and workflow of the NgExTable with the client-side and server-side pagination patterns so that developers can effectively incorporate the NgExTable into their own projects. Developers can even add new, and/or modify existing structures to meet their needs. For a user case with advanced practices of the NgExTable, please see this post Angular Data CRUD with Advanced Practices of Reactive Forms.
History
- 7th February, 2018
- Original post with Angular 5
- 5th December, 2018
- Updated source code
- Upgraded to Angular 6 CLI setup
- Added section Reset Pager Service, and
- edited some other sections
- 19th June, 2019
- Updated source code in Angular 7 and Bootstrap 4.3 (Download NgExTable_All_Ng7.zip)
- Changed
ResetPageService
to calling built-in methods in the TableMainDirective
class - Added
ColumnSortingComponent
to handle sorting processes - Edited or re-wrote text in many sections
- 28th September, 2019
- Updated source code in Angular 8
- Edited text in some sections
-
20th December, 2020
- Updated source code with the Angular 11 CLI
- Updated website project with the ASP.NET Core 5.0
- Edited article text in some sections for the updates
- If you need to run the sample application with previous Angular version 8, 9, or 10, you can download the package.json file for the application, Package.json_Ng8-9-10.zip, replace the package.json file in the existing application with the version you would like, than do the same based on the instructions in the Set Up and Run Sample Application section. The Angular 11 source code of the sample application is fully compatible with the Angular version 8, 9, and 10 without major breaking changes.