typescript 无法让Angular 2表组件接收对共享服务类中数组的更改

wn9m85ua  于 2023-06-24  发布在  TypeScript
关注(0)|答案(1)|浏览(110)

我希望在服务被其他地方的按钮触发后,在表组件中显示服务获取的数据数组。我已经尝试使用ngOnChanges()来完成这一操作,但是在init之后似乎没有注意到服务类中数组的任何变化。我希望流程是这样的:

PixSearchComponent按钮(代码未显示)--> PixSearchService数据提取--> PixTableComponent中显示的数据

我做了一些日志记录/调试,服务方法肯定被调用了。我知道表的字段绑定没有问题,因为我已经测试过了。有谁能告诉我我做错了什么吗?谢谢

*pix-search.service.ts

import {
  HttpClient,
  HttpErrorResponse,
  HttpHeaders,
} from '@angular/common/http';
import { EventEmitter, Inject, Injectable, Optional } from '@angular/core';
import { catchError, map, tap, throwError } from 'rxjs';
import { IPix } from './model/IPix';

@Injectable({
  providedIn: 'root',
})
export class PixSearchService {

  constructor(private http: HttpClient) {}

  pixUpdated: EventEmitter<IPix[]> = new EventEmitter();

  setPixData(pixData: IPix[]) {
    this.pixData = pixData;
    return this.pixUpdated.emit(this.pixData);
  }

  getPixData()  {
    return this.pixData;
  }

  pixData!: IPix[];

  pixUrl: string = 'https://example.ckp-dev.example.com/example';

  retrievePixData(): void {
    const headers = new HttpHeaders({
      'x-api-key':
        'ewogICAgImFwaUtleSIgOiAiMTIzIiwKICAgICJ1c2VySWQiID3649807253098ESSBEZXZlbG9wZXIiCn0=',
    });

    this.setPixData(this.http
      .get<any>(this.pixUrl, {
        headers
      })
      .pipe(
        tap((data) => console.log('All:', JSON.stringify(data))),
        map((data: any) => data.results),
        catchError(this.handleError)
      ) as unknown as IPix[]);
  }

  handleError(err: HttpErrorResponse) {
    let errorMessage = '';
    if (err.error instanceof ErrorEvent) {
      errorMessage = `An error occurred: ${err.error.message}`;
    } else {
      errorMessage = `Server returned code:: ${err.status}, error message is: ${err.message}`;
    }
    console.error(errorMessage);
    return throwError(() => errorMessage);
  }
}

像素表.组件.ts

import {
  Component,
  Inject,
  Input,
  OnChanges,
  OnDestroy,
  OnInit,
  Optional,
} from '@angular/core';
import type { TableSize } from '@dauntless/ui-kds-angular/table';
import type { TableStickyType } from '@dauntless/ui-kds-angular/table';
import type { TableScrollType } from '@dauntless/ui-kds-angular/table';
import { CardElevation } from '@dauntless/ui-kds-angular/types';
import { PixSearchService } from '../pix-search.service';
import { Observable, Subscription } from 'rxjs';
import { IPix } from '../model/IPix';
import { IContract } from '../model/IContract';
import { IAudit } from '../model/IAudit';
import { ICapitation } from '../model/ICapitation';
import { IChangeRequest } from '../model/IChangeRequest';
import { IHnetAudit } from '../model/IHnetAudit';
import { IProduct } from '../model/IProduct';
import { IProvider } from '../model/IProvider';

@Component({
  selector: 'pix-table-component',
  templateUrl: 'pix-table.component.html',
  styleUrls: ['pix-table.component.css'],
  providers: [PixSearchService]
})
export class PixTableComponent implements IPix {
  constructor(private pixSearchService: PixSearchService) {
    this.pixSearchService.pixUpdated.subscribe((pix) => {
      this.pixRecords = this.pixSearchService.getPixData() as unknown as IPix[];
    });
  }

  columns = [
    'ID',
    'Network',
    'LOB',
    'HP Code',
    'Atypical',
    'TIN',
    'GNPI',
    'Org',
    'Business Unit Code',
    'National Contract',
    'National ContractType',
    'Contract Type',
    'Super Group',
    'Contract ID',
    'Amendment ID',
    'Contract Effective Date',
    'Contract Termination Date',
  ];

  rows: any;
  tableSize: TableSize = 'small';
  showHover = true;
  sticky: TableStickyType = 'horizontal';
  scrollType: TableScrollType = 'both';
  label = 'Payment Index Management';
  disabled = 'disabled';
  error = 'error';
  maxlength = 'maxlength';
  showCounter = false;
  elevation: CardElevation = 'medium';

  legacyConfigTrackerId!: number;
  contract!: IContract;
  audit!: IAudit;
  capitation!: ICapitation;
  changeRequest!: IChangeRequest;
  claimType!: string;
  deleted!: string;
  hnetAudit!: IHnetAudit;
  id!: string;
  noPayClassReason!: string;
  payClass!: string;
  product!: IProduct;
  provider!: IProvider;
  rateEscalator!: string;
  status!: string;
  selected: boolean = false;

  pixRecords: IPix[] = [];
  errorMessage: string = '';
}
2exbekwf

2exbekwf1#

EventEmitter通常用于组件之间的通信,即父组件的子组件。
当涉及到服务时,主题是您的最佳选择(SubjectBehaviorSubjectReplaySubjectAsyncSubject)。
对于您的特定情况,使用BehaviorSubject可能就足够了,您可以实现以下内容:

服务项目

@Injectable({
  providedIn: 'root',
})
export class PixSearchService {
  private pixUpdated = new BehaviorSubject<IPix[]>([]);

  constructor(private http: HttpClient) {}

  fetchData(): void {
    this.http.get(...).pipe(take(1))
    .subscribe(response => this.pixUpdated.next(response))
  }

  getData(): Observable<IPix[]> {
    return this.pixUpdated.asObservable();
  }

组件

@Componet({...})
export class PixTableComponent implements OnInit, IPix {
  dataSource$!: Observable<IPix[]>; 
 
  constructor(private pixService: PixSearchService) {}

  ngOnInit(): void {
    this.fetch();
    this.load(); // you can call this function whenever you need from another function
                 // without fetching again data from the backend
  }

  private fetch(): void {
    this.pixService.fetchData();  
  }

  private load(): void {
    this.dataSource$ = this.pixService.getData();
  }
}

我不确定你是在使用Angular Material's table还是仅仅是一个标准的table,所以这里有两种可能的方法来处理表的数据:

HTML

<!-- Angular Material Table -->
<table mat-table [dataSource]="dataSource$ | async">
 ....
</table>

<!-- standard table -->
<table>
 <thead>
   <tr>...</tr>
 </thead>
 <tbody>
  <tr *ngFor="let item of (dataSource$ | async)">
    ....
  </tr>
 <tbody>
</table>

通过使用async管道,您无需处理subscription,就可以React式地执行subscribe
关于主题here的更多信息
更多关于Angular组件通信here的信息

相关问题