创建组件实例并将其传递为另一个呈现为[object HTMLelement]的组件 [英] Creating instance of component and passing to another component rendering as [object HTMLelement]

查看:104
本文介绍了创建组件实例并将其传递为另一个呈现为[object HTMLelement]的组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

From my component (ex. Component), I'm trying to instantiate an Angular component (ex. CustomComponent), set some properties, and send it over to a table (ex. CustomTable) for rendering, but I keep getting [object HTMLElement] instead of the rendered element in the table cell. Here's my setup:

Component.html

Component.html

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

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

Component.ts

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;
}

CustomComponent.html

CustomComponent.html

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

CustomComponent.ts

CustomComponent.ts

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

CustomTable.html

CustomTable.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不能正确呈现.

My text column renders fine, its just the custom-component that isn't rendering properly.

有什么建议吗?

请注意,CustomTable必须能够接受detailComponent中的任何类型的组件/元素,而不仅仅是我的CustomComponent.

Note that CustomTable needs to be able to accept any type of component/element in detailComponent, not just my CustomComponent.

推荐答案

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

Instead of trying to pass the component into the table, I ended up passing the table a ComponentFactory, then the table would take care of instantiating the component from a factory and attaching it to a placeholder once the table was done loading the data (otherwise it would try to attach the component to a placeholder that doesn't exist yet).

这就是我最后得到的:

Component.html

Component.html

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

Component.ts

Component.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;
}

CustomComponent.html

CustomComponent.html

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

CustomComponent.ts

CustomComponent.ts

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

CustomTable.html

CustomTable.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(解决方案的实质)

CustomTable.ts (the meat of the solution)

...
@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>;
}

这篇关于创建组件实例并将其传递为另一个呈现为[object HTMLelement]的组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆