Strong-typed
"ng-template let-element"...?
Huh?
Have you ever had this?
Have you ever had this?
<ng-template let-someVar>
<!-- someVar is what?? π€·β-->
</ng-template>
Have you ever had this?
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<!-- Position Column -->
<ng-container matColumnDef="position">
<th mat-header-cell *matHeaderCellDef> No. </th>
<!-- π this is just horrible -->
<td mat-cell *matCellDef="let element"> {{element.position}} </td>
</ng-container>
<!-- for brevity -->
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
Have you ever had this?
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<!-- Position Column -->
<ng-container matColumnDef="position">
<th mat-header-cell *matHeaderCellDef> No. </th>
<!-- π and more like -->
<td mat-cell *matCellDef="let element">
{{element.positionCatchMeIfYouCan}}
</td>
</ng-container>
<!-- for brevity -->
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
Have you ever had this?
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<!-- Position Column -->
<ng-container matColumnDef="position">
<th mat-header-cell *matHeaderCellDef> No. </th>
<!-- π and more like -->
<td mat-cell *matCellDef="let element">
{{element.positionCatchMeIfYouCan}}
</td>
</ng-container>
<!-- for brevity -->
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
Developers want it!
Developers want it!
Developers want it!
Developers want it!
Angular does want to support it
Angular does want to support it
- Compiler-level technical
- Complicated
- Challenging to both internal team and community
Angular does want to support it
<table mat-table [dataSource]="[{ hello: 'world' }]">
<tr mat-cell *matCellDef="let element">
{{ element.test }}
</tr>
</table>
const _ctor1: <T = any>(init: Pick<i1.MatTable<T>, "trackBy" | "dataSource"> & {
multiTemplateDataRows: typeof i1.MatTable.ngAcceptInputType_multiTemplateDataRows;
fixedLayout: typeof i1.MatTable.ngAcceptInputType_fixedLayout;
}) => i1.MatTable<T> = (null!);
/*tcb1*/
function _tcb1(ctx: i0.SomeComponent) { if (true) {
ctx. /*D:ignore*/ /*T:COMPCOMP*/;
var _t1 = document.createElement("table") /*96,149*/;
var _t2 /*T:DIR*/ /*96,149*/ = _ctor1({ "dataSource": ([{
"hello": "world" /*137,144*/
} /*128,146*/] /*127,147*/) /*113,148*/, "trackBy": (null as any), "multiTemplateDataRows": (null as any), "fixedLayout": (null as any) }) /*D:ignore*/;
_t2.dataSource /*114,124*/ = ([{
"hello": "world" /*137,144*/
} /*128,146*/] /*127,147*/) /*113,148*/;
var _t3: i1.MatCellDef /*T:DIR*/ /*152,191*/ = (null!);
var _t4: any = (null!);
{
var _t5 /*182,189*/ = _t4.$implicit /*178,189*/;
var _t6 = document.createElement("tr") /*152,191*/;
"" + (((_t5 /*205,212*/).test /*213,218*/) /*205,218*/);
}
} }
export const IS_A_MODULE = true;
Angular does want to support it
<table mat-table [dataSource]="[{ hello: 'world' }]">
<tr mat-cell *matCellDef="let element">
{{ element.test }}
</tr>
</table>
const _ctor1: <T = any>(init: Pick<i1.MatTable<T>, "trackBy" | "dataSource"> & {
multiTemplateDataRows: typeof i1.MatTable.ngAcceptInputType_multiTemplateDataRows;
fixedLayout: typeof i1.MatTable.ngAcceptInputType_fixedLayout;
}) => i1.MatTable<T> = (null!);
/*tcb1*/
function _tcb1(ctx: i0.SomeComponent) { if (true) {
ctx. /*D:ignore*/ /*T:COMPCOMP*/;
var _t1 = document.createElement("table") /*96,149*/;
var _t2 /*T:DIR*/ /*96,149*/ = _ctor1({ "dataSource": ([{
"hello": "world" /*137,144*/
} /*128,146*/] /*127,147*/) /*113,148*/, "trackBy": (null as any), "multiTemplateDataRows": (null as any), "fixedLayout": (null as any) }) /*D:ignore*/;
_t2.dataSource /*114,124*/ = ([{
"hello": "world" /*137,144*/
} /*128,146*/] /*127,147*/) /*113,148*/;
var _t3: i1.MatCellDef /*T:DIR*/ /*152,191*/ = (null!);
var _t4: any = (null!);
{
var _t5 /*182,189*/ = _t4.$implicit /*178,189*/;
var _t6 = document.createElement("tr") /*152,191*/;
"" + (((_t5 /*205,212*/).test /*213,218*/) /*205,218*/);
}
} }
export const IS_A_MODULE = true;
Angular does want to support it
<table mat-table [dataSource]="[{ hello: 'world' }]">
<tr mat-cell *matCellDef="let element">
{{ element.test }}
</tr>
</table>
const _ctor1: <T = any>(init: Pick<i1.MatTable<T>, "trackBy" | "dataSource"> & {
multiTemplateDataRows: typeof i1.MatTable.ngAcceptInputType_multiTemplateDataRows;
fixedLayout: typeof i1.MatTable.ngAcceptInputType_fixedLayout;
}) => i1.MatTable<T> = (null!);
/*tcb1*/
function _tcb1(ctx: i0.SomeComponent) { if (true) {
ctx. /*D:ignore*/ /*T:COMPCOMP*/;
var _t1 = document.createElement("table") /*96,149*/;
var _t2 /*T:DIR*/ /*96,149*/ = _ctor1({ "dataSource": ([{
"hello": "world" /*137,144*/
} /*128,146*/] /*127,147*/) /*113,148*/, "trackBy": (null as any), "multiTemplateDataRows": (null as any), "fixedLayout": (null as any) }) /*D:ignore*/;
_t2.dataSource /*114,124*/ = ([{
"hello": "world" /*137,144*/
} /*128,146*/] /*127,147*/) /*113,148*/;
var _t3: i1.MatCellDef /*T:DIR*/ /*152,191*/ = (null!);
var _t4: any = (null!);
{
var _t5 /*182,189*/ = _t4.$implicit /*178,189*/;
var _t6 = document.createElement("tr") /*152,191*/;
"" + (((_t5 /*205,212*/).test /*213,218*/) /*205,218*/);
}
} }
export const IS_A_MODULE = true;
Angular does want to support it
<table mat-table [dataSource]="[{ hello: 'world' }]">
<tr mat-cell *matCellDef="let element">
{{ element.test }}
</tr>
</table>
const _ctor1: <T = any>(init: Pick<i1.MatTable<T>, "trackBy" | "dataSource"> & {
multiTemplateDataRows: typeof i1.MatTable.ngAcceptInputType_multiTemplateDataRows;
fixedLayout: typeof i1.MatTable.ngAcceptInputType_fixedLayout;
}) => i1.MatTable<T> = (null!);
/*tcb1*/
function _tcb1(ctx: i0.SomeComponent) { if (true) {
ctx. /*D:ignore*/ /*T:COMPCOMP*/;
var _t1 = document.createElement("table") /*96,149*/;
var _t2 /*T:DIR*/ /*96,149*/ = _ctor1({ "dataSource": ([{
"hello": "world" /*137,144*/
} /*128,146*/] /*127,147*/) /*113,148*/, "trackBy": (null as any), "multiTemplateDataRows": (null as any), "fixedLayout": (null as any) }) /*D:ignore*/;
_t2.dataSource /*114,124*/ = ([{
"hello": "world" /*137,144*/
} /*128,146*/] /*127,147*/) /*113,148*/;
var _t3: i1.MatCellDef /*T:DIR*/ /*152,191*/ = (null!);
var _t4: any = (null!);
{
var _t5 /*182,189*/ = _t4.$implicit /*178,189*/;
var _t6 = document.createElement("tr") /*152,191*/;
"" + (((_t5 /*205,212*/).test /*213,218*/) /*205,218*/);
}
} }
export const IS_A_MODULE = true;
Angular does want to support it
<table mat-table [dataSource]="[{ hello: 'world' }]">
<tr mat-cell *matCellDef="let element">
{{ element.test }}
</tr>
</table>
const _ctor1: <T = any>(init: Pick<i1.MatTable<T>, "trackBy" | "dataSource"> & {
multiTemplateDataRows: typeof i1.MatTable.ngAcceptInputType_multiTemplateDataRows;
fixedLayout: typeof i1.MatTable.ngAcceptInputType_fixedLayout;
}) => i1.MatTable<T> = (null!);
/*tcb1*/
function _tcb1(ctx: i0.SomeComponent) { if (true) {
ctx. /*D:ignore*/ /*T:COMPCOMP*/;
var _t1 = document.createElement("table") /*96,149*/;
var _t2 /*T:DIR*/ /*96,149*/ = _ctor1({ "dataSource": ([{
"hello": "world" /*137,144*/
} /*128,146*/] /*127,147*/) /*113,148*/, "trackBy": (null as any), "multiTemplateDataRows": (null as any), "fixedLayout": (null as any) }) /*D:ignore*/;
_t2.dataSource /*114,124*/ = ([{
"hello": "world" /*137,144*/
} /*128,146*/] /*127,147*/) /*113,148*/;
var _t3: i1.MatCellDef /*T:DIR*/ /*152,191*/ = (null!);
var _t4: any = (null!);
{
var _t5 /*182,189*/ = _t4.$implicit /*178,189*/;
var _t6 = document.createElement("tr") /*152,191*/;
"" + (((_t5 /*205,212*/).test /*213,218*/) /*205,218*/);
}
} }
export const IS_A_MODULE = true;
What can we do?
What can we do?
Directive
What do we know?
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<!-- Position Column -->
<ng-container matColumnDef="position">
<th mat-header-cell *matHeaderCellDef> No. </th>
<!-- π this is just horrible -->
<td mat-cell *matCellDef="let element"> {{element.position}} </td>
</ng-container>
<!-- for brevity -->
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
Let's do what devs do: hack
import { CdkCellDef } from '@angular/cdk/table';
import { Directive, Input } from '@angular/core';
import { MatCellDef, MatTableDataSource } from "@angular/material/table";
import { Observable } from 'rxjs';
@Directive({
selector: '[matCellDef]', // same selector as MatCellDef
providers: [{ provide: CdkCellDef, useExisting: TypeSafeMatCellDef }],
})
export class TypeSafeMatCellDef<T> extends MatCellDef {
// leveraging syntactic-sugar syntax when we use *matCellDef
@Input() matCellDefDataSource: T[] | Observable<T[]> | MatTableDataSource<T>;
// ngTemplateContextGuard flag to help with the Language Service
static ngTemplateContextGuard<T>(
dir: TypeSafeMatCellDef<T>,
ctx: unknown,
): ctx is { $implicit: T; index: number } {
return true;
}
}
Let's do what devs do: hack
import { CdkCellDef } from '@angular/cdk/table';
import { Directive, Input } from '@angular/core';
import { MatCellDef, MatTableDataSource } from "@angular/material/table";
import { Observable } from 'rxjs';
@Directive({
selector: '[matCellDef]', // same selector as MatCellDef
providers: [{ provide: CdkCellDef, useExisting: TypeSafeMatCellDef }],
})
export class TypeSafeMatCellDef<T> extends MatCellDef {
// leveraging syntactic-sugar syntax when we use *matCellDef
@Input() matCellDefDataSource: T[] | Observable<T[]> | MatTableDataSource<T>;
// ngTemplateContextGuard flag to help with the Language Service
static ngTemplateContextGuard<T>(
dir: TypeSafeMatCellDef<T>,
ctx: unknown,
): ctx is { $implicit: T; index: number } {
return true;
}
}
<!-- ngForOfTrackBy -->
<div *ngFor="let item of items;trackBy: trackByFn"></div>
<!-- ngTemplateOutletContext -->
<ng-container *ngTemplateOutlet="template;context: {}"></ng-container>
Let's do what devs do: hack
import { CdkCellDef } from '@angular/cdk/table';
import { Directive, Input } from '@angular/core';
import { MatCellDef, MatTableDataSource } from "@angular/material/table";
import { Observable } from 'rxjs';
@Directive({
selector: '[matCellDef]', // same selector as MatCellDef
providers: [{ provide: CdkCellDef, useExisting: TypeSafeMatCellDef }],
})
export class TypeSafeMatCellDef<T> extends MatCellDef {
// leveraging syntactic-sugar syntax when we use *matCellDef
@Input() matCellDefDataSource: T[] | Observable<T[]> | MatTableDataSource<T>;
// ngTemplateContextGuard flag to help with the Language Service
static ngTemplateContextGuard<T>(
dir: TypeSafeMatCellDef<T>,
ctx: unknown,
): ctx is { $implicit: T; index: number } {
return true;
}
}
Let's do what devs do: hack
@NgModule({
declarations: [TypeSafeMatCellDef]
})
export class Core/Shell/AppModule {}
Let's do what devs do: hack
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<!-- Position Column -->
<ng-container matColumnDef="position">
<th mat-header-cell *matHeaderCellDef> No. </th>
<!-- πthis is horrible -->
<td mat-cell *matCellDef="let element">
{{element.position}}
</td>
</ng-container>
<!-- for brevity -->
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
Let's do what devs do: hack
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<!-- Position Column -->
<ng-container matColumnDef="position">
<th mat-header-cell *matHeaderCellDef> No. </th>
<!-- πmagic π¦π -->
<td mat-cell *matCellDef="let element;dataSource: dataSource">
{{element.position}}
</td>
</ng-container>
<!-- for brevity -->
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
Let's do what devs do: hack
Let's do what devs do: hack
Reference
Q/A
strong-type-ng-template
By Chau Tran
strong-type-ng-template
- 800