首页 > 解决方案 > 角度聆听变化

问题描述

检测到更改时 NgOnChanges 更新表会导致无限服务调用,我不知道为什么。也许有人可以帮忙。谢谢。

我有父组件 TableMultiSortComponent 并且我传递了作为表数据的输出 EventEmitter 。

EventEmitter 的事件或输出被传递给子组件,我将使用 pagesize 、 pageindex 等进行查询,正如您在子组件上的 dataServiceEvent 中看到的那样。

然后请求的输出是 dealListData ,我将其从子级传递回父级,作为要填充到表中的数据。

它应该在有变化时更新表,这就是为什么我把它放在 ngOnchanges 上,它是 initTableMultiSort 但现在它正在无限更新表,如果有变化,它应该只更新一次

在此处输入图像描述

#我从父组件传递给子组件的表数据,this.dataServiceEvent.emit(this.table); ,这是我发出的,因为我需要页面索引、大小等。

在此处输入图像描述

#HTML 代码 - 父组件

<mat-card *ngIf="table !== undefined">
  <mat-table mat-table [dataSource]="table.dataSource" matMultiSort (matSortChange)="table.onSortEvent()"> 
    <ng-container *ngFor="let column of table.columns" [matColumnDef]="column.id">
      <mat-header-cell class="table-multi-sort-header" *matHeaderCellDef [mat-multi-sort-header]="column.id"> 
        <div>{{column.name}}</div> 
        <div class="sub-text">{{getColumnSubtitle(column.id)}}</div>
      </mat-header-cell>
      <mat-cell *matCellDef="let row">
          <ng-container *ngIf="column.id !== 'action'; then col; else actionCol"></ng-container>
          <ng-template #col>
            <app-table-multi-sort-cell-default [cellData]="row" [id]="column.id" [subId]="getColumnSubId(column.id)"></app-table-multi-sort-cell-default>
          </ng-template>
          <ng-template #actionCol>
            <app-table-multi-sort-cell-action [rowData]="row" [actions]="getActions(column.id)" (actionClickEvent)="clickTableAction($event,row)"></app-table-multi-sort-cell-action>
          </ng-template>
      </mat-cell>
    </ng-container>
    <mat-header-row *matHeaderRowDef="table.displayedColumns; sticky:true"></mat-header-row>
    <mat-row *matRowDef="let item; columns: table.displayedColumns;"></mat-row>
  </mat-table> 
  <mat-progress-bar *ngIf="isLoading" mode="indeterminate"></mat-progress-bar>
</mat-card>

#ts 代码- 父组件

export class TableMultiSortComponent implements OnInit {
  @Input() tableOptions:any;
  @Input() tableData:any = [];
  @Input() isClientSide:boolean = false;
  @Input() isLoading: boolean = false;
  @Output() tableActionsEvent = new EventEmitter<any>();
  @Output() dataServiceEvent = new EventEmitter<any>() ;
  @ViewChild(MatMultiSort, { static: false }) sort: MatMultiSort;
  
  tableConfig: any = TABLE_MULTI_SORT_OPTIONS.DEFAULT;
  table:TableData<any>;
  displayedColumns: any;
  
  constructor() { }

  ngOnInit(): void {   
    this.initTableMultiSort();
  }

  initTableMultiSort(){
    this.tableConfig = {
      ...this.tableConfig,
      ...this.tableOptions
    }
    
    this.table = new TableData<any>(this.tableConfig.columns,this.tableConfig.sortParams);
    this.table.pageSize = this.tableConfig.pageSize;
    this.table.pageIndex = this.tableConfig.pageIndex;
    this.table.nextObservable.subscribe(() => { this.getData(); });
    this.table.sortObservable.subscribe(() => { this.getData(); });
    this.table.previousObservable.subscribe(() => { this.getData(); });
    this.table.sizeObservable.subscribe(() => { this.getData(); });

    setTimeout(()=>{
      this.table.dataSource = new MatMultiSortTableDataSource(this.sort, this.isClientSide);
      this.getData();
    },0);
    
  }

  ngOnChanges(changes: SimpleChanges) {  
    if (changes.tableData && changes.tableData.currentValue){  
      console.log("changes" , changes)
      this.initTableMultiSort()
    }
  }

  getData(){
    this.table.totalElements = 1;
    this.table.pageIndex = 0;
    this.table.pageSize = 10;
    this.table.data = this.tableData;
    if(this.dataServiceEvent) {
      this.dataServiceEvent.emit(this.table);
      
    }
  }

#child 组件 HTML 代码

  <app-table-multi-sort  (dataServiceEvent)="dataServiceEvent($event)" [tableOptions]="tableOptions" [tableData]="dealsListData" (tableActionsEvent)="tableActions($event)"></app-table-multi-sort>

#子组件ts代码

   export class DealsTransactionComponent implements OnInit {
  @Input() transactionId: any = 2;
  @Input() transactionName: string = '-';
  @ViewChild(TableMultiSortComponent, { static: true }) tableMultiSortComponent: TableMultiSortComponent;
  private currentTableConfig: TableData<any>;


  dealsListData: any;
  tableOptions: any;
  @Input() transaction: any;
  isLoading: boolean;
  private _destroyed$ = new Subject();
  totalDeals : number;
  accountId: any;
  searchInput: string;
  table: any;
  constructor(
    private dialog: MatDialog,
    private dealService: DealService,
    private notificationService: NotificationService,
    private route: Router,
    
  ) {}
  ngOnInit(): void {
    const currentAccountDetails = localStorage.getItem('currAcct') as any;
    if (currentAccountDetails) {
      this.accountId = JSON.parse(currentAccountDetails).accountId;
    }

    this.tableOptions = {
      columns:[
        {id:'name',name:'Deal Name',subId:'type', subtitle:'Deal Type'},
        {id:'annualRentProposed',name:'Annual Rent (Proposed)', subId: 'annualRentCurrent', subtitle:'Annual Rent (Proposed)'},
        {id:'firmTermRemain',name:'Firm Term Remaining', subId: 'firmTermAdded', subtitle:'(Current)'},
        {id:'maxTerm',name:'Max Available Term'},
        {id:'cash',name:'Cash Contribution'},
        {id:'action', name: 'Actions', actions:[
          {icon:'file_copy', name:'Copy', class:'primary-color'},
          {icon:'delete', name: 'Delete', class:'mat-error'},
          {icon:'forward', name: 'For Approval', class:'primary-color'}
        ]}
      ]
    }
  }

  dataServiceEvent(item) {
    this.table = item;
    if(this.table) {
      this._pageEventMyList();
    }
  }

  private _pageEventMyList() {
    if (!this.shouldLoadData(this.currentTableConfig, this.table)) {
      return;
    }
    this.currentTableConfig = this.table;
    this.searchInput = '';
    this.isLoading = true;
    this.dealService
      .getAllDeals(
        this.accountId,
        this.transaction.id,
        this.table.pageIndex + 1,
        this.table.pageSize,
        this.searchInput,
        this.table.sortParams,
        this.table.sortDirs
      )
      .pipe(finalize(() => (this.isLoading = false)))
      .subscribe({
        error: (err) => this.notificationService.showError(err),
        next: (res) => {
          this.dealsListData = res.totalItemCount;
          this.dealsListData = res.lastItemOnPage;
          this.totalDeals = res.items.length;
          this.dealsListData = res.items;
          console.log("this.dealsListData" , this.dealsListData)
        },
        complete: noop,
      });
  }
  private shouldLoadData(oldTableConfig: TableData<any>, tableConfig: TableData<any>): boolean {
    if (!oldTableConfig) {
      return true;
    }
    return oldTableConfig.pageIndex !== tableConfig.pageIndex
      || oldTableConfig.pageSize !== tableConfig.pageSize
      || oldTableConfig.sortParams !== tableConfig.sortParams
      || JSON.stringify(oldTableConfig.sortParams) !== JSON.stringify(tableConfig.sortParams)
      || JSON.stringify(oldTableConfig.sortDirs) !== JSON.stringify(tableConfig.sortDirs);
  }

  createDeal() {
    this.route.navigateByUrl(`deals/detail/${this.transactionId}~create`, {
      state: { data: this.transaction },
    });
    localStorage.removeItem('breadCrumbsPath');
    const breadCrumbsPath = [
      {
        text: 'My transactions',
        link: '/transactions',
      },
      {
        text: this.transactionName,
        link: `/transactions/overview/${this.transactionId}`,
      },
    ];
    localStorage.setItem('breadCrumbsPath', JSON.stringify(breadCrumbsPath));
  }

  copyDeal(id: number) {
    const data  = { id : id }
    this.duplicateDeal(data)
  }
  deleteDeal(id: number) {
    this.isLoading = true;
    this.dealService.delete(id)
      .pipe(finalize(() => { this.isLoading = false;}))
      .subscribe({
        next: (res) => {
          this.dealsListData = this.dealsListData.filter((x) => x.id !== id);
          this.notificationService.showSuccess('Deal been removed successfully.');
        },
        error: (err) => {
          this.notificationService.showError('Something went wrong, Try again later.');
          this.isLoading = false;
        },
        complete: () => {
          this.isLoading = false;
        },
      });
  }
  duplicateDeal(data: any) {
    this.isLoading = true;
    this.dealService.duplicate(data)
      .pipe( finalize(() => { this.isLoading = false;}))
      .subscribe({
        next: (res) => {
          let copyData = {
            ...this.dealsListData.filter((x) => x.id === data.id),
          };
          copyData.name = copyData.name + '(copy)';
          copyData.id = this.dealsListData[this.dealsListData.length - 1].id + 1;
          this.notificationService.showSuccess('Deal was successfully duplicated.' );
        },
        error: (err) => {
          this.notificationService.showError('Something went wrong, Try again later.');
          this.isLoading = false;
        },
        complete: () => {
          this.isLoading = false;
        },
      });
  }

  approveDeal(data: any) {
    this.isLoading = true;
    this.dealService
      .approve(data)
      .pipe(finalize(() => { this.isLoading = false; }))
      .subscribe({
        next: (res) => {
          this.notificationService.showSuccess('Deal was approved');
        },
        error: (err) => {
          this.notificationService.showError('Something went wrong, Try again later.');
          this.isLoading = false;
        },
        complete: () => {
          this.isLoading = false;
        },
      });
  }

标签: javascriptangulartypescript

解决方案


this.table每当您第一次调用时getDataTableMultiSortComponent您都会发射ngOnInit

这个事件发射(我想)触发了父组件中的实际数据获取,在tableActions方法中(在你包含的代码中看不到这个方法的实现)。此方法可能正在执行类似于_pageEventMyList从 API 获取数据并在( )上设置dealsListData属性的操作。TableMultiSortComponent[tableData]="dealsListData"

反过来,这会触发您的onChangeinTableMultiSortComponent并通过您的if检查(tableData实际上已更改)。从onChange,您调用initTableMultiSort它重新初始化this.table并使用 发出它this.dataServiceEvent.emit(this.table);。这使一切进入一个循环。

我建议实施一些额外的检查,以确保在表配置相同的情况下不会触发数据重新加载。

简而言之,看起来您使用该表为您提供有关 、 和 的数据pageIndex,因此pageSize我建议您在尝试再次加载数据时保留有关这些属性值的信息。比较它们,如果有变化,然后获取数据sortParamssortDirs

  private currentTableConfig: TableData<any>;

  private _pageEventMyList() {
    if (!shouldLoadData(currentTableConfig, this.table)) {
      return;
    }
    this.currentTableConfig = this.table;
    this.searchInput = '';
    this.isLoading = true;
    this.dealService
      .getAllDeals(
        this.accountId,
        this.transaction.id,
        this.table.pageIndex + 1,
        this.table.pageSize,
        this.searchInput,
        this.table.sortParams,
        this.table.sortDirs
      )
      .pipe(finalize(() => (this.isLoading = false)))
      .subscribe({
        error: (err) => this.notificationService.showError(err),
        next: (res) => {
          this.dealsListData = res.totalItemCount;
          this.dealsListData = res.lastItemOnPage;
          this.totalDeals = res.items.length;
          this.dealsListData = res.items;
        },
        complete: noop,
      });
  }

shouldLoadData方法执行检查:

private shouldLoadData(oldTableConfig: TableData<any>, tableConfig: TableData<any>): boolean {
  if (!oldTableConfig) {
    return true;
  }
  return oldTableConfig.pageIndex !== tableConfig.pageIndex
    || oldTableConfig.pageSize !== tableConfig.pageSize
    || JSON.stringify(oldTableConfig.sortParams) !== JSON.stringify(tableConfig.sortParams)
    || JSON.stringify(oldTableConfig.sortDirs) !== JSON.stringify(tableConfig.sortDirs);
}

使用免责声明JSON.stringify

这可行,但不是最佳的。如果您安装了另一个第三方库,例如lodash,您可以_.isEqual改用(或类似的东西,它将为您比较数组内容)。


推荐阅读