Skip to content

Commit 6b8f7fe

Browse files
hpyourdkmaster
authored andcommitted
修复:#528 Clear timeout on destroy in every component (#530)
* fixes #528 * add callLater() for all view objects, for safety handling callback when object is destroyed * fixes build error * table add timeout protect * combo add timeout protect * range time add timeout protect * combo add timeout protect * box add timeout protect * time add timeout protect * tab add timeout protect * button add timeout protect * group common add timeout protect * input add timeout protect * combo add timeout protect * pagination add timeout protect * dialog add timeout protect * scrollbar add timeout protect * fishbone add timeout protect * remove useless destroy * table inner component add timeout protect * table inner component add timeout protect * movable direction add timeout protect * optimize code * rename to _timerCache * simplify code
1 parent 2dcb094 commit 6b8f7fe

File tree

18 files changed

+156
-111
lines changed

18 files changed

+156
-111
lines changed

src/jigsaw/component/box/box.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,7 @@ export class JigsawBox extends JigsawResizableBoxBase implements AfterContentIni
108108
// resize line 视图渲染完成
109109
if (!this._resizeLine) return;
110110

111-
setTimeout(() => {
112-
// 等待box视图渲染
113-
this._computeResizeLineWidth();
114-
});
111+
this.callLater(this._computeResizeLineWidth, this);
115112

116113
this._removeAllListener();
117114

@@ -129,7 +126,7 @@ export class JigsawBox extends JigsawResizableBoxBase implements AfterContentIni
129126
if (this._isCurrentResizingBox) {
130127
this.renderer.setStyle(this._resizeLineParent.nativeElement, 'display', 'none');
131128
}
132-
setTimeout(() => {
129+
this.callLater(() => {
133130
this.renderer.setStyle(this._resizeLineParent.nativeElement, 'display', 'block');
134131
});
135132
});

src/jigsaw/component/box/common-box.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,17 +140,13 @@ export class JigsawBoxBase extends AbstractJigsawComponent implements OnDestroy
140140

141141
private _checkFlexByOwnProperty(property: string) {
142142
if (property && this.type != 'flex') {
143-
setTimeout(() => {
144-
this.type = 'flex';
145-
})
143+
this.callLater(() => this.type = 'flex');
146144
}
147145
}
148146

149147
protected checkFlexByChildren() {
150148
if (this.childrenBox.length > 0 && this.type != 'flex') {
151-
setTimeout(() => {
152-
this.type = 'flex';
153-
})
149+
this.callLater(() => this.type = 'flex');
154150
}
155151
}
156152

@@ -167,6 +163,7 @@ export class JigsawBoxBase extends AbstractJigsawComponent implements OnDestroy
167163
}
168164

169165
ngOnDestroy() {
166+
super.ngOnDestroy();
170167
if (this.removeBoxChangeListener) {
171168
this.removeBoxChangeListener.unsubscribe();
172169
}

src/jigsaw/component/box/editable-box.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ export class JigsawEditableBox extends JigsawResizableBoxBase implements AfterVi
141141
this._updateDirection();
142142
// 目前先平分创建两个node
143143
this.data.nodes = [new LayoutData, new LayoutData];
144-
setTimeout(() => {
144+
this.callLater(() => {
145145
// 等待子box渲染,填充搬家组件
146146
const firstChildBox = this.childrenBox.toArray()[0];
147147
firstChildBox._viewInit.subscribe(() => {
@@ -186,7 +186,7 @@ export class JigsawEditableBox extends JigsawResizableBoxBase implements AfterVi
186186
//this.data.nodes = node.nodes;
187187
//this.direction = node.direction ? node.direction : 'horizontal'; // 默认是'horizontal'
188188
} else {
189-
setTimeout(() => {
189+
this.callLater(() => {
190190
// 等待删除操作后,box渲染完成
191191
const firstChildBox = this.childrenBox.toArray()[0];
192192
this._moveComponents(firstChildBox, this);
@@ -197,10 +197,8 @@ export class JigsawEditableBox extends JigsawResizableBoxBase implements AfterVi
197197
this.data.components = node.components;
198198
this.data.nodes = [];
199199
this.direction = null;
200-
setTimeout(() => {
201-
// 等待 option bar & block 渲染
202-
this._bindScrollEvent();
203-
});
200+
// 等待 option bar & block 渲染
201+
this.callLater(this._bindScrollEvent, this);
204202
})
205203
}
206204
this._updateDirection();
@@ -378,10 +376,8 @@ export class JigsawEditableBox extends JigsawResizableBoxBase implements AfterVi
378376
this.checkFlex();
379377
// 等待 option bar & block 渲染
380378
this._bindScrollEvent();
381-
setTimeout(() => {
382-
// 异步发送事件,让父box能监听到
383-
this._viewInit.emit();
384-
});
379+
// 异步发送事件,让父box能监听到
380+
this.callLater(this._viewInit.emit, this._viewInit);
385381
}
386382

387383
ngOnDestroy() {

src/jigsaw/component/button/button.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,13 @@ export class JigsawButton extends AbstractJigsawComponent {
4343
private _onClick(): void {
4444
if (!this.disabled && !this._clicked) {
4545
this._clicked = true;
46-
setTimeout(() => this._clicked = false, 360);
46+
this.callLater(() => this._clicked = false, 360);
4747
}
4848
}
4949

5050
private _calcLineHeight(): string {
5151
return parseInt(this.height) - 4 + 'px';
5252
}
53-
5453
}
5554

5655
@NgModule({

src/jigsaw/component/combo-select/combo-select.ts

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,8 @@ export class JigsawComboSelect extends AbstractJigsawComponent implements Contro
151151
// 控件disabled,并且想打开下拉
152152
return;
153153
}
154-
setTimeout(() => {
155-
// toggle open 外部控制时,用setTimeout变更检查
154+
this.callLater(() => {
155+
// toggle open 外部控制时,用异步触发变更检查
156156
// 初始化open,等待组件初始化后执行
157157
if (value) {
158158
this._openDropDown();
@@ -163,7 +163,7 @@ export class JigsawComboSelect extends AbstractJigsawComponent implements Contro
163163
}
164164
this._$opened = value;
165165
this.openChange.emit(value);
166-
}, 0);
166+
});
167167
}
168168

169169
@Output()
@@ -231,9 +231,9 @@ export class JigsawComboSelect extends AbstractJigsawComponent implements Contro
231231
if (!this.autoWidth || !this._popupElement) {
232232
return;
233233
}
234-
setTimeout(() => {
234+
this.callLater(() => {
235235
this._renderer.setStyle(this._popupElement, 'width', this._elementRef.nativeElement.offsetWidth + 'px');
236-
}, 0);
236+
});
237237
}
238238

239239
private _autoEditorWidth() {
@@ -315,9 +315,7 @@ export class JigsawComboSelect extends AbstractJigsawComponent implements Contro
315315
}
316316
if (this._closeTrigger === DropDownTrigger.mouseleave && this._popupElement) {
317317
this._removeMouseOutHandler = this._renderer.listen(this._popupElement, 'mouseleave', () => {
318-
this._rollOutDenouncesTimer = setTimeout(() => {
319-
this.open = false;
320-
}, 200);
318+
this._rollOutDenouncesTimer = this.callLater(() => this.open = false, 200);
321319
});
322320
}
323321

@@ -403,10 +401,9 @@ export class JigsawComboSelect extends AbstractJigsawComponent implements Contro
403401
if (this.closeTrigger !== DropDownTrigger.mouseleave) return;
404402
event.preventDefault();
405403
event.stopPropagation();
404+
406405
clearTimeout(this._rollInDenouncesTimer);
407-
this._rollOutDenouncesTimer = setTimeout(() => {
408-
this.open = false;
409-
}, 200)
406+
this._rollOutDenouncesTimer = this.callLater(() => this.open = false, 200);
410407
}
411408

412409
/**
@@ -430,8 +427,8 @@ export class JigsawComboSelect extends AbstractJigsawComponent implements Contro
430427
public ngAfterViewInit() {
431428
this._tags.changes.subscribe(() => {
432429
this._autoEditorWidth();
433-
setTimeout(() => {
434-
// 等待combo高度变化,调整下拉位置
430+
// 等待combo高度变化,调整下拉位置
431+
this.callLater(() => {
435432
if (this._popupElement) {
436433
this._popupService.setPosition(this._getPopupOption(), this._popupElement);
437434
}
@@ -440,6 +437,8 @@ export class JigsawComboSelect extends AbstractJigsawComponent implements Contro
440437
}
441438

442439
public ngOnDestroy() {
440+
super.ngOnDestroy();
441+
443442
this.open = false;
444443

445444
if (this._removeRefreshCallback) {
@@ -456,7 +455,7 @@ export class JigsawComboSelect extends AbstractJigsawComponent implements Contro
456455
}
457456

458457
this._value = value instanceof ArrayCollection ? value : new ArrayCollection(value);
459-
setTimeout(() => this.valueChange.emit(this._value));
458+
this.callLater(() => this.valueChange.emit(this._value));
460459
this._autoWidth();
461460

462461
if (this._removeRefreshCallback) {

src/jigsaw/component/common.ts

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import {Directive, OnInit, ViewContainerRef, Input, NgModule, AfterViewInit} from "@angular/core";
1+
import {Directive, OnInit, ViewContainerRef, Input, NgModule, OnDestroy} from "@angular/core";
22
import {CommonUtils} from "../core/utils/common-utils";
3-
import {PopupService} from "../service/popup.service";
43

54
/**
65
* @internal
@@ -31,8 +30,73 @@ export interface IJigsawComponent {
3130
maxHeight: string;
3231
}
3332

34-
export abstract class AbstractJigsawComponent implements IJigsawComponent, OnInit {
33+
export abstract class AbstractJigsawViewBase implements OnInit, OnDestroy {
34+
protected initialized: boolean = false;
35+
private _timerCache = [];
36+
37+
/**
38+
* 延迟一会在执行一些逻辑,常常用于解决时间差的问题
39+
*
40+
* @param {Function} handler 需要延迟执行的函数
41+
*/
42+
protected callLater(handler: Function);
43+
/**
44+
* 延迟一会在执行一些逻辑,常常用于解决时间差的问题
45+
*
46+
* @param {Function} handler 需要延迟执行的函数
47+
* @param {number} timeout 延迟的毫秒数
48+
*/
49+
protected callLater(handler: Function, timeout: number);
50+
/**
51+
* 延迟一会在执行一些逻辑,常常用于解决时间差的问题
52+
*
53+
* @param {Function} handler 需要延迟执行的函数
54+
* @param {any} context `handler`函数执行的上下文
55+
*/
56+
protected callLater(handler: Function, context: any);
57+
/**
58+
* 延迟一会在执行一些逻辑,常常用于解决时间差的问题
59+
*
60+
* @param {Function} handler 需要延迟执行的函数
61+
* @param context `handler`函数执行的上下文
62+
* @param {number} timeout 延迟的毫秒数
63+
*/
64+
protected callLater(handler: Function, context: any, timeout: number);
65+
/**
66+
* @internal
67+
*/
68+
protected callLater(handler: Function, contextOrTimeout: any | number = undefined, timeout: number = 0): any {
69+
if (typeof contextOrTimeout === 'number') {
70+
timeout = +contextOrTimeout;
71+
contextOrTimeout = null;
72+
}
73+
74+
const timer = setTimeout(() => {
75+
if (!this._timerCache) {
76+
// maybe this object has been destroyed!
77+
return;
78+
}
79+
const idx = this._timerCache.indexOf(timer);
80+
if (idx != -1) {
81+
this._timerCache.splice(idx, 1);
82+
}
83+
CommonUtils.safeInvokeCallback(contextOrTimeout, handler);
84+
}, timeout);
85+
this._timerCache.push(timer);
86+
return timer;
87+
}
88+
89+
ngOnInit() {
90+
this.initialized = true;
91+
}
3592

93+
ngOnDestroy() {
94+
this._timerCache.forEach(t => clearTimeout(t));
95+
this._timerCache = null;
96+
}
97+
}
98+
99+
export abstract class AbstractJigsawComponent extends AbstractJigsawViewBase implements IJigsawComponent {
36100
@Input()
37101
public basicClass: string;
38102

@@ -66,13 +130,6 @@ export abstract class AbstractJigsawComponent implements IJigsawComponent, OnIni
66130
public set maxHeight(value: string) {
67131
this._maxHeight = CommonUtils.getCssValue(value);
68132
}
69-
70-
//TODO 所有组件都使用这个属性判断是否初始化好
71-
protected initialized: boolean = false;
72-
73-
ngOnInit() {
74-
this.initialized = true;
75-
}
76133
}
77134

78135
export interface IJigsawFormControl {

src/jigsaw/component/dialog/dialog.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,17 +153,18 @@ export abstract class AbstractDialogComponentBase
153153
}
154154

155155
//设置弹出位置和尺寸
156-
setTimeout(() => {
156+
this.callLater(() => {
157157
if (this.top) {
158158
this.renderer.setStyle(this.popupElement, 'top', this.top);
159159
}
160160
if (this.popupElement.style.position != 'fixed' && this.popupElement.style.position != 'absolute') {
161161
this.renderer.setStyle(this.popupElement.querySelector('.jigsaw-dialog-base-head'), 'cursor', 'inherit');
162162
}
163-
}, 0);
163+
});
164164
}
165165

166166
ngOnDestroy() {
167+
super.ngOnDestroy();
167168
this.answer.unsubscribe();
168169
}
169170
}

src/jigsaw/component/fish-bone/fish-bone.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,12 @@ export class JigsawFishBone extends AbstractJigsawComponent implements AfterView
180180

181181
ngAfterViewInit() {
182182
this._cacheFishBoneItems(this._firstLevelBones);
183-
setTimeout(() => {
183+
this.callLater(() => {
184184
this._setAllBoneAttribute();
185185
this._setFirstLevelBoneOffset(this._firstLevelBones);
186186
this._setRangeHeight();
187187
this._setRangeWidth();
188-
}, 0);
188+
});
189189

190190
this._zone.runOutsideAngular(() => {
191191
this._removeWindowListener = this._renderer.listen('window',
@@ -194,6 +194,7 @@ export class JigsawFishBone extends AbstractJigsawComponent implements AfterView
194194
}
195195

196196
ngOnDestroy() {
197+
super.ngOnDestroy();
197198
if (this._removeWindowListener) {
198199
this._removeWindowListener();
199200
}
@@ -399,9 +400,7 @@ export class JigsawFishBoneItem extends AbstractJigsawComponent implements After
399400

400401
// 异步发送事件,为了最外面的父组件能够在ngAfterViewInit中订阅到子组件的事件
401402
// 如果立即发送事件,则父组件订阅不到事件
402-
setTimeout(() => {
403-
this.rectifyEvent.emit();
404-
}, 0);
403+
this.callLater(this.rectifyEvent.emit, this.rectifyEvent);
405404

406405
// 标识没有子节点的,没有子节点的节点文本放在上面
407406
if (!this.childBones.length) {

src/jigsaw/component/input/input.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {
2-
NgModule, Component, EventEmitter, Input, Output, Directive,
3-
ElementRef, ViewChild, AfterContentInit, Renderer2, AfterViewChecked, ChangeDetectorRef, forwardRef
2+
NgModule, Component, EventEmitter, Input, Output, ElementRef, ViewChild,
3+
AfterContentInit, Renderer2, AfterViewChecked, ChangeDetectorRef, forwardRef
44
} from "@angular/core";
55
import {CommonModule} from "@angular/common";
66
import {ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR} from "@angular/forms";
@@ -134,7 +134,7 @@ export class JigsawInput extends AbstractJigsawComponent
134134
if (this.blurOnClear) {
135135
this._blurEmitter.emit(event);
136136
} else {
137-
setTimeout(() => {
137+
this.callLater(() => {
138138
if (!this._focused) {
139139
this._blurEmitter.emit(event);
140140
}
@@ -180,15 +180,14 @@ export class JigsawInput extends AbstractJigsawComponent
180180
}
181181

182182
ngAfterContentInit() {
183-
setTimeout(() => {
183+
this.callLater(() => {
184184
this._render2.setStyle(this._elementRef.nativeElement, 'opacity', 1);
185-
}, 0);
185+
});
186186
}
187187

188188
ngAfterViewChecked() {
189189
this._setInputPaddingStyle();
190190
}
191-
192191
}
193192

194193
@NgModule({

0 commit comments

Comments
 (0)