Skip to content
This repository was archived by the owner on Feb 2, 2025. It is now read-only.

[Feature] Add TemplateRef support #1536

Merged
merged 2 commits into from
May 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions demo/src/app/advanced/demo-ng-template-ref-event-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface IDemoNgComponentEventType {
cmd: string,
data: any
}
3 changes: 3 additions & 0 deletions demo/src/app/advanced/demo-ng-template-ref.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div class="btn-group d-block text-center">
<button class="btn btn-sm btn-dark" (click)="onAction1()">Action 1</button>
</div>
33 changes: 33 additions & 0 deletions demo/src/app/advanced/demo-ng-template-ref.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Component, Input, OnInit, Output } from '@angular/core';
import { Subject } from 'rxjs';
import { IDemoNgComponentEventType } from './demo-ng-template-ref-event-type';

@Component({
selector: 'app-demo-ng-template-ref',
templateUrl: './demo-ng-template-ref.component.html',
})
export class DemoNgComponent implements OnInit {

constructor() { }

@Output()
emitter = new Subject<IDemoNgComponentEventType>();

@Input()
data = {};

ngOnInit(): void {
}

onAction1() {
this.emitter.next({
cmd: 'action1',
data: this.data
});
}

ngOnDestroy() {
this.emitter.unsubscribe();
}

}
12 changes: 12 additions & 0 deletions demo/src/app/advanced/using-ng-template-ref.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<ng-template #preview>
<blockquote>Please click on Action button</blockquote>
<p class="text-danger">You clicked on: <strong>{{ message }}</strong></p>
<br />
<table datatable [dtOptions]="dtOptions" [dtTrigger]="dtTrigger" class="row-border hover"></table>
</ng-template>
<app-base-demo [pageTitle]="pageTitle" [mdIntro]="mdIntro" [mdHTML]="mdHTML" [mdTS]="mdTS" [template]="preview">
</app-base-demo>

<ng-template #demoNg let-data="adtData" let-emitter="captureEvents">
<app-demo-ng-template-ref [data]="data" (emitter)="emitter($event)"></app-demo-ng-template-ref>
</ng-template>
73 changes: 73 additions & 0 deletions demo/src/app/advanced/using-ng-template-ref.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { AfterViewInit, Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { ADTSettings, } from 'angular-datatables/src/models/settings';
import { Subject } from 'rxjs';
import { IDemoNgComponentEventType } from './demo-ng-template-ref-event-type';
import { DemoNgComponent } from './demo-ng-template-ref.component';

@Component({
selector: 'app-using-ng-template-ref',
templateUrl: './using-ng-template-ref.component.html',
})
export class UsingNgTemplateRefComponent implements OnInit, AfterViewInit {

constructor() { }

pageTitle = 'Using Angular TemplateRef';
mdIntro = 'assets/docs/advanced/using-ng-template-ref/intro.md';
mdHTML = 'assets/docs/advanced/using-ng-template-ref/source-html.md';
mdTS = 'assets/docs/advanced/using-ng-template-ref/source-ts.md';

dtOptions: ADTSettings = {};
dtTrigger = new Subject();

@ViewChild('demoNg') demoNg: TemplateRef<DemoNgComponent>;
message = '';

ngOnInit(): void {
}

ngAfterViewInit() {
const self = this;
this.dtOptions = {
ajax: 'data/data.json',
columns: [
{
title: 'ID',
data: 'id'
},
{
title: 'First name',
data: 'firstName',
},
{
title: 'Last name',
data: 'lastName'
},
{
title: 'Actions',
data: null,
defaultContent: '',
ngTemplateRef: {
ref: this.demoNg,
context: {
// needed for capturing events inside <ng-template>
captureEvents: self.onCaptureEvent.bind(self)
}
}
}
]
};

// wait before loading table
setTimeout(() => {
this.dtTrigger.next();
}, 200);
}


onCaptureEvent(event: IDemoNgComponentEventType) {
this.message = `Event '${event.cmd}' with data '${JSON.stringify(event.data)}`;
}


}
3 changes: 3 additions & 0 deletions demo/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ <h3>
<li>
<a routerLink="/advanced/using-pipe">Using Angular Pipes</a>
</li>
<li>
<a routerLink="/advanced/using-template-ref">Using Angular TemplateRef</a>
</li>
</ul>
</div>
</li>
Expand Down
6 changes: 5 additions & 1 deletion demo/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import { UpperCasePipe } from '@angular/common';
import { MarkdownModule } from 'ngx-markdown';
import { BaseDemoComponent } from './base-demo/base-demo.component';
import { FAQComponent } from './f-a-q/f-a-q.component';
import { UsingNgTemplateRefComponent } from './advanced/using-ng-template-ref.component';
import { DemoNgComponent } from './advanced/demo-ng-template-ref.component';

@NgModule({
declarations: [
Expand Down Expand Up @@ -72,7 +74,9 @@ import { FAQComponent } from './f-a-q/f-a-q.component';
SelectExtensionComponent,
UsingNgPipeComponent,
BaseDemoComponent,
FAQComponent
FAQComponent,
UsingNgTemplateRefComponent,
DemoNgComponent
],
imports: [
BrowserModule,
Expand Down
5 changes: 5 additions & 0 deletions demo/src/app/app.routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { ResponsiveExtensionComponent } from './extensions/responsive-extension.
import { SelectExtensionComponent } from './extensions/select-extension.component';
import { UsingNgPipeComponent } from './advanced/using-ng-pipe.component';
import { FAQComponent } from './f-a-q/f-a-q.component';
import { UsingNgTemplateRefComponent } from './advanced/using-ng-template-ref.component';

const routes: Routes = [
{
Expand Down Expand Up @@ -101,6 +102,10 @@ const routes: Routes = [
path: 'advanced/using-pipe',
component: UsingNgPipeComponent
},
{
path: 'advanced/using-template-ref',
component: UsingNgTemplateRefComponent
},
{
path: 'extensions/buttons',
component: ButtonsExtensionComponent
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
You can use Angular `TemplateRef` acquired from `ViewChild` or passing it from HTML to transform data on the table.
11 changes: 11 additions & 0 deletions demo/src/assets/docs/advanced/using-ng-template-ref/source-html.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
```html

<!-- demo-ng-template-ref.component.html -->
<div class="btn-group d-block text-center">
<button class="btn btn-sm btn-dark" (click)="onAction1()">Action 1</button>
</div>

<!-- using-ng-template-ref.component.html -->
<table datatable [dtOptions]="dtOptions" class="row-border hover"></table>

```
116 changes: 116 additions & 0 deletions demo/src/assets/docs/advanced/using-ng-template-ref/source-ts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
```typescript
// demo-ng-template-ref.component.ts

import { Component, Input, OnInit, Output } from "@angular/core";
import { Subject } from "rxjs";
import { IDemoNgComponentEventType } from "./demo-ng-template-ref-event-type";

@Component({
selector: "app-demo-ng-template-ref",
templateUrl: "./demo-ng-template-ref.component.html",
})
export class DemoNgComponent implements OnInit {
constructor() {}

@Output()
emitter = new Subject<IDemoNgComponentEventType>();

@Input()
data = {};

ngOnInit(): void {}

onAction1() {
this.emitter.next({
cmd: "action1",
data: this.data,
});
}

ngOnDestroy() {
this.emitter.unsubscribe();
}
}

// demo-ng-template-ref-event-type.ts

export interface IDemoNgComponentEventType {
cmd: string;
data: any;
}

// ng-template-ref.component.ts
import {
AfterViewInit,
Component,
OnInit,
TemplateRef,
ViewChild,
} from "@angular/core";
import { ADTSettings } from "angular-datatables/src/models/settings";
import { Subject } from "rxjs";
import { IDemoNgComponentEventType } from "./demo-ng-template-ref-event-type";
import { DemoNgComponent } from "./demo-ng-template-ref.component";

@Component({
selector: "app-using-ng-template-ref",
templateUrl: "./using-ng-template-ref.component.html",
})
export class UsingNgTemplateRefComponent implements OnInit, AfterViewInit {
constructor() {}

pageTitle = "Using Angular TemplateRef";
mdIntro = "assets/docs/advanced/using-ng-template-ref/intro.md";
mdHTML = "assets/docs/advanced/using-ng-template-ref/source-html.md";
mdTS = "assets/docs/advanced/using-ng-template-ref/source-ts.md";

dtOptions: ADTSettings = {};
dtTrigger = new Subject();

@ViewChild("demoNg") demoNg: TemplateRef<DemoNgComponent>;

ngOnInit(): void {}

ngAfterViewInit() {
const self = this;
this.dtOptions = {
ajax: "data/data.json",
columns: [
{
title: "ID",
data: "id",
},
{
title: "First name",
data: "firstName",
},
{
title: "Last name",
data: "lastName",
},
{
title: "Actions",
data: null,
defaultContent: "",
ngTemplateRef: {
ref: this.demoNg,
context: {
// needed for capturing events inside <ng-template>
captureEvents: self.onCaptureEvent.bind(self),
},
},
},
],
};

// wait before loading table
setTimeout(() => {
this.dtTrigger.next();
}, 200);
}

onCaptureEvent(event: IDemoNgComponentEventType) {
console.log(event);
}
}
```
29 changes: 25 additions & 4 deletions src/angular-datatables.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
* found in the LICENSE file at https://raw.githubusercontent.com/l-lin/angular-datatables/master/LICENSE
*/

import { Directive, ElementRef, Input, OnDestroy, OnInit } from '@angular/core';
import { Directive, ElementRef, Input, OnDestroy, OnInit, Renderer2, ViewContainerRef } from '@angular/core';
import { Subject } from 'rxjs';
import { ADTSettings } from './models/settings';
import { ADTSettings, ADTTemplateRefContext } from './models/settings';

@Directive({
selector: '[datatable]'
Expand Down Expand Up @@ -37,7 +37,11 @@ export class DataTableDirective implements OnDestroy, OnInit {
// Only used for destroying the table when destroying this directive
private dt: DataTables.Api;

constructor(private el: ElementRef) { }
constructor(
private el: ElementRef,
private vcr: ViewContainerRef,
private renderer: Renderer2
) { }

ngOnInit(): void {
if (this.dtTrigger) {
Expand All @@ -59,6 +63,7 @@ export class DataTableDirective implements OnDestroy, OnInit {
}

private displayTable(): void {
const self = this;
this.dtInstance = new Promise((resolve, reject) => {
Promise.resolve(this.dtOptions).then(dtOptions => {
// Using setTimeout as a "hack" to be "part" of NgZone
Expand All @@ -69,7 +74,7 @@ export class DataTableDirective implements OnDestroy, OnInit {
if (dtOptions.columns) {
const columns = dtOptions.columns;
// Filter columns with pipe declared
const colsWithPipe = columns.filter(x => x.ngPipeInstance);
const colsWithPipe = columns.filter(x => x.ngPipeInstance && !x.ngTemplateRef);
// Iterate
colsWithPipe.forEach(el => {
const pipe = el.ngPipeInstance;
Expand All @@ -83,6 +88,22 @@ export class DataTableDirective implements OnDestroy, OnInit {
// Apply transformed string to <td>
$(rowFromCol).text(rowValAfter);
});

// Filter columns using `ngTemplateRef`
const colsWithTemplate = columns.filter(x => x.ngTemplateRef && !x.ngPipeInstance);
colsWithTemplate.forEach(el => {
const { ref, context } = el.ngTemplateRef;
// get <td> element which holds data using index
const index = columns.findIndex(e => e.data == el.data);
const cellFromIndex = row.childNodes.item(index);
// render onto DOM
// finalize context to be sent to user
const _context = Object.assign({}, context, context?.userData, {
adtData: data
});
const instance = self.vcr.createEmbeddedView(ref, _context);
self.renderer.appendChild(cellFromIndex, instance.rootNodes[0]);
});
}

// run user specified row callback if provided.
Expand Down
16 changes: 15 additions & 1 deletion src/models/settings.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
import { PipeTransform } from '@angular/core';
import { PipeTransform, TemplateRef } from '@angular/core';

export interface ADTSettings extends DataTables.Settings {
columns?: ADTColumns[];
}
export interface ADTColumns extends DataTables.ColumnSettings {
/** Set instance of Angular pipe to transform the data of particular column */
ngPipeInstance?: PipeTransform;
/** Set `TemplateRef` to transform the data of this column */
ngTemplateRef?: ADTTemplateRef;
}

export interface ADTTemplateRef {
/** `TemplateRef` to work with */
ref: TemplateRef<any>;
/** */
context?: ADTTemplateRefContext;
}

export interface ADTTemplateRefContext {
captureEvents: Function;
userData?: any;
}