【问题标题】:angular listen for changes角度聆听变化
【发布时间】:2021-08-30 09:09:34
【问题描述】:

检测到更改时 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;
        },
      });
  }

【问题讨论】:

    标签: javascript angular typescript


    【解决方案1】:

    每当您在TableMultiSortComponent 中调用getData 时,您都会发出this.table,这是第一次在ngOnInit 中发生。

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

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

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

    简而言之,您似乎使用该表为您提供了有关 pageIndex、pageSize、sortParams 和 sortDirs 的数据,因此我建议您在尝试时保留有关这些属性值的信息再次加载数据。比较一下,如果有变化,就去获取数据

      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(或类似的东西,它将为您比较数组内容)。

    【讨论】:

    • 您好,先生,您是否有一些基于我的代码和您的建议的示例修复或实现,我可以尝试。谢谢。
    • 您所说的一些额外检查是什么意思,先生,这已经困扰我好几天了?
    • 您能否在表配置相同或不同时添加更多详细信息。谢谢
    • @JellynAng 查看我的更新答案以获取潜在解决方案。
    • 您好,先生,所以我不会更改我的 TableMultiSortComponent 的任何内容?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-21
    • 2020-10-12
    • 1970-01-01
    • 1970-01-01
    • 2020-05-27
    相关资源
    最近更新 更多