如何使用字符串创建 DOM 元素使 angular 点击绑定工作?

How to make angular click bindings work using string to create DOM elements?

我应该如何使 angular 绑定在动态添加的 DOM 元素上起作用?我正在使用 ag-grid (ng2) 作为数据表。 基于某些条件,我使用不同的列渲染。

      columnDef.cellRenderer = function (params) {
        return `<div><i class='fa ${params.value}'></i></div>`;
      };

在此,我想像这样给图标添加点击功能:

      columnDef.cellRenderer = function (params) {
        return `<div><i (click)='iconClicked()' class='fa ${params.value}'></i></div>`;
      };

如何让这些 click bindingsangular 2 中工作?

正如 micronyks 所说,您可以使用 ComponentFactoryResolver(不是 DynamicComponentResolver,它不再存在)动态创建组件,您可以在 S.O 上找到示例。 (例如 )。

但它不适用于您的情况,因为:

  • 您不想创建整个组件,而只想向现有组件添加一段标记。
  • 您不是创建标记的人,ag-grid 是。

既然你在 ag-grid 上下文中,你为什么不使用 ag-grid 的 API 而不是 Angular 的?快速查看 their docs 表明网格有一个 属性 onCellClicked(params) 接受一个函数回调,当一个单元格被点击时调用。

然后希望您可以从该回调中触发一些 Angular 代码。

如果你想要 ag-grid 中的动态 Angular 2 个组件,你不需要直接使用 ComponentFactoryResolver - 你可以使用 ag-grid 提供的 Angular 2 个接口。

假设您有以下简单组件:

// CubeComponent
import {Component} from '@angular/core';
import {AgRendererComponent} from 'ag-grid-ng2/main';

@Component({
    selector: 'cube-cell',
    template: `{{valueCubed()}}`
})
export class CubeComponent implements AgRendererComponent {
    private params:any;
    private cubed:number;

    // called on init
    agInit(params:any):void {
        this.params = params;
        this.cubed = this.params.data.value * this.params.data.value * this.params.data.value;
    }

    public valueCubed():number {
        return this.cubed;
    }
}

// Square Component
import {Component} from '@angular/core';

import {AgRendererComponent} from 'ag-grid-ng2/main';

@Component({
    selector: 'square-cell',
    template: `{{valueSquared()}}`
})
export class SquareComponent implements AgRendererComponent {
    private params:any;

    agInit(params:any):void {
        this.params = params;
    }

    public valueSquared():number {
        return this.params.value * this.params.value;
    }
}

// from-component.component.html
<div style="width: 200px;">
    <button (click)="changeComponentType()">Change Component Type</button>
    <ag-grid-ng2 #agGrid style="width: 100%; height: 350px;" class="ag-fresh"
                 [gridOptions]="gridOptions">
    </ag-grid-ng2>
</div>

// from-component.component.ts
import {Component} from '@angular/core';

import {GridOptions} from 'ag-grid/main';
import {SquareComponent} from "./square.component";
import {CubeComponent} from "./cube.component";

@Component({
    moduleId: module.id,
    selector: 'ag-from-component',
    templateUrl: 'from-component.component.html'
})
export class FromComponentComponent {
    public gridOptions:GridOptions;
    private currentComponentType : any = SquareComponent;

    constructor() {
        this.gridOptions = <GridOptions>{};
        this.gridOptions.rowData = this.createRowData();
        this.gridOptions.onGridReady = () => {
            this.setColumnDefs();
        }
    }

    public changeComponentType() {
        this.currentComponentType = this.currentComponentType === SquareComponent ? CubeComponent : SquareComponent;
        this.setColumnDefs();
    }

    private createRowData() {
        let rowData:any[] = [];

        for (var i = 0; i < 15; i++) {
            rowData.push({
                value: i
            });
        }

        return rowData;
    }

    private setColumnDefs():void {
        this.gridOptions.api.setColumnDefs([
            {
                headerName: "Dynamic Component",
                field: "value",
                cellRendererFramework: this.currentComponentType,
                width: 200
            }
        ])
    }
}

// app.module.ts
import {NgModule} from "@angular/core";
import {BrowserModule} from "@angular/platform-browser";
import {RouterModule, Routes} from "@angular/router";
// ag-grid
import {AgGridModule} from "ag-grid-ng2/main";
// application
import {AppComponent} from "./app.component";
// from component
import {FromComponentComponent} from "./from-component.component";
import {SquareComponent} from "./square.component";
import {CubeComponent} from "./cube.component";

const appRoutes:Routes = [
    {path: 'from-component', component: FromComponentComponent, data: {title: "Using Dynamic Components"}},
    {path: '', redirectTo: 'from-component', pathMatch: 'full'}
];

@NgModule({
    imports: [
        BrowserModule,
        RouterModule.forRoot(appRoutes),
        AgGridModule.withComponents(
            [
                SquareComponent,
                CubeComponent,
            ])
    ],
    declarations: [
        AppComponent,
        FromComponentComponent,
        SquareComponent,
        CubeComponent
    ],
    bootstrap: [AppComponent]
})
export class AppModule {
}

这里的按钮将允许您在两个组件之间动态切换 - 这显然可以根据您的某些条件在运行时完成。

另请注意,拥有一个组件可能更简单,并在实际输出中执行条件逻辑 - 例如:

// called on init
agInit(params:any):void {
    this.params = params;

    if(this.params.isCube) {
        // cubed
        this.value = this.params.data.value * this.params.data.value * this.params.data.value;
    } else {
        // square
        this.value = this.params.data.value * this.params.data.value;
    }
}

您可以在此处找到有关如何将 Angular 2 与 ag-Grid 一起使用的更多信息:https://www.ag-grid.com/best-angular-2-data-grid

有了这些,您可以参考 https://github.com/ceolter/ag-grid-ng2-example/blob/master/systemjs_aot/app/clickable.component.ts for an example of using a component that supports click events in the grid. For all the examples take a look at https://github.com/ceolter/ag-grid-ng2-example,其中提供了许多示例,以及如何使用 systemjs、webpack 或 angular cli

打包它们

您可以像 中解释的那样动态构建组件,以便能够通过 HTML 与 Angular 事件和值绑定。

或者您可以使用命令式事件绑定

export class MyComponent {
  constructor(private elRef:ElementRef) {}

  someMethod() {
    columnDef.cellRenderer = (params) => {
      return `<div><i id="addClick" class='fa ${params.value}'></i></div>`;
      this.elRef.nativeElement.querySelector('#addClick')
      .addEventListener('click',  this.iconClicked.bind(this));
    };
  }

  iconClicked(e) {
  }
}