I've built a datatable compoonent where I'm using a structural directive to pass a rowTemplate:
<bs-datatable [data]="artists">
<tr *bsRowTemplate="let artist">
<td class="text-nowrap">{{ artist.name }}</td>
<td class="text-nowrap">{{ artist.yearStarted }}</td>
<td class="text-nowrap">{{ artist.yearQuit }}</td>
</tr>
</bs-datatable>
I'm trying to provide type inference in my custom structural directive, so the generic type of the structural directive should be infered from the parent component. According to the documentation, you should be able to simply implement a ngTemplateContextGuard
:
@Component({ ... })
export class DatatableComponent<TData> {
@Input() data: TData[] = [];
}
@Directive({ selector: '[appDatatableRow]' })
export class DatatableRowDirective<TData> {
constructor(public datatable: DatatableComponent<TData>, public template: TemplateRef<TData>) { }
static ngTemplateContextGuard<TData>(dir: DatatableRowDirective<TData>, ctx: unknown)
: ctx is DatatableRowContext<TData> {
return true;
}
}
The type inference on the component works as expected:
But I can't get the structural directive to infer this generic type:
The goal is obviously to have the $implicit
variable inferred in VS Code:
Why is this not working as I would expect? What am I still missing? I want the structural directive to infer the generic type of the parent datatable.
InstanceOf
- Let
- Select2
(similar problem)Similar articles/discussions:
I ended up using the of
notation of structural directives:
@Directive({ selector: '[bsRowTemplate]' })
export class BsRowTemplateDirective<TData> {
constructor(private datatableComponent: BsDatatableComponent<TData>, templateRef: TemplateRef<BsRowTemplateContext<TData>>) {
this.datatableComponent.rowTemplate = templateRef;
}
@Input() set bsRowTemplateOf(value: PaginationResponse<TData> | undefined) {
this.datatableComponent.data = value;
}
public static ngTemplateContextGuard<TData>(
dir: BsRowTemplateDirective<TData>,
ctx: any
): ctx is BsRowTemplateContext<Exclude<TData, false | 0 | '' | null | undefined>> {
return true;
}
}
export class BsRowTemplateContext<TData = unknown> {
public $implicit: TData = null!;
}
Datatable
@Component({
selector: 'bs-datatable',
...
})
export class BsDatatableComponent<TData> {
...
rowTemplate?: TemplateRef<BsRowTemplateContext<TData>>;
data?: PaginationResponse<TData>;
}
Usage
<bs-datatable #tabel [(settings)]="settings" (settingsChange)="loadArtists()">
<div *bsDatatableColumn="'Name'; sortable: true">
1. Artist
</div>
<div *bsDatatableColumn="'YearStarted'; sortable: true">
2. Year started
</div>
<div *bsDatatableColumn="'YearQuit'; sortable: true">
3. Year quit
</div>
<tr *bsRowTemplate="let artist of artists">
<td class="text-nowrap">{{ artist.name }}</td>
<td class="text-nowrap">{{ artist.yearStarted }}</td>
<td class="text-nowrap">{{ artist.yearQuit }}</td>
</tr>
</bs-datatable>