javascript - 角度聆听变化
问题描述
检测到更改时 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;
},
});
}
解决方案
this.table
每当您第一次调用时getData
,TableMultiSortComponent
您都会发射ngOnInit
。
这个事件发射(我想)触发了父组件中的实际数据获取,在tableActions
方法中(在你包含的代码中看不到这个方法的实现)。此方法可能正在执行类似于_pageEventMyList
从 API 获取数据并在( )上设置dealsListData
属性的操作。TableMultiSortComponent
[tableData]="dealsListData"
反过来,这会触发您的onChange
inTableMultiSortComponent
并通过您的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
改用(或类似的东西,它将为您比较数组内容)。
推荐阅读
- swift - 为什么我不能在我的 Swift 项目中访问我的视图控制器的委托属性?
- webforms - 网络表单中的 reCaptcha3 集成
- reactjs - threejs + vanilla js 和 react-three-fiber + create-react-app 的颜色差异
- python - 解析混合数据类型矩阵日志文件
- python - Python 脚本从 Json 中提取信息但需要修改键和值
- java - 播放框架:2.8.0 - 尝试运行 SBT 命令时出现 Class Not Found Exception
- java - 有没有办法让这个switch语句更小更专业?
- reactjs - 使用 React 和 Spring 分页获取数据
- javascript - Apollo GraphQL 将重复项附加到组件状态
- twitter-bootstrap - 通过引导日期选择器检测自动填充以输入文本类型