Angular 如何将数据从指令传递到父组件
Angular How to pass data from Directive to Parent component
我的要求是,我有一个可重复使用的 table,我想在多个地方使用它。无论我在哪里使用这个 table,我都会传递 table 数据和列。
现在,在某些父组件中,我想在用户单击 table 行或双击 table 行时获取数据。
为此,我使用@HostListener 创建了一个指令。
当我在 table 行上使用指令时,我可以在指令中获取点击的行数据,但是当我发出此数据时,我没有在父级中获取它。
table.component.html
<section class="example-section">
<div class="cdx-table-wrapper cdx-table-wrapper-fixed">
<table mat-table [dataSource]="tableData" class="mat-elevation-z8">
<!--- Note that these columns can be defined in any order.
The actual rendered columns are set as a property on the row definition" -->
<ng-container *ngFor="let disCol of tableColumns; let colIndex = index" matColumnDef="{{disCol}}">
<th mat-header-cell *matHeaderCellDef>{{disCol}}</th>
<td mat-cell *matCellDef="let element">{{element[disCol]}}</td>
</ng-container>
<tr class="persues-table-row" mat-header-row *matHeaderRowDef="tableColumns"></tr>
<tr class="persues-table-row" mat-row *matRowDef="let row; columns: tableColumns;" appTableEvent [rowData]="row"></tr>
</table>
</div>
</section>
table-events.directive.ts
import { Directive, HostListener, Input, Output, EventEmitter } from '@angular/core';
@Directive({
selector: '[appTableEvent]'
})
export class TableEventDirective {
@Input() rowData;
@Output()
clickHandler: EventEmitter<any> = new EventEmitter();
constructor() { }
@HostListener('click', ['$event'])
onClickHandler(event: MouseEvent) {
this.clickHandler.emit(this);
}
}
父组件
<app-table
[tableData]="dataSourceBase"
[tableColumns]="displayedColumnsBase"
(clickHandler)="onRowClick($event)">
</app-table>
父组件ts
onRowClick(event) {
console.log('onRowClick', event)
}
我没有在 onRowClick()
方法中获取数据。
现在有些情况下我只想使用 table 来显示数据,在那种情况下我不想应用指令。
提前致谢
您还需要在 table 组件上创建一个 HostListener
:
@Component({
selector: 'app-table'
})
export class TableComponent {
@Output()
clickHandler: EventEmitter<MouseEvent> = new EventEmitter();
}
并从您的 appTableEvent
指令中发出:
<tr class="persues-table-row" mat-row
*matRowDef="let row; columns: tableColumns;" [rowData]="row"
appTableEvent (clickHandler)="clickHandler.emit($event)">
</tr>
如果您计划有多个这样的案例,您也可以选择以下解决方案。你仍然需要 TableComponent
上的 clickHandler
就像我之前展示的那样,但是你可以像这样更新你的指令:
@Directive({
selector: '[appTableEvent]'
})
export class TableEventDirective {
@Input() rowData;
constructor(@Host() private table: TableComponent) { }
@HostListener('click', ['$event'])
onClickHandler(event: MouseEvent) {
this.table.clickHandler.emit(this);
}
}
这样您就不必将点击处理程序添加到 <tr>
元素
工作演示 在此 StackBlitz Link
这里,可以用service
和rxjs - Behaviorsubject
。为此,您必须创建一项服务,然后在该服务中定义您的 Behaviorsubject
和所有常见的通信相关内容。您必须在组件和指令中注入这项服务。这样一来,不必 @Output()
多级,这样做的一个好处是您可以与组件的任何父级别进行通信,而不管事件链如何。
table.component.html
<table class="table table-dark table-striped table-hover">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Company Name</th>
<th scope="col">Brand</th>
<th scope="col">Engine Type</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of tableService.tableData; let i = index" appTableRow [rowData]="row">
<th scope="row">{{row.id}}</th>
<td>{{row.companyName}}</td>
<td>{{row.brand}}</td>
<td>{{row.engineType}}</td>
</tr>
</tbody>
</table>
<div *ngIf="selectedRow | async as rowSelect" class="alert alert-primary">
<pre>
{{rowSelect | json}}
</pre>
</div>
table.component.ts
export class TableComponent implements OnInit {
selectedRow;
constructor(private tableService: TableDataService) { }
ngOnInit() {
this.selectedRow = this.tableService.getTableRow();
}
}
directive.ts
@Directive({
selector: '[appTableRow]'
})
export class TableRowDirective {
@Input() rowData;
constructor(private tableRowSerivce: TableDataService) { }
@HostListener('click', ['$event'])
onClickHandler(event: MouseEvent) {
this.tableRowSerivce.tableRowData.next(this.rowData);
}
}
table-service.ts
@Injectable()
export class TableDataService {
tableData = [{
id: 1,
companyName: 'Audi',
brand: 'A4',
engineType: 'Diesel'
},{
id: 2,
companyName: 'Rolls-Roys',
brand: 'R4',
engineType: 'Diesel'
},{
id: 3,
companyName: 'Hyundai',
brand: 'Kona',
engineType: 'Electric'
},{
id: 4,
companyName: 'Ford',
brand: 'Endevour',
engineType: 'Diesel'
}];
tableRowData : BehaviorSubject<any> = new BehaviorSubject<any>({});
tableRowData$ = this.tableRowData.asObservable();
constructor() { }
getTableRow(): Observable<any>{
return this.tableRowData$;
}
}
我的要求是,我有一个可重复使用的 table,我想在多个地方使用它。无论我在哪里使用这个 table,我都会传递 table 数据和列。
现在,在某些父组件中,我想在用户单击 table 行或双击 table 行时获取数据。
为此,我使用@HostListener 创建了一个指令。
当我在 table 行上使用指令时,我可以在指令中获取点击的行数据,但是当我发出此数据时,我没有在父级中获取它。
table.component.html
<section class="example-section">
<div class="cdx-table-wrapper cdx-table-wrapper-fixed">
<table mat-table [dataSource]="tableData" class="mat-elevation-z8">
<!--- Note that these columns can be defined in any order.
The actual rendered columns are set as a property on the row definition" -->
<ng-container *ngFor="let disCol of tableColumns; let colIndex = index" matColumnDef="{{disCol}}">
<th mat-header-cell *matHeaderCellDef>{{disCol}}</th>
<td mat-cell *matCellDef="let element">{{element[disCol]}}</td>
</ng-container>
<tr class="persues-table-row" mat-header-row *matHeaderRowDef="tableColumns"></tr>
<tr class="persues-table-row" mat-row *matRowDef="let row; columns: tableColumns;" appTableEvent [rowData]="row"></tr>
</table>
</div>
</section>
table-events.directive.ts
import { Directive, HostListener, Input, Output, EventEmitter } from '@angular/core';
@Directive({
selector: '[appTableEvent]'
})
export class TableEventDirective {
@Input() rowData;
@Output()
clickHandler: EventEmitter<any> = new EventEmitter();
constructor() { }
@HostListener('click', ['$event'])
onClickHandler(event: MouseEvent) {
this.clickHandler.emit(this);
}
}
父组件
<app-table
[tableData]="dataSourceBase"
[tableColumns]="displayedColumnsBase"
(clickHandler)="onRowClick($event)">
</app-table>
父组件ts
onRowClick(event) {
console.log('onRowClick', event)
}
我没有在 onRowClick()
方法中获取数据。
现在有些情况下我只想使用 table 来显示数据,在那种情况下我不想应用指令。
提前致谢
您还需要在 table 组件上创建一个 HostListener
:
@Component({
selector: 'app-table'
})
export class TableComponent {
@Output()
clickHandler: EventEmitter<MouseEvent> = new EventEmitter();
}
并从您的 appTableEvent
指令中发出:
<tr class="persues-table-row" mat-row
*matRowDef="let row; columns: tableColumns;" [rowData]="row"
appTableEvent (clickHandler)="clickHandler.emit($event)">
</tr>
如果您计划有多个这样的案例,您也可以选择以下解决方案。你仍然需要 TableComponent
上的 clickHandler
就像我之前展示的那样,但是你可以像这样更新你的指令:
@Directive({
selector: '[appTableEvent]'
})
export class TableEventDirective {
@Input() rowData;
constructor(@Host() private table: TableComponent) { }
@HostListener('click', ['$event'])
onClickHandler(event: MouseEvent) {
this.table.clickHandler.emit(this);
}
}
这样您就不必将点击处理程序添加到 <tr>
元素
工作演示 在此 StackBlitz Link
这里,可以用service
和rxjs - Behaviorsubject
。为此,您必须创建一项服务,然后在该服务中定义您的 Behaviorsubject
和所有常见的通信相关内容。您必须在组件和指令中注入这项服务。这样一来,不必 @Output()
多级,这样做的一个好处是您可以与组件的任何父级别进行通信,而不管事件链如何。
table.component.html
<table class="table table-dark table-striped table-hover">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Company Name</th>
<th scope="col">Brand</th>
<th scope="col">Engine Type</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of tableService.tableData; let i = index" appTableRow [rowData]="row">
<th scope="row">{{row.id}}</th>
<td>{{row.companyName}}</td>
<td>{{row.brand}}</td>
<td>{{row.engineType}}</td>
</tr>
</tbody>
</table>
<div *ngIf="selectedRow | async as rowSelect" class="alert alert-primary">
<pre>
{{rowSelect | json}}
</pre>
</div>
table.component.ts
export class TableComponent implements OnInit {
selectedRow;
constructor(private tableService: TableDataService) { }
ngOnInit() {
this.selectedRow = this.tableService.getTableRow();
}
}
directive.ts
@Directive({
selector: '[appTableRow]'
})
export class TableRowDirective {
@Input() rowData;
constructor(private tableRowSerivce: TableDataService) { }
@HostListener('click', ['$event'])
onClickHandler(event: MouseEvent) {
this.tableRowSerivce.tableRowData.next(this.rowData);
}
}
table-service.ts
@Injectable()
export class TableDataService {
tableData = [{
id: 1,
companyName: 'Audi',
brand: 'A4',
engineType: 'Diesel'
},{
id: 2,
companyName: 'Rolls-Roys',
brand: 'R4',
engineType: 'Diesel'
},{
id: 3,
companyName: 'Hyundai',
brand: 'Kona',
engineType: 'Electric'
},{
id: 4,
companyName: 'Ford',
brand: 'Endevour',
engineType: 'Diesel'
}];
tableRowData : BehaviorSubject<any> = new BehaviorSubject<any>({});
tableRowData$ = this.tableRowData.asObservable();
constructor() { }
getTableRow(): Observable<any>{
return this.tableRowData$;
}
}