两次调用组件的方法

时间:2019-02-12 10:15:40

标签: html angular typescript

我试图过滤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次。由于某种原因,该过程执行了两次。如前所述,单击其他组件-Form组件中的任何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

2 个答案:

答案 0 :(得分:1)

两种方法均比其应有的触发次数更多的原因是,{{someMethodCall()}}在每次呈现组件时都会被触发。因此,在更改组件中的任何内容时,方法都会被调用。

这里是一个快速的example

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

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

更新

  

您还可以使用吸气剂代替管道,因为some pipes are discouraged – @trichetriche

答案 1 :(得分:0)

Angular提供了组件和变更检测机制的整个生命周期。如果您的模板上有任何getter / setter或函数,它将在每个更改检测周期被调用。 Angular运行了很多这样的周期。这就是为什么模板上永远不应该有任何getter,setter或函数的原因-请记住这一点。您只能绑定属性(不是函数的变量)。如果该数据在组件使用期内没有变化,请在 $bookingId = DB::table('booking_header') ->select('id') ->where('parent_booking_id', 1151); $rateArray = DB::table('user_transaction') ->joinSub($bookingId, 'booking_id', function ($join) { $join->on('user_transaction.booking_header_id', '=', 'booking_id.id'); })->get(); 内进行计算。

对我来说,您绝对应该计算甚至在调用ngOnInit()之前所需的所有内容(例如,从可观察到的数据接收之后)。

推荐How does Angular2 change detection really works?,它应该使您理解该主题。