Extending MatRowDef

二次信任 提交于 2020-01-04 02:12:06

问题


I want to display some custom component/html between two rows on click. I believe the quick and easy solution would be to use the event from the click handler and manipulate the DOM directly however I'd like to do it the angular way if possible.

For inspiration I first looked at this article on extending structural directive. It was of limited use though since *matRowDef isn't supposed to be used on its own, but in conjunction with other elements as part of the material table. I then went to have a look at the source code directly and tried to mimic the way MatRowDef extended CdkRowDef and ended up with this:

@Directive({
  selector: '[expandableRowDef]',
  providers: [
    {provide: MatRowDef, useExisting: ExpandableRowDirective},
    {provide: CdkRowDef, useExisting: ExpandableRowDirective}
  ],
  inputs: ['columns: expandableRowDefColumns', 'when: expandableRowDefWhen']
})
export class ExpandableRowDirective<T> extends MatRowDef<T> {
  constructor(template: TemplateRef<any>,
              viewContainer: ViewContainerRef,
              _differs: IterableDiffers) {
                super(template, _differs);
              }
}

I then simply switched *matRowDef="..." with *expandableRowDef="...", it compiles fine and doesn't fail at runtime.

Where should I take it from here to edit the DOM inside the created mat-row element?


回答1:


I had a basic requirement for something similar a while ago, and managed to put something together based off the discussion and broken plunkr on this thread.

It essentially works by creating a custom component using ComponentFactory and then using @ViewChildren to get a list of the table rows, and on click of the selected table row inserts the custom component at the specified index passing in the row data using @Input:

Template:

<mat-row *matRowDef="let row; columns: displayedColumns; let index = index" 
  (click)="insertComponent(index)"
  #tableRow  
  matRipple>
</mat-row>

Component:

export class TableBasicExample {

  displayedColumns = ['position', 'name', 'weight', 'symbol'];
  dataSource = new MatTableDataSource<Element>(ELEMENT_DATA);

  @ViewChildren('tableRow', { read: ViewContainerRef }) rowContainers;
  expandedRow: number;

  constructor(private resolver: ComponentFactoryResolver) { }

  insertComponent(index: number) {
    if (this.expandedRow != null) {
      // clear old content
      this.rowContainers.toArray()[this.expandedRow].clear();
    }

    if (this.expandedRow === index) {
      this.expandedRow = null;
    } else {
      const container = this.rowContainers.toArray()[index];
      const factory: ComponentFactory<any> = this.resolver.resolveComponentFactory(InlineMessageComponent);
      const inlineComponent = container.createComponent(factory);

      inlineComponent.instance.user = this.dataSource.data[index].name;
      inlineComponent.instance.weight = this.dataSource.data[index].weight;
      this.expandedRow = index;
    }
  }
}

Custom component which gets inserted between the rows:

@Component({
  selector: 'app-inline-message',
  template: 'Name: {{ user }} | Progress {{ progress}}%',
  styles: [`...`]
})
export class InlineMessageComponent {
  @Input() user: string;
  @Input() weight: string;
}

I have created a basic working demo of this on StackBlitz here. Since this functionality is not officially supported yet, doing it in this way may eventually break down the line, however the Angular Material team does have something similar in the pipeline.



来源:https://stackoverflow.com/questions/47693876/extending-matrowdef

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!