1

从我的组件(例如 Component)中,我正在尝试实例化一个 Angular 组件(例如 CustomComponent),设置一些属性,然后将其发送到一个表(例如 CustomTable)进行渲染,但我不断得到[object HTMLElement]而不是表格单元格中呈现的元素。这是我的设置:

组件.html

<custom-table [data]="tableData"...></custom-table>

<custom-component #rowDetailTemplate></custom-component>

组件.ts

@Input() data: Array<CustomDataSource>;
@ViewChild('rowDetailTemplate') template: ElementRef;

public tableData: Array<CustomTableData> = new Array<CustomTableData>();

...

private mapper(dataSource: CustomDataSource): CustomTableData {
    var detailComponent = this.template.nativeElement;
    detailComponent.phone = dataSource.phone;

    var tableRow = new CustomTableData();
    tableRow.textColumn = "test";
    tableRow.detailComponent = detailComponent;

    return tableRow;
}

自定义组件.html

<div>
    <span>{{phone}}</span>
</div>

自定义组件.ts

@Component({
    selector: `[custom-component]`,
    templateUrl: 'CustomComponent.html'
})
export class CustomComponent {
    @Input() phone: string;
}

自定义表格.html

<mat-table [dataSource]="dataSource">
    <ng-container matColumnDef...>
        <mat-cell *matCellDef="let element;">
            <div [innerHTML]="element.textColumn"></div>
            <div [innerHTML]="element.detailComponent"></div>
        </mat-cell>
    </ng-container>
</mat-table>

我的文本列渲染得很好,只是custom-component没有正确渲染。

有什么建议么?

请注意,CustomTable 需要能够接受任何类型的组件/元素detailComponent,而不仅仅是我的 CustomComponent。

4

1 回答 1

0

我没有尝试将组件传递到表格中,而是最终将表格传递给了一个 ComponentFactory,然后表格将负责从工厂实例化组件并在表格完成加载数据后将其附加到占位符(否则它会尝试将组件附加到尚不存在的占位符)。

这是我最终得到的结果:

组件.html

<custom-table [data]="tableData"...></custom-table>

组件.ts

@Input() data: Array<CustomDataSource>;

public tableData: Array<CustomTableData> = new Array<CustomTableData>();
...
private mapper(dataSource: CustomDataSource): CustomTableData {
    var detailComponentFactory: TableExpandableFactoryColumn = {
            componentFactory: this.componentFactoryResolver.resolveComponentFactory(CustomComponent),
            properties: {
                "phone": dataSource.phone;
            }
        }    

    var tableRow : TableExpandableDataRow = {
        rowId: dataSource.rowID,
        columns: {
            "detailComponentFactory": detailComponentFactory,
            "textColumn": "test"
        }
    }
    return tableRow;
}

自定义组件.html

<div>
    <span>{{phone}}</span>
</div>

自定义组件.ts

@Component({
    selector: `[custom-component]`,
    templateUrl: 'CustomComponent.html'
})
export class CustomComponent {
    @Input() phone: string;
}

自定义表格.html

<mat-table [dataSource]="dataSource">
    <ng-container matColumnDef...>
        <mat-cell *matCellDef="let row;">
            <div [innerHTML]="row.textColumn"></div>
            <div id="detail-placeholder-{{row.internalRowId}}" className="cell-placeholder"></div>
        </mat-cell>
    </ng-container>
</mat-table>

CustomTable.ts(解决方案的核心)

...
@Input() data: any;
public placeholders: { placeholderId: string, factoryColumn: TableExpandableFactoryColumn }[];
public dataSource: MatTableDataSource<any>;
...
constructor(private renderer: Renderer2,
        private injector: Injector,
        private applicationRef: ApplicationRef) {

}
...
public ngOnChanges(changes: SimpleChanges) {
    if (changes['data']) {
        // Wait to load table until data input is available
        this.setTableDataSource();
        this.prepareLoadTableComponents();
    }
}
...
private setTableDataSource() {
    this.placeholders = [];

    this.dataSource = new MatTableDataSource(this.data.map((row) => {
        let rowColumns = {};

        // process data columns
        for (let key in row.columns) {
            if ((row.columns[key] as TableExpandableFactoryColumn).componentFactory != undefined) {
                // store component data in placeholders to be rendered after the table loads
                this.placeholders.push({
                    placeholderId: "detail-placeholder-" + row.rowId.toString(),
                    factoryColumn: row.columns[key]
                });
                rowColumns[key] = "[" + key + "]";
            } else {
                rowColumns[key] = row.columns[key];
            }
        }

        return rowColumns;
    }));
}

private prepareLoadTableComponents() {
    let observer = new MutationObserver((mutations, mo) => this.loadTableComponents(mutations, mo, this));
    observer.observe(document, {
        childList: true,
        subtree: true
    });
}

private loadTableComponents(mutations: MutationRecord[], mo: MutationObserver, that: any) {
    let placeholderExists = document.getElementsByClassName("cell-placeholder"); // make sure angular table has rendered according to data
    if (placeholderExists) {
        mo.disconnect();

        // render all components
        if (that.placeholders.length > 0) {
            that.placeholders.forEach((placeholder) => {
                that.createComponentInstance(placeholder.factoryColumn, placeholder.placeholderId);
            });
        }
    }

    setTimeout(() => { mo.disconnect(); }, 5000); // auto-disconnect after 5 seconds
}

private createComponentInstance(factoryColumn: TableExpandableFactoryColumn, placeholderId: string) {
    if (document.getElementById(placeholderId)) {
        let component = this.createComponentAtElement(factoryColumn.componentFactory, placeholderId);
        // map any properties that were passed along
        if (factoryColumn.properties) {
            for (let key in factoryColumn.properties) {
                if (factoryColumn.properties.hasOwnProperty(key)) {
                    this.renderer.setProperty(component.instance, key, factoryColumn.properties[key]);
                }
            }

            component.changeDetectorRef.detectChanges();
        }
    }
}

private createComponentAtElement(componentFactory: ComponentFactory<any>, placeholderId: string): ComponentRef<any> {
    // create instance of component factory at specified host
    let element = document.getElementById(placeholderId);
    let componentRef = componentFactory.create(this.injector, [], element);
    this.applicationRef.attachView(componentRef.hostView);

    return componentRef;
}

...
export class TableExpandableFactoryColumn {
    componentFactory: ComponentFactory<any>;
    properties: Dictionary<any> | undefined;
}
export class TableExpandableDataRow {
    rowId: string;
    columns: Dictionary<any>;
}
于 2018-11-30T17:13:00.157 回答