【问题标题】:Angular, how parse template from string and pass current context variablesAngular,如何从字符串解析模板并传递当前上下文变量
【发布时间】:2017-10-19 19:31:42
【问题描述】:

我制作了一个简单的组件来创建表格:

@Component({
  selector: 'admin-table',
  template: `
    <table class='table table-bordered'>
      <thead>
      <th *ngFor='let column of columns'>
        {{ column.label }}
      </th>
      </thead>
      <tbody>
      <tr *ngFor="let record of records">
        <td *ngFor='let column of columns' [innerHTML]="fieldContent(column, record) | safeHtml">
        </td>
      </tr>
      </tbody>
    </table>
  `,
})
export class AdminTableComponent {

  @Input() columns: AdminTableColumn[];

  @Input() records: {}[];

  fieldContent(column: AdminTableColumn, record: {}) {
    if (column.template) {
      //TODO: parse the template and pass current record as argument
      return column.template;
    }

    return record[column.field];
  }
}

和其他组件使用上述组件创建产品表

@Component({
  selector: 'product-admin',
  template: `
    <h1>Products</h1>
    <admin-table [columns]="columns" [records]="products"></admin-table>
  `,
  providers: [ProductService],
})
export class ProductAdminComponent implements OnInit {

  products: Product[];

  columns: AdminTableColumn[] = [
    {
      field: 'id',
      label: 'SKU',
    },
    {
      field: 'name',
      label: 'Name',
      template: '<strong>{{record.name}}</strong>',
    }
  ];
}

如您所见,AdminTableColumn 有一个名为template 的附加选项,用于使用模板设置单元格的值。但是当尝试渲染我得到的值 {{record.name}} 而不是真实的产品名称时,我不能这样做。

我需要解析在template 选项中输入的值,以允许使用诸如{{record.name}}&lt;some-component [title]="record.name"&gt;&lt;/some-component> 之类的角度声明,以创建丰富的表格。

换句话说,存在类似render(template, { record: record })

【问题讨论】:

  • 您也许可以使用[innerHTML] 属性绑定。值得一试stackoverflow.com/questions/34936027/…
  • 目前我正在使用[innerHTML],请参见[innerHTML]="fieldContent(column, record) | safeHtml" 行,但内容按原样(原始)呈现,但我需要传递一些变量,例如record
  • 尝试注入ChangeDetectorRef,然后调用detectChanges()。或者另一种方法似乎是注入NgZone 然后执行run()
  • 我不确定如何以及在何处使用 ChangeDetectorRefNgZone。我是 Angular 的初学者,这是我的第一个项目,你能举个例子吗?谢谢

标签: angular angular2-template


【解决方案1】:

您可以为此目的构建特殊指令:

@Directive({
  selector: '[compile]'
})
export class CompileDirective implements OnChanges {
  @Input() compile: string;
  @Input() compileContext: any;

  compRef: ComponentRef<any>;

  constructor(private vcRef: ViewContainerRef, private compiler: Compiler) {}

  ngOnChanges() {
    if(!this.compile) {
      if(this.compRef) {
        this.updateProperties();
        return;
      }
      throw Error('You forgot to provide template');
    }

    this.vcRef.clear();
    this.compRef = null;

    const component = this.createDynamicComponent(this.compile);
    const module = this.createDynamicModule(component);
    this.compiler.compileModuleAndAllComponentsAsync(module)
      .then((moduleWithFactories: ModuleWithComponentFactories<any>) => {
        let compFactory = moduleWithFactories.componentFactories.find(x => x.componentType === component);

        this.compRef = this.vcRef.createComponent(compFactory);
        this.updateProperties();
      })
      .catch(error => {
        console.log(error);
      });
  }

  updateProperties() {
    for(var prop in this.compileContext) {
      this.compRef.instance[prop] = this.compileContext[prop];
    }
  }

  private createDynamicComponent (template:string) {
    @Component({
      selector: 'custom-dynamic-component',
      template: template,
    })
    class CustomDynamicComponent {}
    return CustomDynamicComponent;
  }

  private createDynamicModule (component: Type<any>) {
    @NgModule({
      // You might need other modules, providers, etc...
      // Note that whatever components you want to be able
      // to render dynamically must be known to this module
      imports: [CommonModule],
      declarations: [component]
    })
    class DynamicModule {}
    return DynamicModule;
  }
}

管理组件

@Component({
  selector: 'admin-table',
  template: `
    <table class='table table-bordered'>
      <thead>
      <th *ngFor='let column of columns'>
        {{ column.label }}
      </th>
      </thead>
      <tbody>
      <tr *ngFor="let record of records">
        <td *ngFor='let column of columns'>
          <ng-container *ngIf="column.template as tmpl; else staticTmpl">
            <ng-container *compile="tmpl; context: { record: record }"></ng-container>
          </ng-container>
          <ng-template #staticTmpl>{{record[column.field]}}</ng-template>
        </td>
      </tr>
      </tbody>
    </table>
  `,
})
export class AdminTableComponent {
  @Input() columns: any[];

  @Input() records: {}[];
}

Plunker Example

另见

【讨论】:

    【解决方案2】:

    我很确定 Angular 会清理通过 innerHtml 注入的 html,因此您的字符串插值将无法在那里工作。

    您可以尝试在 fieldContent 函数中解析模板并直接添加记录的键。

    这是一个使用正则表达式替换所有 {{record[key]}} 实例的示例,无论键是什么,并返回插入的字符串以注入您的 Html。

    @Component({
      selector: 'admin-table',
      template: `
        <table class='table table-bordered'>
          <thead>
          <th *ngFor='let column of columns'>
            {{ column.label }}
          </th>
          </thead>
          <tbody>
          <tr *ngFor="let record of records">
            <td *ngFor='let column of columns' [innerHTML]="fieldContent(column, record) | safeHtml">
            </td>
          </tr>
          </tbody>
        </table>
      `,
    })
    export class AdminTableComponent {
    
      @Input() columns: AdminTableColumn[];
    
      @Input() records: {}[];
    
      fieldContent(column: AdminTableColumn, record: {}) {
        if (column.template) {
          let template = column.template;
    
          // Go through the keys and replace all isntances in the field.
          // Note that this is strict and will not replace {{ record.name }}
          // You may want to add additional regexp to do that.
    
          Object.keys(record).forEach(key => {
            template = template.replace(new RegExp(`{{record.${key}}}`, 'g'), 'some name');
          });
    
          return template;
        }
    
        return record[column.field];
      }
    
    }
    

    【讨论】:

    • 感谢您的回答,但该方法已将其丢弃,因为无法在模板中使用 pipe 或任何其他组件。它最像我需要的渲染。
    猜你喜欢
    • 2012-09-30
    • 2021-09-22
    • 1970-01-01
    • 1970-01-01
    • 2011-05-15
    • 2014-08-20
    • 2014-03-10
    • 1970-01-01
    • 2016-03-13
    相关资源
    最近更新 更多