Angular如何将数据从指令传递到父组件

Posted

技术标签:

【中文标题】Angular如何将数据从指令传递到父组件【英文标题】:Angular How to pass data from Directive to Parent component 【发布时间】:2020-07-21 20:22:19 【问题描述】:

我的要求是,我有一张可重复使用的桌子,我想在多个地方使用它。无论我在哪里使用此表,我都在传递表数据和列。

现在在一些父组件中,当用户单击表格行或双击表格行时,我想获取数据。

为此,我使用@HostListener 创建了一个指令。

当我在表格行上使用指令时,我可以在指令中获得点击的行数据,但是当我发出这些数据时,我没有在父级中获得它。

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() 方法中获取数据。

现在有些情况下我只想使用表格来显示数据,在这种情况下我不想应用指令。

提前致谢

【问题讨论】:

【参考方案1】:

您还需要在表格组件上创建一个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);
  

这样您就不必将点击处理程序添加到&lt;tr&gt; 元素

【讨论】:

如果我无法访问表怎么办,假设它是第三方组件。但是,我仍然想通过指令为其行添加一些功能?【参考方案2】:

工作演示在这个StackBlitz Link

这里,您可以使用servicerxjs - 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$;
  

【讨论】:

以上是关于Angular如何将数据从指令传递到父组件的主要内容,如果未能解决你的问题,请参考以下文章

Angular 10/Ionic 5 - 将输入数据从模态传递到父组件

如何在Angular中将表单数据从子组件传输到父组件

将数据从子组件传递到父组件Angular4

如何在Angular 4中将值从子组件传递到父组件

在 Angular 中有没有办法将包含组件指令的 html 注入到父组件的模板中?

如何使用 Angular 中的依赖注入将属性指令实例传递给嵌套组件