'Create a custom table similar to primeng table
In our project earlier we used p-table from primeng. Now we try to create a custom one.
So, I want to create a custom table similar to https://www.primefaces.org/primeng/#/table
I need a table with checkbox, I do not need sort, pagination, filter and other features.
I search to create a custom table, Most of them could not customize the column we want to display. I mean, custom table component has designed with static or to bind from api response.
i.e The custom component seems like this. (I don't like want this)
in app.component.ts
<custom-table [options]="tableOptions">Custom table here</custom-table>
custom-table.ts
<table class="table-striped table-hover custom-table">
<thead>
<tr>
<th class="th-checkbox">
<tri-state-checkbox class="toggle-all" [items]="filteredDataObservable"></tri-state-checkbox>
</th>
<th *ngFor="let column of options.columns" (click)="sortHeaderClick(column.value)" [ngClass]="{ 'sorting': isSorting(column.value), 'sorting_asc': isSortAsc(column.value), 'sorting_desc': isSortDesc(column.value) }">
<span [innerHTML]="column.name"></span>
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of filteredData">
<td class="td-checkbox">
<input type="checkbox" [(ngModel)]="row.isSelected">
</td>
<td *ngFor="let column of options.columns">{{getCellValue(row, column)}}</td>
</tr>
</tbody>
</table>
I need like this shown below. we can customize the tbody,tbody with what we what to show
in app.component.ts
<custom-table
#table
[value]="holidays"
[(selection)]="selectedItems"
>
<ng-template pTemplate="colgroup" let-columns>
<colgroup>
<col style="width: 100px" />
<col style="width: 150px" />
<col style="width: 100px" />
<col style="width: 120px" />
</colgroup>
</ng-template>
<ng-template pTemplate="header">
<tr>
<th >
<tableHeaderCheckbox></tableHeaderCheckbox>
</th>
<th pSortableColumn="id">
Id <p-sortIcon field="id"></p-sortIcon>
</th>
<th pSortableColumn="title">
Title <p-sortIcon field="title"></p-sortIcon>
</th>
<th pSortableColumn="slug">
Slug <p-sortIcon field="slug"></p-sortIcon>
</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-data>
<tr>
<td>
<tableCheckbox [value]="data"></tableCheckbox>
</td>
<td>{{ data.id }}</td>
<td>{{ data.title }}</td>
<td>{{ data.slug }}</td>
</tr>
</ng-template>
</custom-table>
I want to create a customTableComponent to bind like this.
please some help me how to do this. Any examples?
Solution 1:[1]
You can do that with the TemplateRef mechanism in angular. You can access templates with @ContentChildren.
Or you can use directives to access templates.
First of all, you need a Provider for communication between templates and your table (also for other components)
import { InjectionToken, TemplateRef } from "@angular/core";
export interface TemplateConsumer{
setTemplate(name: string, template: TemplateRef<unknown>);
}
export const APP_TEMPLATE_CONSUMER_ACCESSOR =
new InjectionToken<ReadonlyArray<TemplateConsumer>>('TemplateConsumerAccessor');
Then you should create a directive that injects the interface (from the parent provider) and calls the setTemplate function.
import { Directive, Inject, Input, TemplateRef } from "@angular/core";
import { APP_TEMPLATE_CONSUMER_ACCESSOR, TemplateConsumer } from "./models/TemplateConsumer";
@Directive({
selector: '[appTemplate]'
})
export class TemplateDirective {
constructor(
// Injecting the component instance that provides APP_TEMPLATE_CONSUMER_ACCESSOR
@Inject(APP_TEMPLATE_CONSUMER_ACCESSOR) private readonly templateConsumer: TemplateConsumer,
private readonly templateRef: TemplateRef<unknown>
) { }
private _appTemplate: string;
@Input()
set appTemplate(value: string) {
this._appTemplate = value;
this.templateConsumer.setTemplate(value, this.templateRef);
}
get appTemplate(): string {
return this._appTemplate;
}
}
You should implement the TemplateConsumer interface and Provide it with the APP_TEMPLATE_CONSUMER_ACCESSOR token at your table component.
import { Component, forwardRef, Input, TemplateRef } from '@angular/core';
import { APP_TEMPLATE_CONSUMER_ACCESSOR, TemplateConsumer } from '../models/TemplateConsumer';
@Component({
selector: 'app-my-alternate-list',
template: `
<div class="my-list">
<ng-container *ngIf="!!data && data.length > 0 else emptyTemplate">
<div class="list-item" *ngFor="let item of data">
<ng-container *ngTemplateOutlet="itemTemplate;context:{item:item}"></ng-container>
</div>
</ng-container>
</div>
`,
// Providing the component instance as TemplateConsumer
providers: [{
provide: APP_TEMPLATE_CONSUMER_ACCESSOR,
useExisting: forwardRef(() => MyAlternateListComponent)
}]
})
export class MyAlternateListComponent implements TemplateConsumer {
@Input()
data: Array<unknown>;
itemTemplate: TemplateRef<unknown>;
emptyTemplate: TemplateRef<unknown>;
private readonly templateNameSetters = {
item: (templateRef: TemplateRef<unknown>) => {
this.itemTemplate = templateRef;
},
empty: (templateRef: TemplateRef<unknown>) => {
this.emptyTemplate = templateRef;
}
};
setTemplate(name: string, template: TemplateRef<unknown>) {
if (!!this.templateNameSetters[name]) {
this.templateNameSetters[name](template);
} else {
// Optional, Throwing an error can cause usability difficulties in some cases.
throw new Error(`'${name}' template is not supported by '${MyAlternateListComponent.name}'`);
}
}
}
I prefer a basic list for example, but you can use it for tables too. For more information about angular templates and how primeng uses these templates, you can check my post on medium. https://medium.com/javascript-in-plain-english/an-alternative-way-to-use-angular-templates-97becc84e109
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | ierhalim |
