首页 > 解决方案 > 组件调用方法两次

问题描述

我正在尝试过滤 Worklog 类型的现有对象,并在我的 PeriodViewTable 组件中总结每个对象所花费的时间。我遇到了重复方法调用的问题。

我试图通过重置 ngOnInit() 中的值来解决问题,但它只是部分解决了问题 - 用户看到了正确的值,但它在幕后仍然是错误的,并且控制台输出“ExpressionChangedAfterItHasBeenCheckedError”。更重要的是,每次我单击 Form 组件中的 DatePicker 元素时都会调用这些方法 - 这是一个不同的方法。

这是我的 PeriodViewTable 组件:

<div *ngIf="datesBetween.length > 0 && (filterType == 6 || filterType == 5)">
  <div class="container container-fluid">
    <mat-checkbox #hide [checked]="true">
      <label>Show overview</label>
    </mat-checkbox>
  </div>
  <table *ngIf="hide.checked" class="table table-bordered table-striped">
    <thead>
    <tr>
      <th>Issue</th>
      <th *ngFor="let date of datesBetween">{{date}}</th>
    </tr>
    </thead>
    <tbody>
    <tr *ngFor="let wrk of worklogs">
      <td>{{wrk.issueKey}}</td>
      <td *ngFor="let date of datesBetween">{{getWorklogTimeForDay(wrk, datesBetween.indexOf(date))}}</td>
    </tr>
    <tr>
      <th>Total</th>
      <th *ngFor="let date of datesBetween">{{getTotalTimeForDay(datesBetween.indexOf(date))}}</th>
    </tr>
    <tr>
      <th>Total for period</th>
      <th>{{periodTotal}}</th>
    </tr>
    </tbody>
  </table>
</div>

因此,例如,如果我在worklogs数组中有 5 个条目并选择了 2 天,则该getWorklogTimeForDay方法应该被调用 10 次 - 对于数组中的每个条目 * 天数。此外,该getTotalTimeForDay方法应该每天调用一次,所以这里总共调用了 2 次。由于某种原因,该过程进行了两次。正如我之前所说,它也会在我单击不同组件(表单组件)中的任何 DatePicker HTML 元素后触发。这一刻是我完全迷失的地方。这是它的 HTML 代码:

<div class="container container-fluid">
  <div class="form">
    <div class="row">
      <div class="col-md-4">
        <div class="input-group">
          <input #tfDomain type="text" class="form-control" placeholder="Domain address" value="jira-test">
          <div class="input-group-append">
            <span class="input-group-text">.atlassian.net</span>
          </div>
        </div>
      </div>
      <div class="col-md-4">
        <input #tfProject type="text" class="form-control" placeholder="Type project name here" value="TEST">
      </div>
    </div>
    <div class="row">
      <div class="col-md-4">
        <div class="input-group">
          <input #tfUser type="text" class="form-control" placeholder="Email address" value="user.name">
          <div class="input-group-append">
            <span class="input-group-text">@testmail.com</span>
          </div>
        </div>
      </div>
      <div class="col-md-4">
        <input #pfPassword type="password" class="form-control" placeholder="Password" value="passwrd123">
      </div>
    </div>
    <h4>Filters</h4>
    <div class="row">
      <div class="col-md-4">
        <div class="input-group">
          <input #userFilter type="text" class="form-control" placeholder="Email address" value="user.filter"
                 [disabled]="startDate.value.length>0 && chbRefresh.checked">
          <div class="input-group-append">
            <span class="input-group-text">@testmail.com</span>
          </div>
        </div>
      </div>
      <div class="col-md-6">
        <mat-form-field>
          <input #startDate matInput [matDatepicker]="picker" placeholder="Start date"
                 [disabled]="userFilter.value.length>0 && chbRefresh.checked">
          <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
          <mat-datepicker #picker></mat-datepicker>
        </mat-form-field>
        <mat-form-field>
          <input #endDate matInput [matDatepicker]="picker2" placeholder="End date"
                 [disabled]="userFilter.value.length>0 && chbRefresh.checked">
          <mat-datepicker-toggle matSuffix [for]="picker2"></mat-datepicker-toggle>
          <mat-datepicker #picker2></mat-datepicker>
        </mat-form-field>
      </div>
    </div>
    <div class="row">
      <div class="col-xs-6">
        <button class="btn btn-primary" id="searchButton"
                (click)="submitAll(tfDomain.value, tfProject.value, tfUser.value, pfPassword.value, chbRefresh.checked, userFilter.value, startDate.value, endDate.value)">
          Search
        </button>
      </div>
      <div class="col-xs-6">
        <mat-checkbox #chbRefresh [disabled]="userFilter.value.length > 0 && startDate.value.length > 0"
                      [checked]="true">
          <label>Refresh search</label>
        </mat-checkbox>
      </div>
    </div>
  </div>
</div>

PeriodViewTable 打字稿文件:

export class PeriodViewTableComponent implements OnInit {

  datesBetween: string[];
  worklogs: Worklog[] = [];
  newSearch: boolean;
  filterType: number = 0;
  periodTotal: number = 0;
  someint: number = 0;
  someint2: number = 0;

  constructor(private formService: FormService) { }

  ngOnInit() {
    console.log('ngOnInit() - period view table');
    this.formService.newSearchChanged.subscribe(
      newSearch => {
        if(newSearch != this.newSearch) {
          this.worklogs = [];
          this.periodTotal = 0;
        }
      }
    );
    this.formService.filterChanged.subscribe(filterType => this.filterType = filterType);
    console.log('filterChanged');
    this.datesBetween = this.formService.datesBetweenChanged.subscribe( dates => this.datesBetween = dates);
    this.formService.worklogsChanged.subscribe(worklog => this.worklogs.push(worklog));
  }

  getWorklogTimeForDay(wrk: Worklog, index: number){
    console.log('getWorklogTimeForDay(' + wrk + ', ' + index + ')'+ '    ' + this.someint++);
    let currentDate = new Date(this.datesBetween[index]);
    if(moment(wrk.started).startOf('day').isSame(moment(currentDate).startOf('day'))) {
      return wrk.timeSpentSeconds/3600;
    }else return '-';
  }

  getTotalTimeForDay(index: number) {
    console.log('getTotalTimeForDay(' + index + ')' + '    ' + this.someint2++);
    let total: number = 0;
    for(let wrk of this.worklogs){
      if(moment(wrk.started).startOf('day').isSame(moment(this.datesBetween[index]).startOf('day'))){
        total += wrk.timeSpentSeconds/3600;
      }
    }
    this.periodTotal += total;
    return total;
  }

}

下面是我在控制台中看到的屏幕截图的链接。如您所见,该过程重复了两次: https ://i.imgur.com/oYOwfJH.jpg 它的样子,可视化: https ://i.imgur.com/RsC3mVO.jpg https://i .imgur.com/yYuGf1a.jpg

标签: htmlangulartypescript

解决方案


这两种方法的触发频率比预期的要高的原因{{someMethodCall()}}是每次渲染组件时都会触发。因此,在更改组件中的任何内容时 - 正在调用方法。

这是一个简单的例子

我建议您不要在字符串插值中使用方法。

关于如何解决此问题 - 请考虑为您需要的每种方法创建一个自定义管道。仅当数据更改时才会触发管道,从而为您提供预期的行为。

更新

您也可以使用 getter 代替管道,因为不鼓励使用某些管道– @trichetriche


推荐阅读