Angular as a parsing pattern from a string and pass the current context variables

I am creating a simple component for creating tables:

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

and another component for creating a product table using the above component

@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>',
    }
  ];
}

As you can see, it AdminTableColumnhas an additional parameter templateto set the cell value using the template. But I can not do this when I try to make the value that I received {{record.name}}instead of the real name of the product.

I need to parse the value entered in a parameter templateto allow the use of angular declarations, such as: {{record.name}}or <some-component [title]="record.name"></some-component>, to create a rich table.

In other words, there is something like render(template, { record: record })

+1
2

:

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

AdminComponent

@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: {}[];
}

.

+2

, Angular html, innerHtml, .

fieldContent .

, regexp {{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];
  }

}
-1

Source: https://habr.com/ru/post/1677575/


All Articles