csg-table-2
v4.1.0
Published
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17
Downloads
51
Maintainers
Readme
CsgTableLibrary
This library was generated with Angular CLI version 15.0.0. This library is an implementation of the Angular material mat-table with additional functionalities to keep your code dry and clean. The library uses the dynamic component injection, to inject any components instances to the table columns,The table and the component instances are linked together to manipulate all the operations needed for the data.
Advantages
- Keep your code clean and dry , no need to repeat the mat-table HTML code.
- The dynamic component injection will divide your code to clean blocks easy to track and manipulate.
- Csg table provide the loading skeleton animation when loading the data.
- Csg table provide i18 translations, you can provide enums , translations keys to the columns configurations to render the correct record attribute value.
- Csg table can be connected to all types of data by using the
getAllDataObs
orpaginationObservable
functions that injected to the library as inputs, by using these inputs you can inject arrays of objects or a http observable. - Csg table provides the functionalities to pass a
filtrationModel
or apaginationModel
with the retrieve data Observable function (getAllDataObs
orpaginationObservable
). - The filtrationModel and paginationModel are passed by reference ,so that allows the library to manage the inputs in a dynamic way.
- Csg table provides all the functionalities to manipulate the paginationObservable and getAllDataObs.
Requirements
- Downloading Angular material. https://www.npmjs.com/package/@angular/material
- Imports Angular materials styles and themes to your application :
- Be sure that you are adding your general styles.scss to the styles list in Angular.json.
- In your styles.scss add : @import "~@angular/material/prebuilt-themes/indigo-pink.css"; // Choose a theme.
- In your application Angular.json styles array add : "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css".
- Downloading Angular material icons. https://www.npmjs.com/package/material-icons
- In your styles.scss add : @import '~material-icons/iconfont/material-icons.css'.
- In your application Angular.json styles array add : "node_modules/material-icons/iconfont/material-icons.css".
- Download and integrate ngx-translate package :
- npm i @ngx-translate/[email protected]
- npm i @ngx-translate/[email protected]
- Add i18n directory to your assets and create the translation json file (ex:en-gb.json)
- Import TranslateModule in your app module and use HttpLoaderFactory to load the translations from the files.
export function HttpLoaderFactory(http: HttpClient) { return new TranslateHttpLoader(http); } @NgModule({ declarations: [ AppComponent, ], imports: [ . . . BrowserAnimationsModule, ... required for Angualr material HttpClientModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, useFactory: HttpLoaderFactory, deps: [HttpClient], }, }), ]
- In your AppComponent constructor :
constructor(private translateService: TranslateService) { translateService.use('en-gb'); // 'en-gb' is your translation json file name in the assets/i18n }
csg-table basic inputs and outputs
- uniqueKeyOperation : The unique attribute of the src object that the table CRUD operations will be based on.
- columns : The csg-table columns configurations of type CsgTableConfiguration[] .
- renderedColumns : The array of the columns names wanted to view of type string[].
- skeletonRows : The number of skeleton rows to show in the table loading skeleton when fetching the data.
- rowSelection : Set to false will remove the cursor pointer and not calling the row selection event.
- animatedRows : Set to false will remove the row transition hovering effect , you can update the row hover background using --csg-rows-bgr.
- rowSelectedEmitter : Subscribe to get the selected row event of type CsgSelectedRowEvent<
T
>. - localPaginating : Set to true if a local pagination is needed next to getAllDataObs.
- pageSize: Selected(initial) page size (default 25) , can be activated locally if the
localPaginating
is set to true . - paginatedPageSizes:The allowed pagination page sizes options(default options [15, 25, 50, 100, 200]), can be activated locally if the
localPaginating
is set to true .
csg-table inputs for pass all data approach
- getAllDataObs : Function returns an Observable to load all the data form the data source , could be a
predefined list
in the observable result or could be ahttp request
observable. - filtrationModel :
- This model was developed to hold the
params keys
needed for the filtration process,add the filtration keys to this model receive them in thefiltration service
function called by the observable function. - Create a new
interface
with the required keys(attributes) thatextend
this model ,and pass it to the getAllDataObs function. - The csg-table will not update this model, this model should be
manipulated
form the parent component, the new filtered data can be loaded by callingreloadCurrentView
method.
- This model was developed to hold the
- filterKeys : The attributes to filter the table records (locally).
- filterString: A subscriber get the filter substring to filter the records locally , the filtration will be based on the filterKeys inputs.
/**
* filtrationModel defention
*/
export interface UsersFiltrationModel extends SgTableDataFiltrationModel{
//add all proparites needed for filtration , ex:name param
name:string
}
/**
* filtrationModel initialization. in ngOnInit().
* Note : you can connect this object to your filtration form , when any form value updated , update your FiltrationModel and
* call the reloadCurrentView function in the csgTable view child referance
*/
this.usersFiltrationModel : UsersFiltrationModel ={
.
.
name:'',
}
/**
* getAllUsersObs defention
*/
getAllUsersObs: (filtrationModel:UsersFiltrationModel) => Observable<User[]> = (filtrationModel): Observable<User[]> => {
return this.usersService.getUsers(filtrationModel);
};
/**
* service calls http request with your server url . ex:get users API with http params
*/
getUsers(filtrationModel: UsersFiltrationModel): Observable<User[]> {
let params= new HttpParams().append('filterKey',filtrationModel.name)
return this.http.get<User[]>(this.baseUrl,{params:params})
}
/**
* csg-table tags input
*/
<csg-table
#csgTable
.
.
.
[filtrationModel]="usersFiltrationModel"
[getAllDataObs]="getAllUsersObs">
</csg-table>
csg-table inputs for pass paginated data approach
- paginationObservable :
- Function returns an Observable, use this function (with a SgTablePaginationModel as parameter) to call the pagination
API
.Theservice
function called by the observable function should manipulate the model keys in the required form for the HTTP request. - In pagination process the csg-table will only change the
pagination properties
keys values of the pagination model. - When the other params updated form the parent page call the
reloadCurrentView
method to pull the new pagination results from the server.
- Function returns an Observable, use this function (with a SgTablePaginationModel as parameter) to call the pagination
- paginationModel :
- This model was developed to hold the pagination params needed for the
server
pagination and filtration, this model will be received in thepagination service
function called by the observable function. - Initially this model holds the
basics
pagination keys . if another keys needed , a newinterface
extends theSgTablePaginationModel
should be created with the needed keys(attributes). - This component will update only the pagination properties of the pagination model, the other properties should be
manipulated
form the parent component,the new paginated data can be loaded by callingreloadCurrentView
method.
- This model was developed to hold the pagination params needed for the
/**
* pagiantionModel defention , CsgTablePaginationModel type has alrady all the properties needed for pagaintion. pageSize , pageIndex ,sortFiled , ascending.
*/
export interface UsersPagiantionModel extends CsgTablePaginationModel{
//add all proparites needed for the pagiantion or filtration , ex:name param for filtration
name:string
}
/**
* pagiantionModel initialization. in ngOnInit().
* defiend page size should be included in the paginatedPageSizes array
* Note : you can connect this object to your pagaintion-filtration form , when any form value updated , update your PagiantionModel and
* call the reloadCurrentView function in the csgTable view child referance
*/
this.usersPaginationModel : UsersPagiantionModel ={
pageSize:25,
pageIndex:0,
name:''
}
/**
* getPaginatedUsersObs defention
*/
getPaginatedUsersObs: (paginationModel:UsersPagiantionModel) => Observable<CsgTablePaginationResult<User>> = (paginationModel): Observable<CsgTablePaginationResult<User>> => {
return this.usersService.getPagiantedUsers(pagiantionModel);
};
/**
* service calls http request with your server url . ex:get pagented users API withhttp params.
* if the retrved data type is not CsgTablePaginationResult<User> use exjs map operator to reconstrcut your response
*/
getPagiantedUsers(pagiantionModel: UsersPagiantionModel): Observable<CsgTablePaginationResult<User>> {
let params= new HttpParams()
.append('filterKey',filtrationModel.name)
.append('pageSize',filtrationModel.pageSize)
.append('pageIndex',filtrationModel.pageIndex)
return this.http.get<CsgTablePaginationResult<User>>(this.baseUrl,{params:params})
}
/**
* csg-table tags input
*/
<csg-table
#csgTable
.
.
.
[pageSize]="25"
[paginationModel]="usersPaginationModel"
[paginationObservable]="getPaginatedUsersObs">
</csg-table>
Injected components properties
Csg table provides case-sensitive inputs and outputs to be added in the injected components classes(if needed) for data manipulation :
- @Input('dataSrc') dataSrc ; the table record object
- @Input('recordIndex') recordIndex ; the table record row index
- @Input('rowClickedEmitter') rowClickedEmitter = new EventEmitter<CsgTableUpdateEvent<
T
>>(); subscribe this event emitter to get the row clicking event. - @Output('deleteRecord') deleteRecord = new EventEmitter<string | number>(); an event emitter that emit an event with the component dataSrc
uniqueKeyOperation
(you add in the csg-table tag inputs csg-table tag) to the table to delete the record - @Output('updateRecord') updateRecord = new EventEmitter<CsgTableUpdateEvent<
T
>>(); an event emitter to emit update record event to update the table record , the table use theuniqueKeyOperation
to find the record to update it . CsgTableUpdateEvent is used to provide the re-inject cell option(rerender the injected component after updating).
Injected components requirements
- Be sure to set the
uniqueKeyOperation
in the csg-table tag inputs(default value isid
), should be unique for all objects records. - All the csg-table events developed to accept generic types
<T>
, add your type when defining the event, ex:CsgTableUpdateEvent<User
> event type is used to update a user record of typeUser
in a users table constructed from aUser[]
.
More documentation
You will find more documentations for all the package inputs,outputs and event in the library files.
Usage
The next example represents the csg-table basics implementation with some styling options. The example csg-table is used to preview some dummy sites list.
- Create the csg-table columns configurations in your component.ts , the configurations you need to define :
- Rendered columns.
- Table columns.
- Retrieve data observable : getAllSitesObs or paginationObservable.
- Table filtering observable.
export interface Site { id: string, name?: string; address?: string; numberOfFloors?: number; logo?: string; area?: number; } @Component({ selector: 'your-component-selector', templateUrl: './your-component-selector.html', styleUrls: ['./your-component-selector.scss'] }) export class YourComponent implements OnInit,OnDestroy { @ViewChild('csgTable') csgTable: CsgTableComponent; . . . private filterObs$ = new Subject<string>(); filterString = ''; filterSubscription:Subscription=null; renderedColumns: string[] = []; tableColumns: CsgTableConfiguration[] = []; ngOnInit(): void { this.subscribeFiltering(); this.defineSgTableConfig(); } /** * This function use to subscribe input search field * @private */ private subscribeFiltering() { this.filterSubscription = this.filterObs$.pipe(distinctUntilChanged(), debounceTime(200)).subscribe((key: string) => { this.filterString = key; }); } filterSites(event: string) { this.filterObs$.next(event) } private defineSgTableConfig() { this.tableColumns = [ { name: 'Logo', width: '10%', injectedComponent: SitesLogoCellComponent, }, { name: 'Site', width: '20%', attributeKey: 'name' }, { name: 'Address', width: '30%', attributeKey: 'address' }, { name: 'Floors', width: '15%', attributeKey: 'numberOfFloors', }, { name: 'Area', width: '15%', attributeKey: 'area', }, { name: 'actions', hideColumnName: true, width: '10%', actionsComponent: true, injectedComponent: DeleteSiteCellComponent, headerIcons: [ { icon: 'add', action: this.addNewSite, colorHoverTransition:true, toolTip: 'Create site', leftMargin: '41%' } ] } ]; this.renderedColumns = [ 'Logo', 'Site', 'Address', 'Floors', 'Area', 'actions' ]; } private addNewSite = () => { let newSite:Site={ id: 'd5a67eae-835a-483e-9e66-bf93d3449465', name: 'new site', address: 'new site Adreess', numberOfFloors: 15, logo: 'new site logo url', area: 2500, } this.csgTable.addNewRecord(newSite) } /** * yourDummyData is a list of Site objects */ getAllSitesObs: CsgTableTypes.GetAllDataObs<Site> = (): Observable<Site[]> => { return of(yourDummyData) }; ngOnDestroy(): void { this.filterSubscription.unsubscribe(); } }
- In your-component-selector.html :
<div class="wrapper"> <mat-form-field appearance="outline"> <mat-label>{{'FORMS.SEARCH' | translate | capitalize}}</mat-label> <input matInput class="name-input" #searchInput (keyup)="filterSites(searchInput.value)" [placeholder]="'FORMS.SEARCH' | translate"> </mat-form-field> <div class="table-container" *ngIf="renderedColumns.length>0"> <csg-table #csgTable [filterKey]="['name']" [uniqueKeyOperation]="'id'" [columns]="tableColumns" [animatedRows]="false" [renderedColumns]="renderedColumns" [filterString]="filterString" [getAllDataObs]="getAllSitesObs"> </csg-table> </div> </div>
- In your-component-selector.scss file :
.wrapper { width: 100%; height: 100%; display: flex; flex-direction: column; padding:20px; mat-form-field { height: 80px; width: min(30%, 400px); } .table-container { height: calc(100% - 80px); width: 100%; csg-table { width: 100%; height: 100%; } } }
- Define columns component instances SitesLogoCellComponent and DeleteSiteCellComponent
- SitesLogoCellComponent :
@Component({ selector: 'gmao-sites-logo-cell', template: `<img alt="logo" src="{{dataSrc.logo}}" class="site-logo"> <style> .site-logo { width: 100px; height: 100px; padding: 5px 0; } </style> ` }) export class SitesLogoCellComponent implements OnInit { @Input('dataSrc') dataSrc: Site; constructor() {} ngOnInit(): void {} }
- DeleteSiteCellComponent
- In DeleteSiteCellComponent component
@Component({ selector: 'delete-site-cell', styleUrls: ['./delete-site-cell.component.scss'], template: ` <div class="delete-cell"> <button matRipple (click)="deleteSite()" [matTooltip]="'FORMS.DELETE' | translate"> <mat-icon>delete</mat-icon> </button> </div> ` }) export class DeleteSiteCellComponent implements OnInit { @Input('dataSrc') dataSrc: Site; @Output('deleteRecord') deleteRecord = new EventEmitter<string | number>(); constructor() {} ngOnInit(): void {} /** * emit the uniqueKeyOperation(should be passed to the csg-table input) of the record to delete it * from the table */ deleteSite(){ this.deleteRecord.emit(this.dataSrc.id); } }
- In delete-site-cell.component.scss file
.delete-cell { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; button { background: transparent !important; height: 100%; mat-icon { color: red; transition: 180ms ease-in-out; user-select: none; &:hover { color: var(--background-primary); } } } }
- In DeleteSiteCellComponent component
- SitesLogoCellComponent :
Fonts inheritance
To let the csg-table inherits your font family , you need to add the font-family: inherit
in your styles.scss :host
:host {
font-family: inherit;
}
SCSS variables
Csg table using a SCSS variables
to give some animated styles to the components , these variables should be defined in your styles.scss :root
:root {
/* Header icons hovering effect color , mainly its your primary theme color*/
--background-primary: orange;
/* Table rows hover background color */
--csg-rows-bgr: rgb(238, 238, 238);
}
Component styles
Csg table is based on the mat-table , the table styles can be updated by removing the styles encapsulation in the parent component by setting the
encapsulation
value to None
, and add the style you need to overwrite in the parent scss file :
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
encapsulation:ViewEncapsulation.None,
})
More resources and examples
A git repository will be provided soon for all the csg-table implementations