我试图过滤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
答案 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?,它应该使您理解该主题。