代码之家  ›  专栏  ›  技术社区  ›  TabsNotSpaces

创建组件的实例并将其作为[对象htmlelement]传递给另一个组件呈现

  •  0
  • TabsNotSpaces  · 技术社区  · 7 年前

    从我的组件(如组件)中,我试图实例化一个角度组件(如自定义组件),设置一些属性,并将其发送到一个表(如自定义表)进行渲染,但我一直在获取 [object HTMLElement] 而不是表单元格中呈现的元素。以下是我的设置:

    组件HTML

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

    组件

    @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 不只是我的自定义组件。

    1 回复  |  直到 7 年前
        1
  •  0
  •   TabsNotSpaces    7 年前

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

    以下是我的结论:

    组件HTML

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

    组件

    @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>;
    }
    
    推荐文章