问题
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