0

I’m trying to enhance an angular table component I created to output formatted cell content instead of just text. I thought a set of format components would be the way to go as they will have to contain html tags (which would be stripped by simply binding to a property). I guess pipes might have been an option but as most of the functionality is in a base component I don't want much in the way of specific code in instances of the table.

So I tried the Angular document on Dynamic Component Loading and created a [format-host] directive, linked it all up as in the doc & tried to debug. The failure is in the component that uses the directive.

let viewContainerRef = this.formatHost.viewContainerRef; 

this fails as formatHost is undefined. The directive is defined in the module.

I'm using the VS2017 angular SPA template, angular 4.2.5 The relevant code in the using component is (kept it short):

export class PagedTableComponent implements OnInit, AfterViewInit{

@Input() pagedTable: PagedTable<any[]>;
@Output() tableState: EventEmitter<TableState> = new EventEmitter<TableState>();
…
@ViewChild(FormatDirective) formatHost: FormatDirective;
…
formatters: Formatter[] = [];

constructor(private componentFactoryResolver: ComponentFactoryResolver) {
}

loadFormatComponent() {
    let componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.formatters[0].component);

    let viewContainerRef = this.formatHost.viewContainerRef;
    viewContainerRef.clear(); //??
    let componentRef = viewContainerRef.createComponent(componentFactory);
}

ngOnInit() {
  …
    //this.formatters.push(new Formatter(ContactFormatComponent, "test"));
    // this.loadFormatComponent();
}

ngAfterViewInit() {
    this.formatters.push(new Formatter(ContactFormatComponent, "test"));
    this.loadFormatComponent();
}

Any idea on why the directive is undefined would be appreciated.

directive:

import { Directive, ViewContainerRef } from '@angular/core';
@Directive({
    selector: '[format-host]',
    })

export class FormatDirective {
    constructor(public viewContainerRef: ViewContainerRef) { }
}

template:

<td *ngFor="let column of pagedTable.tableConfig.columns"
     [ngClass]="{ 'cell-right' : column.justification ===  ColumnJustification.right, 'cell-center' : column.justification === ColumnJustification.center}">
....
<ng-template format-host></ng-template>
</td>
Bod
  • 25
  • 5

1 Answers1

0

Finally got something working - (pasted below if it helps anyone) thanks for the prod to ViewChildren. I can now get a test Format componet to appear with content - but only when I step through the debugger in Chrome. When just in a browser the column is empty. I'm thinking a timing issue (tried setTimeout) but can't fathom the intricasies of Angular.

The template now looks like :

<tr *ngFor="let row of pagedTable.pagedList?.tableData">
    <td *ngFor="let column of pagedTable.tableConfig.columns">
        <div *ngIf="column.format != undefined; else normalCell">                            
            <ng-template avFormatHost rowId="{{row.id}}" formatName="{{column.format}}"></ng-template>
        </div>
        <ng-template #normalCell>{{getCellContent(row, column)}}</ng-template>
    </td>
</tr>

and relevant component code:

 @ViewChildren(FormatHostDirective) formatHostDirectives: QueryList<FormatHostDirective>;

...

loadFormatComponent() {
    setTimeout(() => {
        this.formatHostDirectives.forEach((formatHostDirective) => {

            let rowId = formatHostDirective.rowId;
            let row = { email: 'test email', telephone: '999999' };

            let componentType: any = FormatComponents[formatHostDirective.formatName];
            if (componentType) {
                let componentFactory = this.componentFactoryResolver.resolveComponentFactory(componentType);

                //formatHostDirective.viewContainerRef.clear(); //??
                let componentRef = formatHostDirective.viewContainerRef.createComponent(componentFactory);
                this.components.push(componentRef);
                (<FormatComponent>componentRef.instance).data = row;
            }

        });
    });


}

Is the use of timeout correct? or should it be elsewhere? or something else entirely. How do you decide what a safe delay is?

Bod
  • 25
  • 5
Craig
  • 517
  • 1
  • 9
  • 17
  • I added a delay of 600 which seems to do the trick. I'l still insure of the selection of a sensible value though. Is there a performance hit if a large value is chosen? I do occasionally see the column empty then it appears filled. – Bod Mar 17 '18 at 15:41
  • What is `avFormatHost`? – FIL Nov 22 '18 at 15:53
  • I had setup a directive: import { Directive, ViewContainerRef, Input, TemplateRef, ElementRef } from '@angular/core'; @Directive({ selector: '[avFormatHost]', }) export class FormatHostDirective { @Input() formatName: string; @Input() rowKey: string; constructor(public viewContainerRef: ViewContainerRef) { } } – Craig Nov 27 '18 at 07:41