@tavanasystem/tips-mat-table

tips-mat-table is an Angular component for presenting large and complex data with a lightning fast performance (at least 10x faster) and excellent level of control over the presentation.

Usage no npm install needed!

<script type="module">
  import tavanasystemTipsMatTable from 'https://cdn.skypack.dev/@tavanasystem/tips-mat-table';
</script>

README

Tips Mat Table

Dynamic Table built with Angular Material.

Online Demo

Version(1.1.4): Checkout this Repo!

Checkout this Demo! in stackblitz.com

Table of Contents

No. Questions
1 Getting Started
2 Inputs
3 Outputs
4 How to add international
5 Table Events
6 Form Field Cell
7 Button Group Cell
8 Toolbar Buttons
9 additional Setting with TableSetting
10 Pagination
11 Advance and Simple Filter
12 Releases

  1. Getting Started

A full demo can be found on the github repository.

Install with npm:

npm i @tavanasystem/tips-mat-table

After installation include TipsMatTableModule in your module imports:

import { TipsMatTableModule } from '@tavanasystem/tips-mat-table';
...
imports: [
    TipsMatTableModule
  ],
...

Column types are defined as follow:

export interface AbstractField {
  index?: number;
  name: string; // The key of the data
  type?: "text" | "number" | "date" | "category"; // Type of data in the field
  width?: number; // width of column
  header?: string; // The title of the column
  print?: boolean; // disply in printing view by defualt is true
  isKey?: boolean;
  inlineEdit?: boolean;
  display?: "visible" | "hiden" | "prevent-hidden"; // Hide and visible this column
  sticky?: "start" | "end" | "none"; // sticky this column to start or end
  filter?: "client-side" | "server-side" | "none";
  sort?: "client-side" | "server-side" | "none";
  cellClass?: string; // Apply a class to a cell, class name must be in the data
  footerClass?: string;
  cellStyle?: any;
  footerStyle?: any;
  classNames?: string;
  rowClass?: string | AtClassFunc;
  customSortFunction?: AtSortFunc<R>;
  customFilterFunction?: AtSortFunc<R>;
  toPrint?: ToPrint;
  toExport?: ToExport;
  inputFieldConfig?: InputFieldConfig;
  btnGroupConfig?: BtnGroupConfig;
  footerFn?: string | ((allData?: any[]) => string);
}

Source data must be an BehaviorSubject:

let data = [
  { "row": 1, "name": "Element #4", "weight": "65 KG", "color": "Magenta", "brand": "Zanjan Benz", "type": "Van" }, ...];

this.dataSource = new BehaviorSubject<any[]>(data);

and for updating data just next your new data

let newData = [
  { "row": 2, "name": "Element #5", "weight": "75 KG", "color": "Magenta", "brand": "Zanjan Benz2", "type": "Van" }, ...];

this.dataSource.next(newData);

In the HTML add the selector:

<tips-mat-table
  tableName="Tips Mat Table"
  [columns]="fields"
  [dataSource]="dataSourcequot;
  [pending]="pending"
  [tableSetting]="tableSetting"
  [rowContextMenuItems]="contextMenuItems"
  [rowActionMenuItems]="contextMenuItems"
  [toolbarItems]="toolbarItems"
  [pagination]="pagination"
  [rowSelectionMode]="rowSelectionMode"
  [rowSelectionModel]="selectionModel"
  [expandComponent]="expandComponentRef"
  (onTableEvent)="tableEvent($event)"
>
</tips-mat-table>

⬆ Back to Top


  1. Inputs:

columns = Column definitions dataSource = Table data in BehaviorSubject pending = pending progress on table rowSelectionMode = active selection row ('single' | 'multi' | 'none') rowSelectionModel = new SelectionModel() from Angular Material Select pagination = configuration for pagination. eg: { pageIndex: 0, pageSize: 10, pageSizeOptions: [ 5, 10, 100, 1000], length:30 } tableSetting = many general setting of table include styles and features configuration rowContextMenuItems = a menu that shown in right click rowActionMenuItems = a menu that shown in end of the row in ActionMenu toolbarItems = at the footer of table, with right configuration it can be very usefull expandComponent = you can use this to show your extra data bitween two row as expanded

⬆ Back to Top


  1. Outputs:

onTableEvent = all the events of table witch included in TableEvents enum can be listen from this event

export interface ContextMenuItem {
  name: string;
  text: string;
  color?: ThemePalette | string;
  icon?: string;
  disabled?: boolean | ((row: any) => boolean);
  visible?: boolean | ((row: any) => boolean);
  divider?: boolean;
  styles?: any;
  keyboardKey?: string; //Ctrl+ ( keyboard key like KeyS = 's')
}

this.rowActionMenuItems = [
  {
    name: "Edit",
    text: "Edit",
    color: "primary",
    icon: "edit",
    disabled: (row) => row.valid, //it also can be just a value or like this a function
    visible: true, //it also can be just a value or like this a function
  },
  {
    name: "Delete",
    text: "Delete Record",
    color: "warn",
    icon: "delete",
    disabled: false, //it also can be just a value or like this a function
    visible: true, //it also can be just a value or like this a function
  },
];

For more examples run the demo application.

⬆ Back to Top


  1. How to add international

to support new language you must declare new class and implement LanguagePack for example this is persian language:

import { MatPaginatorIntl } from "@angular/material/paginator";
import {
  FilterLabels,
  LanguagePack,
  MenuLabels,
  TableLabels,
} from "@tavanasystem/tips-mat-table";
export class PersionLanguage implements LanguagePack {
  constructor() {}

  menuLabels: MenuLabels = {
    saveData: "ذخیره داده ها ",
    columnSetting: "تنظیمات ستون ها ",
    saveTableSetting: "ذخیره  تنظیمات جدول",
    clearFilter: "فیلتر را پاک کنید",
    jsonFile: "Json فایل",
    csvFile: "CSV فایل",
    printTable: "چاپ جدول",
    filterMode: "نوع فیلتر",
    filterLocalMode: "محلی",
    filterServerMode: "سرور",
    sortMode: "حالت مرتب سازی",
    sortLocalMode: "سمت کاربر",
    sortServerMode: "سمت سرور",
    printMode: "حالت چاپ",
    printYesMode: "بله",
    printNoMode: "خیر",
    pinMode: "حالت پین ",
    pinNoneMode: "هیچ کدام",
    pinStartMode: "شروع",
    pinEndMode: "پایان",
  };

  paginatorLabels: MatPaginatorIntl = {
    changes: null,
    itemsPerPageLabel: "ایتم های هر صفحه:",
    nextPageLabel: "صفحه بعدی:",
    previousPageLabel: "صفحه قبلی:",
    firstPageLabel: "اولین صفحه:",
    lastPageLabel: "آخرین صفحه:",
    getRangeLabel: (page: number, pageSize: number, length: number) => {
      if (length === 0 || pageSize === 0) {
        return `0 از ${length}`;
      }
      length = Math.max(length, 0);
      const startIndex = page * pageSize;
      const endIndex =
        startIndex < length
          ? Math.min(startIndex + pageSize, length)
          : startIndex + pageSize;
      return `${startIndex + 1} - ${endIndex} از ${length}`;
    },
  };

  tableLabels: TableLabels = {
    NoData: "هیچ رکوردی پیدا نشد",
  };

  filterLabels: FilterLabels = {
    Clear: "پاک کردن",
    Search: "جستجو",
    And: "و",
    Or: "یا",
    /* Text Compare */
    Text: "متن",
    TextContains: "دربرگرفتن",
    TextEmpty: "خالی بودن",
    TextStartsWith: "شروع شدن با",
    TextEndsWith: " پایان گرفتن با",
    TextEquals: "مساوی بودن",
    TextNotEmpty: "خالی نبودن",
    /* Number Compare */
    Number: "تعداد",
    NumberEquals: "مساوی",
    NumberNotEquals: "مساوی نبودن",
    NumberGreaterThan: " بزرگ تر از",
    NumberLessThan: "کم تر از ",
    NumberEmpty: "خالی بودن",
    NumberNotEmpty: "خالی نبودن",
    /* Category List Compare */
    CategoryContains: "در برگرفتن",
    CategoryNotContains: "در بر نگرفتن",
    /* Boolean Compare */
    /* Date Compare */
  };
}

and passed this class to grid so :

providers: [{ provide: TableIntl, useFactory: languageIntl }];

And

export function languageIntl() {
  return new PersionLanguage();
}

⬆ Back to Top


  1. Table Events

you can use this switch to handle all events from onTableEvent (remove those you dont need :))

export enum TableEvents {
  MasterSelectionChange = "MasterSelectionChange",//depricated
  RowSelectionChange = "RowSelectionChange",
  RowActionMenu = "RowActionMenu",
  ReloadData = "ReloadData",
  RowClick = "RowClick",
  CellClick = "CellClick",
  DblRowClick = "DblRowClick",
  DblCellClick = "DblCellClick",
  BeforContextMenuOpen = "BeforContextMenuOpen",
  BeforActionMenuOpen = "BeforActionMenuOpen",
  ContextMenuClick = "ContextMenuClick",
  SortChanged = "SortChanged",
  FormFieldCellEvent = "FormFieldCellEvent",
  validatonChange = "validatonChange",
  settingChange = "settingChange",
  ToolbarItemEvent = "ToolbarItemEvent",
  PaginationEvent = "PaginationEvent",
  BtnGroupCellEvent = "BtnGroupCellEvent",
  SimpleFilterEvent = "SimpleFilterEvent",
  FooterCellClick = "FooterCellClick",
  ExportData = "ExportData",
  FilterClear = "FilterClear",
  CtrlKeydown = "CtrlKeydown",
}

⬆ Back to Top


  1. Form Field Cell

you can also add rows with a inline form or just edit a cell with giving this config to you column definition

export interface InputFieldConfig {
  // neccesseries
  id: string;
  fieldType: FormElementType;
  //view related
  icon?: string;
  label?: string;
  class?: string;
  styles?: any;
  appearance?: MatFormFieldAppearance;
  floatLabel?: FloatLabelType;
  hideRequiredMarker?: boolean;
  hideClearBtn?: boolean;
  convertor?: (id: any, row?: any, column?: any) => string;
  hasSeperator?: boolean;
  seperator?: string;
  hasMask?: boolean;
  mask?: string;
  maskPatterns?: any;
  enableCellEditMode?: boolean;
  //validators
  min?: any;
  max?: any;
  inputType?: InputType;
  customValidator?: (row: any) => ValidatorFn;
  //field states
  disabled?: boolean;
  required?: boolean;
  readonly?: boolean;
  defaultValue?: any;
  //messages
  hint?: string;
  error?: string;
  placeholder?: string;
  //events
  blur?: (event: any) => void;
  click?: (event: any) => void;
  input?: (event: any) => void;
  keyup?: (event: any) => void;
  change?: (event: any) => void;
  onResetValue?: (row?: any) => void;
  onSuffixIconClick?: (row?: any, column?: any) => void;
  //for select field
  options?: Observable<Partial<{ value?: any, label?: string }>[]>;
  onSelectItem?: (item: any, row?: any) => void;
  onDataListChanged?: (list?: any[], row?: any) => void;
  hasInitSelection?: boolean;
  multiple?: boolean;
  valueKey?: string; //default: value
  labelKey?: string; //default: label
  disableKey?: string; //default: disable
  // + for auto-complete field
  endTyping?: (searched: any, pageIndex?: number, row?: any) => void;
  NoInitEndTyping?: boolean;
  // + for multi-column-autocomplete
  columnsConfig?: ColumnConfig[];
  footerActions?: {
    icon: string,
    closeAfter?: boolean,
    color?: ThemePalette | string,
    disabled?: boolean | ((mainRow?: any) => boolean),
  }[];
  onRowEvent?: (event: any, row?: any) => void;
  onFooterEvent?: (event: any, searched?: string) => void;
  appendNewData?: (searched?: string, pageSize?: number, row?: any) => void;
  refreshData?: (row?: any) => void;
  NoDataLabel?: string;
  selectHeightFn?: (data: any[], row?: any, column?: any) => string;
  appendedOptions?: Observable<Partial<{ value?: any, label?: string }>[]>;
  // + for duplicate-mc
  secondSelectConfig?: InputFieldConfig;
  secondSelectControlConfig?: {
    icon?: string | ((row?: any, col?: any) => string),
    svgIcon?: string | ((row?: any, col?: any) => string),
    switchFn?: (row?: any, column?: any) => 1 | 2,
    onSwitchEvent?: (type: 1 | 2, row?: any, column?: any) => void,
  };
  // for checkbox
  checkboxLabelPosition?: "before" | "after";
  onCheckChange?: (event: any) => void;
  // for input-select
  defaultInputSelectMode?: "input" | "select";
  // for input
  inputSuffixIcon?: string | ((row?: any, col?: any) => string);
}

and for Multi Column Auto Complete :

export interface ColumnConfig {
  id: string;
  title: string;
  width?: string;
  rowActions?: {
    icon: string,
    color?: ThemePalette | string,
    disabled?: boolean | ((option?: any) => boolean),
  }[];
}

this is all the types that we support until now

export type FormElementType =
  | "input"
  | "select"
  | "auto-complete"
  | "datepicker"
  | "checkbox"
  | "mc-auto-complete"
  | "duplicate-mc"
  | "input-select";

⬆ Back to Top


  1. Button Group Cell

you can have one cell with many mat-icon-button to do some actions on row, it will give you the event with row and action

export interface BtnGroupConfig {
  click?: (event: any) => void;
  btnConfigs?: CellBtnConfig[];
}

export interface CellBtnConfig {
  id: string;
  tooltip?: string;
  showMode?: "edit" | "show" | "always";
  color?: ThemePalette | string;
  icon?: string;
  svgIcon?: string;
  styles?: any;
  disabledFn?: boolean | ((row: any) => boolean);
  hideFn?: boolean | ((row: any) => boolean);
  keyboardKey?: string; //Ctrl+ ( keyboard key like KeyS = 's')
}

⬆ Back to Top


  1. Toolbar Buttons

you can have one diffrent types of buttons with custom styles and config

export interface ToolbarItem {
  id: any;
  tooltip?: string; //used as title too
  disabled?: boolean | ((item?: ToolbarItem) => boolean);
  hidden?: boolean | ((item?: ToolbarItem) => boolean);
  styles?: any | ((item?: ToolbarItem) => any);
  icon?: string;
  svgIcon?: string;
  color?: ThemePalette | string;
  menuitems?: ToolbarMenuitemConfig[];
  keyboardKey?: string; //Ctrl+ ( keyboard key like KeyS = 's')
  btnType?:
    | "mat-button"
    | "mat-raised-button"
    | "mat-flat-button"
    | "mat-stroked-button"
    | "mat-icon-button"
    | "mat-fab"
    | "mat-menu"
    | "mat-mini-fab"; //default
}
export interface ToolbarMenuitemConfig {
  id: any;
  title: string;
  hide?: boolean | (() => boolean);
  disable?: boolean | (() => boolean);
  icon?: string;
  disableIcon?: string;
  styles?: any | (() => any);
}

⬆ Back to Top


  1. additional Setting with TableSetting

you can customize your table on many aspects to suit your needs and desires with the Power implemented in TableSetting

export interface TableSetting {
  direction?: Direction;
  columnSetting?: AbstractField[];
  visibaleActionMenu?: VisibleActionMenu;
  stickyActionMenu?: boolean;
  visibleTableMenu?: boolean;
  alternativeRowStyle?: any;
  normalRowStyle?: any;
  rowStyle?: any;
  cellStyle?: any;
  footerStyle?: any;
  enableContextMenu?: boolean;
  headerStyles?: DmtHeaderStyles;
  ActionMenuMode?: "hover" | "menu";
  tableBackgroundColor?: string;
  paginatorLabels?: DmtpaginatorLabels;
  defaultWidth?: number;
  minWidth?: number;
  headerHeight?: number;
  rowHeight?: number;
  showNoData?: boolean;
  showReload?: boolean;
  printConfig?: PrintConfig;
  tableName?: string;
  showPagination?: boolean;
  expandContainerStyle?: any;
  selectOnRowClick?: boolean;
  toolbarStyles?: any;
  hasFooter?: boolean;
  tableNoDataMessage?: string;
  serverSort?: boolean;
  exportMode?: "SERVER" | "CLIENT";
  hasFilterOnAllColumns?: boolean;
  highlightMoveActive?: boolean;
  shortkeyActive?: boolean;
  SelectNoDataLabel?: string;
}

and you can specifically style headers with DmtHeaderStyles

export interface DmtHeaderStyles {
  headerFontSize?: string;
  headerColor?: string;
  headerJustify?:
    | "center"
    | "flex-start"
    | "flex-end"
    | "space-between"
    | string;
  headerBackgroundColor?: string;
  extraStyles?: any;
}

⬆ Back to Top


  1. Pagination

like mat-paginator you can have pagination object like this:

export interface TablePagination {
  length?: number;
  pageIndex?: number;
  pageSize?: number;
  pageSizeOptions?: number[];
}

and you can also customize labels too:

export interface DmtpaginatorLabels {
  itemsPerPageLabel: string;
  nextPageLabel: string;
  previousPageLabel: string;
  firstPageLabel: string;
  lastPageLabel: string;
  toLabel: string;
  fromLabel: string;
}

⬆ Back to Top


  1. Advance and Simple Filter

you have notice the filter on headers , now we add simple filter witch just have one field and give you the data on what user has entered (of course we are still developing it!)

export interface TableField<R extends TableRow> extends AbstractField {
  ...
  footerFn?: string | ((allData?: any[]) => string);
  fliterMode?: "simple" | "advance"; //'advance' is default!
  simpleFilterModeConfig?: SimpleFilterModeConfig;
  convertor?: (id: any, row?: any, column?: any) => string;
}

export interface SimpleFilterModeConfig {
  fieldType?: "input" | "checkbox-list" | "switch"; //'input' is default!
  placeholder?: string;
  defaultValue?: any;
  switchTitle?: string;
  disabledFn?: (column: any) => boolean;
  options?: Partial<{ id: number | string; value: any; label: string }>[];
}

⬆ Back to Top


  1. Releases

(from v1.1.3)


v1.1.4 :

Menu type in toolbar

  • if you wnat menu in footer toolbar you can set btnType to mat-menu in ToolbarItem like this:
      this.toolbar = [
      {
        ...
        btnType: "mat-menu",
      }

and after that, you can add your options in menuitems like this:

      this.toolbar = [
      {
        ...
        btnType: "mat-menu",
        menuitems: [
          {
            id: "1",
            title: "item 1",
          },
          {
            id: "2",
            title: "item 2",
          },
          {
            id: "3",
            title: "item 3",
          },
        ],
      }
    ]

also this might help you in your config :

export interface ToolbarMenuitemConfig {
  id: any;
  title: string;
  hide?: boolean | (() => boolean);
  disable?: boolean | (() => boolean);
  icon?: string;
  disableIcon?: string;
  styles?: any | (() => any);
}
  • now you dont need to do some hard styling or using ::ng-deep to style table header , simply you just need add your style object in tableSetting like this:
tableSetting: TableSetting = {
    ...//other setting
    headerStyles: {
      ...//other header styles
      extraStyles: {
        fontWeight: "900",
        backgroundColor: "white",
        "box-shadow": "-10px 2px 10px #ddd",
        ...//other styles
      },
    },
    ...
  }

v1.1.5 :

  • fix toolbar menu open position (2022/1/23)
  • fix color type to take custom ThemePalette
  • set pageIndex default value to 1 in endTyping