Package Exports
- sdk-datagrid
- sdk-datagrid/package.json
Readme
Description:
Mobile-friendly customizable (Angular) datagrid with data options for manipulation, and charts for visualization.
INSTALLATION:
Using NPM:
npm install --save sdk-datagridCONFIGURATION:
To configure the sdk-datagrid for your application, add the following lines to your app.module.ts file:
import { SDKDatagridModule } from 'sdk-datagrid';
@NgModule({
imports: [
SDKDatagridModule
]
})
export class AppModule { }USAGE:
To use the sdk-datagrid, follow the code options below. It can be embedded as a component on any page, or used as the ONLY component on any page.
HTML for partially loading large datasets
<sdk-datagrid
[datasets]="datasets"
[name]="activeView"
[data]="data"
[fullData]="fullData"
[dbPage]="dbPage"
[dbTotal]="dbTotal"
[dbMAXRECORDS]="MAXRECORDS"
[isLoading]="isLoading"
[error]="error"
(loadDataEvent)="loadData($event)">
</sdk-datagrid>HTML for fully loading small datasets
<sdk-datagrid
[datasets]="datasets"
[name]="activeView"
[data]="data"
[isLoading]="isLoading"
[error]="error"
(loadDataEvent)="loadData($event)">
</sdk-datagrid>.ts file
public isLoading: boolean = true;
public MAXRECORDS: number = 1000;
public activeView: string = "vwMyData";
public datasets: SDKDataGridDataset[] = [
{
Title: "My Data",
DbName: "vwMyData",
Source: "api/path/for/vxMyData"
},
{
Title: "My Other Data",
DbName: "vwMyOtherData",
Source: "api/path/for/vwMyOtherData"
}
];
public data: string = "";
public fullData: string = "";
public dbPage: number = 0;
public dbTotal: number = 0;
public error: string = "";
// Dynamically load datasets (optional).
private async loadDatasets() {
this.isLoading = true;
await this.httpService.Get("api/path/for/dynamic/datasets").then((data: any) => {
if (data) {
if (this.activeView === "" || !data.find((d: any) => d.DbName === this.activeView)) {
this.activeView = data[0].DbName;
}
this.datasets = data;
}
});
}
// Callback function for loading data (required).
public async loadData(event: any = null) {
this.isLoading = true;
let body: string = this.buildBody(event);
let fullLoad: boolean = false;
if (event && (event.chart !== undefined || event.export !== undefined)) {
fullLoad = true;
}
try {
let ndx: number = this.datasets.findIndex((ds: any) => ds.DbName === this.activeView);
if (ndx > -1) {
let dataSource: string = this.datasets[ndx].Source;
await this.httpService.Post(dataSource, body).then((data: any) => {
this.resetVariables();
if (data) {
if (fullLoad) {
this.fullData = data["Data"];
} else {
this.data = data["Data"];
this.dbPage = parseInt(data["Page"]);
this.dbTotal = parseInt(data["Total"]);
this.error = data["Error"];
}
}
});
}
} catch (error: any) {
this.error = error.message;
}
this.isLoading = false;
}
// Change the body parameters to fit your API call.
private buildBody(event: any = null): string {
let page: number = 1;
let rows: number = this.MAXRECORDS;
let sorts: any = [];
let filters: any = [];
if (event) {
if (event.name && event.name !== "") {
this.activeView = event.name;
}
if (event.page) {
page = parseInt(event.page);
}
if (event.sorts) {
event.sorts.forEach((element: any) => {
sorts.push({ ColumnName: element.Name, Direction: element.Sort });
});
}
if (event.filters) {
event.filters.forEach((element: any) => {
filters.push({ ColumnName: element.Name, Operation: element.Operation, Value: element.Value });
});
}
if (event.chart || event.export) {
page = 0;
rows = 999999999;
}
if (event.addon) {
// do addon stuff here
}
}
let parameters: any = {
page: page,
rows: rows,
filters: filters,
sorts: sorts
};
return JSON.stringify(parameters);
}
private resetVariables() {
this.data = "";
this.fullData = "";
this.dbPage = 0;
this.dbTotal = 0;
this.error = "";
}Columns
If you want to specify the columns, use the columns option in the sdk-datagrid.
.ts file
@ViewChild('category') category!: TemplateRef<any>;
@ViewChild('dataTemplate') dataTemplate!: TemplateRef<any>;
@ViewChild('actionRight') actionRight!: TemplateRef<any>;
public columns: SDKDataGridColumn[] = [
{ Name: 'category', DisplayName: 'Category', required: true, requiredColor: 'red', notes: 'MUST be a valid Category.', editTemplate: () => this.category },
{ Name: 'air_date', DisplayName: 'Date', required: true, validCharacters: 'calendar', pattern: 'YYYY-MM-DD' },
{ Name: 'question', DisplayName: 'Question', height: '50px', width: '100px', dataTemplate: () => this.dataTemplate },
{ Name: 'value', DisplayName: 'Value', required: true, notes: 'Between 100 - 1000.', validCharacters: 'numeric' },
{ Name: 'answer', DisplayName: 'Answer', dataTemplate: () => this.dataTemplate },
{ Name: 'round', DisplayName: 'Round' },
{ Name: 'show_number', DisplayName: 'Show', validCharacters: 'custom', pattern: '^[0-9]{4}$' },
{ Name: 'fin', DisplayName: 'Completed' },
{ Name: 'Edit', DisplayName: 'Edit', actionSide: "right", actionTemplate: () => this.actionRight }
];
// If editing is enabled...
public editData(index: any) {
this.editRowIndex = index;
}
public saveData(rowItem: any, activeRow: any) {
for (let key in activeRow) {
if (Object.prototype.hasOwnProperty.call(activeRow, key)) {
rowItem[key] = activeRow[key];
}
}
// Save to database and show message.
this.editRowIndex = -1;
}
public cancelData() {
this.editRowIndex = -1;
}.html file
<sdk-datagrid
[datasets]="datasets"
[name]="activeView"
[data]="data"
[fullData]="fullData"
[columns]="columns"
[dbPage]="dbPage"
[dbTotal]="dbTotal"
[dbMAXRECORDS]="MAXRECORDS"
[isLoading]="isLoading"
[error]="error"
(loadDataEvent)="loadData($event)">
</sdk-datagrid>
<ng-template #category let-rowItem="rowItem" let-index="index">
<div>
<select style="margin-left: 10px;">
<option value="">...</option>
<option value="HISTORY">HISTORY</option>
<option value="3-LETTER WORDS">3-LETTER WORDS</option>
<option value="THE COMPANY LINE">THE COMPANY LINE</option>
<option value="EPITAPHS & TRIBUTES">EPITAPHS & TRIBUTES</option>
<option value="ESPN's TOP 10 ALL-TIME ATHLETES">ESPN's TOP 10 ALL-TIME ATHLETES</option>
</select>
</div>
</ng-template>
<ng-template #dataTemplate let-value="value">
<div>{{ value }}</div>
</ng-template>
<ng-template #actionRight let-rowItem="rowItem" let-activeRow="activeRow" let-index="index">
<div *ngIf="!rowItem.isEdit">
<div title="Edit" class="dg-icon material-symbols-outlined cursor" (click)="editData(index)">edit</div>
<div title="Delete" class="dg-icon material-symbols-outlined cursor delete">delete</div>
</div>
<div *ngIf="rowItem.isEdit">
<div title="Save" class="dg-icon material-symbols-outlined cursor done" (click)="saveData(rowItem, activeRow)">done</div>
<div title="Cancel" class="dg-icon material-symbols-outlined cursor delete" (click)="cancelData()">close</div>
</div>
</ng-template>Custom Options
If you want to add a custom option to the sdk-datagrid, use the optionAddons/windowAddons parameters. First, create your "custom" option and window components.
mycustom-option.component
.html file
<div (click)="showDataOptions('myCustom')">
<div title="My Custom Option" class="icon material-symbols-outlined" [ngClass]="optionTitle === 'myCustom' ? 'active' : ''">file_download</div>
<div *ngIf="myCustomBadge !== ''" class="badge">
<div class="label">{{ myCustomBadge }}</div>
</div>
</div>.scss/.css file
.icon {
font-size: 2em;
cursor: pointer;
&.active {
color: blue;
}
&:hover {
color: blue;
}
}
.badge {
position: absolute;
top: -5px;
right: 0;
height: 16px;
width: 16px;
border-radius: 50%;
background: red;
text-align: start;
.label {
position: absolute;
top: 0;
height: 16px;
width: 16px;
margin: 2px auto;
text-align: center;
font-size: 0.75em;
color: white;
}
}.ts file
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'mycustom-option',
templateUrl: './mycustom-option.component.html',
styleUrls: ['./mycustom-option.component.scss']
})
export class MyCustomOptionComponent {
@Input() optionTitle: string = ""; // Unique name for option.
@Output() showDataOptionsEvent = new EventEmitter<any>(); // Method to show option window.
public myCustomBadge: string = ""; // Used to add badge to custom option.
public showDataOptions(type: string) {
this.showDataOptionsEvent.emit(type);
}
}mycustom-window.component
.html file
<div *ngIf="dataClass === 'expand' && optionTitle === 'mycustom'" class="window">
<div class="header">
<span class="title">My Custom Option</span>
<span title="Close" class="icon material-symbols-outlined close" (click)="close()">close</span>
</div>
<div class="content">
// Add your custom option functionality here.
</div>
<div class="footer">
<button class="button" type="button" (click)="apply()">Apply</button>
</div>
</div>.scss/.css file
.window {
position: absolute;
top: 10px;
right: 10px;
bottom: 10px;
left: 10px;
overflow: hidden;
.header {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 50px;
.title {
position: absolute;
top: 0;
left: 0;
font-size: large;
font-weight: 600;
}
.close {
position: absolute;
top: 0;
right: 0;
&.icon {
cursor: pointer;
font-size: xx-large;
&:hover {
color: blue;
}
}
}
}
.actions {
position: absolute;
top: 50px;
right: 0;
left: 0;
margin-top: 5px;
height: 50px;
width: 100%;
.button-left {
float: left;
margin: 0;
}
.button-right {
float: right;
margin: 0;
margin-right: 5px;
}
}
.content {
position: absolute;
top: 50px;
right: 0;
bottom: 50px;
left: 0;
border: 1px solid gray;
display: block;
background: white;
border-radius: 4px;
margin-top: 0;
overflow: auto;
text-align: left;
}
.footer {
position: absolute;
right: 0;
left: 0;
bottom: 0;
height: 40px;
width: 100%;
text-align: center;
}
button {
box-shadow: 1px 1px 2px 1px rgb(140, 140, 140);
border-radius: 5px;
}
}.ts file
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { SDKDataGridColumn } from 'sdk-datagrid';
@Component({
selector: 'mycustom-window',
templateUrl: './mycustom-window.component.html',
styleUrls: ['./mycustom-window.component.scss']
})
export class MyCustomWindowComponent {
@Input() dataClass: string = ""; // Used to indicate if window is open/closed.
@Input() optionTitle: string = ""; // Unique name for option.
@Input() datasets: any[] = []; // Data array.
@Input() columns: SDKDataGridColumn[] = []; // Columns defines for data.
@Input() filters: any[] = []; // Filters defines for data.
@Input() sorts: any[] = []; // Sorting defines for data.
@Output() applyEvent = new EventEmitter<any>(); // Apply option.
@Output() closeEvent = new EventEmitter<any>(); // Close window.
public close() {
this.closeEvent.emit();
}
public async apply() {
this.applyEvent.emit();
this.closeEvent.emit();
}
}Then add those components to your datagrid.
.ts file
import { MyCustomOptionComponent } from './mycustom/option/mycustom-option.component';
import { MyCustomWindowComponent } from './mycustom/window/mycustom-window.component';
public myCustomOptionComponent = MyCustomOptionComponent;
public myCustomWindowComponent = MyCustomWindowComponent;.html file
<sdk-datagrid
[datasets]="datasets"
[name]="activeView"
[data]="data"
[fullData]="fullData"
[columns]="columns"
[optionAddons]="[myCustomOptionComponent]"
[windowAddons]="[myCustomWindowComponent]"
[dbPage]="dbPage"
[dbTotal]="dbTotal"
[dbMAXRECORDS]="MAXRECORDS"
[isLoading]="isLoading"
[error]="error"
(loadDataEvent)="loadData($event)">
</sdk-datagrid>NOTES:
The loadData() method is NOT directly called from your component. The
sdk-datagridwill handle calling the method - including the initial load.SDKDataGridDataset object is defined as:
[ { Title: "My Dataset", <-- Display name for UI (Text, Dropdown, Tab). DbName: "myDataset", <-- The name of the view/table. Source: "api/datasource" <-- The source of the data (optional). } ]In order for the
sdk-datagridto handle data defined in the above example, your APIs should return the following object:{ Data: {}, <-- The actual data in JSON form Page: 1, <-- Current page it is returning Total: 33, <-- Total records for entire query Error: "" <-- Any errors from API call }However, you can change the parameters in the API body to fit your specific needs.
- If you are displaying a simple grid with data ONLY (no options/functionality), you can simply return and set the "this.data" parameter ONLY, and turn off the Header and Footer areas:
<sdk-datagrid [datasets]="datasets" [name]="activeView" [data]="data" [showHeader]=false [showFooter]=false [isLoading]="isLoading" [error]="error" (loadDataEvent)="loadData($event)"> </sdk-datagrid>
To use sorting, your APIs SHOULD be able to accept/handle an array of objects defining the columns/direction for sorting:
sorts: [ { ColumnName: "column", Direction: "asc" <-- See list below } ]
Sort Direction List:
- asc - descTo use filtering, your APIs SHOULD be able to accept/handle an array of objects defining the columns/operators/values for filtering:
filters: [ { ColumnName: "column", Operation: "Equals", <-- See list below Value: "column_value" } ]
Filter Operation List:
- Equals - NotEquals - Empty (value is null) - NotEmpty (value is NOT null) - Contains - NotContains - GreaterThan - GreaterThanOrEqual - LessThan - LessThanOrEqual - InListTo customize filtering for a column:
Define the
FilterTypesandFilterValuesin the columns object.public columns: SDKDataGridColumn[] = [ { Name: 'category', DisplayName: 'Category', required: true, requiredColor: 'red', notes: 'MUST be a valid Category.', editTemplate: () => this.category }, { Name: 'air_date', DisplayName: 'Date', required: true, validCharacters: 'calendar', pattern: 'YYYY-MM-DD' }, { Name: 'question', DisplayName: 'Question', height: '50px', width: '100px', dataTemplate: () => this.dataTemplate }, { Name: 'value', DisplayName: 'Value', required: true, notes: 'Between 100 - 1000.', validCharacters: 'numeric' }, { Name: 'answer', DisplayName: 'Answer', dataTemplate: () => this.dataTemplate }, { Name: 'round', DisplayName: 'Round', FilterTypes: [ Filters.Equals ], FilterValues: [ 'Jeopardy!', 'Double Jeopardy!' ] }, { Name: 'show_number', DisplayName: 'Show', FilterTypes: [ Filters.LessThanOrEqual, Filters.GreaterThanOrEqual ], FilterValues: [ 4000, 4500, 5000 ], validCharacters: 'custom', pattern: '^[0-9]{4}$' }, { Name: 'fin', DisplayName: 'Completed', FilterTypes: [ Filters.Equals ], FilterValues: [ true, false ] }, { Name: 'Edit', DisplayName: 'Edit', actionSide: "right", actionTemplate: () => this.actionRight } ];
To use a multi-select dropdown as your filter, define the
FilterMultiSelectandFilterValuesin the columns object.public columns: SDKDataGridColumn[] = [ { Name: 'category', DisplayName: 'Category', required: true, requiredColor: 'red', notes: 'MUST be a valid Category.', editTemplate: () => this.category }, { Name: 'air_date', DisplayName: 'Date', required: true, validCharacters: 'calendar', pattern: 'YYYY-MM-DD' }, { Name: 'question', DisplayName: 'Question', isVisible: false, height: '50px', width: '100px', dataTemplate: () => this.dataTemplate }, { Name: 'value', DisplayName: 'Value', required: true, notes: 'Between 100 - 1000.', validCharacters: 'numeric' }, { Name: 'answer', DisplayName: 'Answer', showSort: false, showFilter: false, dataTemplate: () => this.dataTemplate }, { Name: 'round', DisplayName: 'Round', FilterMultiSelect: true, FilterValues: [ 'Jeopardy!', 'Double Jeopardy!', 'Final Jeopardy!', 'Tiebreaker' ] }, { Name: 'show_number', DisplayName: 'Show', FilterTypes: [ Filters.GreaterThanOrEqual, Filters.LessThanOrEqual ], FilterValues: [ 4000, 4500, 5000, 6000 ] }, { Name: 'fin', DisplayName: 'Completed', FilterTypes: [ Filters.Equals ], FilterValues: [ true, false ]}, { Name: 'Edit', DisplayName: 'Edit', actionSide: "right", actionTemplate: () => this.actionRight } ];
To create custom filters (
SDKDataGridCustomFilter), the following properties are available:Name: string = ""; // Unique filter name. DisplayName?: string = ""; // Name to display to user. Type?: FilterType = FilterType.Textbox; <-- See list below // START: sdk-select properties for Dropdown filters. FilterMultiSelect?: boolean = false; // Allows multiple filter values to be selected. FilterTypes?: Filters[] = []; // Allows you to define specific filter types. NOTE: If FilterMultiSelect = true, this property is forced to [Filters.Equals, Filters.NotEquals]. FilterValues?: any[] = []; // Allows you to define specific filter values. // END: sdk-select properties for Dropdown filters.
FilterType Enums:
- Textbox - Checkbox - DropdownNOTE: If
Type = FilterType.Textboxand is used withFilterTypes, the custom filter will have an Operation/Value layout. IfFilterTypesis NOT used, the layout will ONLY have a Value.To use formulas, the following properties are available:
formulas: [ { Name: string = ""; Format: number = 0; <-- The number of digits for precision. Operation: FormulaOperation = "Avg"; <-- See list below } ]
Formula Operation List:
- Avg - Rng - SumTo use columns (
SDKDataGridColumn), the following properties are available:Name: string = ""; // Original db name. DisplayName?: string = ""; // System override of db name (Name). FriendlyName?: string = ""; // User provides. Produces * indicator. Sort?: string = ""; ColumnSort?: boolean = false; showSort?: boolean = true; // Allows you to turn on/off sorting capabilities. showFilter?: boolean = true; // Allows you to turn on/off filtering capabilities. showTooltip?: boolean = false; isAction?: boolean = false; // Is the column associated to an action. isVisible?: boolean = true; // Allows you to turn on/off the visibility of the column. actionSide?: string = "right" // right or left // START: sdk-select properties FilterMultiSelect?: boolean = false; // Allows multiple filter values to be selected. FilterTypes?: any[] = []; // Allows you to define specific filter types. NOTE: If FilterMultiSelect = true, this property is forced to [Filters.Equals, Filters.NotEquals]. FilterValues?: any[] = []; // Allows you to define specific filter values. // END: sdk-select properties // START: editTemplate option allowEdit?: boolean = true; required?: boolean = false; requiredColor?: string = ""; notes?: string = ""; validCharacters?: string = ""; hint?: string = ""; pattern?: string = ""; height?: string = ""; width?: string = ""; border?: string = ""; style?: string = ""; // END: editTemplate option actionTemplate?: () => TemplateRef<any>; // Used to add "sticky" column for actions (i.e., Edit); editTemplate?: () => TemplateRef<any>; // Used to enable row-level edits. dataTemplate?: () => TemplateRef<any>; // Used to customize columns. formatter?: (value: any) => string; // Optional callback method to format the data. setFilterValues?: () => Promise<any[]>; // Optional callback method to load filter values.
List of properties for the sdk-datagrid:
/**************************************************************************
* Required Input Parameters
**************************************************************************/
datasets: SDKDataGridDataset[] = []; // Once provided, the loadDataEvent will be triggered.
/**************************************************************************
* Required Output Parameters
**************************************************************************/
loadDataEvent = new EventEmitter<any>(); // This is a callback to load data.
/**************************************************************************
* Optional Input/Output Parameters
**************************************************************************/
name: string = ""; // This value will be set by the loadDataEvent callback and will be the active dataset (view).
data: string = ""; // This value will be set by the loadDataEvent callback and contain the actual data.
fullData: string = ""; // This value will be set by the loadDataEvent callback when chart or export options are selected.
title: string = ""; // Add a title to the page if only 1 dataset (view) is being used.
titleStyle: string = ""; // Add a style to the title.
uniqueIdentifier: string = ""; // This value is ONLY used for creating the storage (saved session) name.
options: SDKDataGridOptions | null = null; // You can turn on/off data options.
columns: SDKDataGridColumn[] = []; // You can specify certain columns to be set.
customFilters: SDKDataGridCustomFilter[] = []; // Custom filters added to standard filter provided in loaddata callback (filters).
formulas: SDKDataGridFormula[] = []; // You can specify default formulas.
settings: SDKDataGridSettings[] = []; // Options that are saved outside the grid. This value should be set to the value retieved from the datagrid @Output method "settingsSavedEvent". Consider it a passthrough value.
includeAllColumns: boolean = false; // You can specify whether or not to include all columns in dataset.
isDataWrapped: boolean = false; // You can specify whether or not to wrap data within a column.
forceReload: boolean = false; // You can reload the current data.
fontFamily: string = ""; // Applies a given font-family to entire grid.
columnHeaderStyle: string = ""; // You can set the column header style for the grid.
filterTypes: Filters[] = []; // Limits the filtering list to provided types.
rowValues: [10, 25, 50, 100, 500, 1000]; // Allows you to override the values in the Rows dropdown.
defaultRow: 100; // Sets the default value of the Rows dropdown.
loadingStyle: string = ""; // Adjust the loading style when the datagrid is embedded on the page with other components.
error: string = ""; // Any errors that occur during processing.
useTabs: boolean = false; // This option shows the list of datasets in a tabular format (instead of the default dropdown).
isLoading: boolean = false; // Parent controlled variable for the entire grid.
showLoadingTimer: boolean = false; // Shows a timer while the loading component is running.
/**************************************************************************
* Edit Mode
**************************************************************************/
editRowIndex: any; // Required for editing rows. This is the current row index that is being edited.
/**************************************************************************
* Addon Parameters (Option Icon/Modal Window)
**************************************************************************/
optionAddons: Type<any>[] = [];
windowAddons: Type<any>[] = [];
/**************************************************************************
* Database Parameters
**************************************************************************/
dbMAXRECORDS: number = 1000; // Total records to retrieve from db on any given pull (skip/take).
dbPage: number | undefined; // Current db page (for partial loads).
dbTotal: number | undefined; // Total db records (based on filters).
/**************************************************************************
* UI Section Parameters
**************************************************************************/
showHeader: boolean = true; // Enable/disable the header section of the grid.
showOptions: boolean = true; // Enable/disable options section of the grid.
showFooter: boolean = true; // Enable/disable the footer section of the grid.
showPaging: boolean = true; // Enable/disable the paging section (within the footer) of the grid.
showPanelOverlay: boolean = false; // Enable/disable the overlay of the options panel.
autoClosePanel: boolean = false; // Enable/disable automatically closing panel after action.
minimizeOptions: boolean | undefined; // Minimize the 'options' section on the right-side of the grid.
detailTemplate!: TemplateRef<any> | undefined; // Embedded component for row detail.
/**************************************************************************
* Version - Used for local storage across ALL grids. This should be based
* on the version (whatever that may be) associated with your data model.
**************************************************************************/
version: string = "";
/**************************************************************************
* Optional Output Parameters
**************************************************************************/
columnSettingsChangedEvent: EventEmitter<SDKDataGridColumn[]> = new EventEmitter(); // Used as a callback for column changes.
settingsSavedEvent: EventEmitter<SDKDataGridSettings[]> = new EventEmitter(); // Used as a callback to save settings externally.
selectedRowsChangedEvent: EventEmitter<any[]> = new EventEmitter(); // Used as a callback for row changes.
selectedRowActionEvent: EventEmitter<any> = new EventEmitter(); // Used as a callback for row actions. NOTE: The return object is { data: [the row], index: [the row index] }