Skip to content

Commit 3bb0e72

Browse files
Fix removing nested components (#1028)
* Fix removing nested components * Add tests * Fix lint * Refactoring * Test refactoring * Small refactoring
1 parent 80d3600 commit 3bb0e72

File tree

5 files changed

+92
-7
lines changed

5 files changed

+92
-7
lines changed

src/core/component.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export abstract class DxComponent implements OnChanges, OnInit, DoCheck, AfterCo
5151
instance: any;
5252
isLinked = true;
5353
changedOptions = {};
54+
removedOptions = [];
5455
widgetUpdateLocked = false;
5556

5657
private _initTemplates() {
@@ -170,6 +171,7 @@ export abstract class DxComponent implements OnChanges, OnInit, DoCheck, AfterCo
170171
}
171172

172173
protected _destroyWidget() {
174+
this.removedOptions = [];
173175
if (this.instance) {
174176
let element = this.instance.element();
175177
events.triggerHandler(element, 'dxremove', { _angularIntegration: true });
@@ -209,6 +211,7 @@ export abstract class DxComponent implements OnChanges, OnInit, DoCheck, AfterCo
209211

210212
ngAfterContentChecked() {
211213
this.applyOptions();
214+
this.resetOptions();
212215
this.unlockWidgetUpdate();
213216
}
214217

@@ -226,6 +229,15 @@ export abstract class DxComponent implements OnChanges, OnInit, DoCheck, AfterCo
226229
}
227230
}
228231

232+
resetOptions() {
233+
if (this.instance) {
234+
this.removedOptions.forEach(option => {
235+
this.instance.resetOption(option);
236+
});
237+
this.removedOptions = [];
238+
}
239+
}
240+
229241
setTemplate(template: DxTemplateDirective) {
230242
this.templates.push(template);
231243
}

src/core/nested-option.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const VISIBILITY_CHANGE_SELECTOR = 'dx-visibility-change-handler';
1111
export interface INestedOptionContainer {
1212
instance: any;
1313
isLinked: boolean;
14+
removedOptions: string[];
1415
optionChangedHandlers: EventEmitter<any>;
1516
}
1617

@@ -64,8 +65,10 @@ export abstract class BaseNestedOption implements INestedOptionContainer, IColle
6465
}
6566
}
6667

67-
protected _resetOption(name: string) {
68-
this.instance.resetOption(name);
68+
protected _addRemovedOption(name: string) {
69+
if (this.instance && this.removedOptions) {
70+
this.removedOptions.push(name);
71+
}
6972
}
7073

7174
setHost(host: INestedOptionContainer, optionPath: IOptionPathGetter) {
@@ -86,6 +89,10 @@ export abstract class BaseNestedOption implements INestedOptionContainer, IColle
8689
return this._host && this._host.instance;
8790
}
8891

92+
get removedOptions() {
93+
return this._host && this._host.removedOptions;
94+
}
95+
8996
get isLinked() {
9097
return !!this.instance && this._host.isLinked;
9198
}

templates/nested-component.tst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export class <#= it.className #>Component extends <#= it.baseClass #><#? it.hasT
107107
<#?#>
108108
<#? !it.isCollection #>
109109
ngOnDestroy() {
110-
this._resetOption(this._optionPath);
110+
this._addRemovedOption(this._fullOptionPath().slice(0, -1));
111111
}
112112
<#?#>
113113
}

tests/src/ui/chart.spec.ts

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
} from '@angular/core/testing';
1111

1212
import {
13-
DxChartModule, DxChartComponent
13+
DxChartModule, DxChartComponent, DxScrollViewModule
1414
} from 'devextreme-angular';
1515

1616
@Component({
@@ -23,13 +23,18 @@ class TestContainerComponent {
2323
}];
2424
@ViewChild(DxChartComponent) chart: DxChartComponent;
2525
dataSource = [];
26+
disposed = false;
2627
commonSeriesSettings = {
2728
argumentField: undefined
2829
};
2930
seriesAsArray = [];
3031
seriesAsObject = {
3132
valueField: undefined
3233
};
34+
35+
onDisposing() {
36+
this.disposed = true;
37+
}
3338
}
3439

3540
describe('DxChart', () => {
@@ -38,7 +43,7 @@ describe('DxChart', () => {
3843
TestBed.configureTestingModule(
3944
{
4045
declarations: [TestContainerComponent],
41-
imports: [DxChartModule]
46+
imports: [DxChartModule, DxScrollViewModule]
4247
});
4348
});
4449

@@ -62,7 +67,7 @@ describe('DxChart', () => {
6267
it('should not repainting twice in change detection cycle after applying options directly', () => {
6368
TestBed.overrideComponent(TestContainerComponent, {
6469
set: {
65-
template: `<dx-chart
70+
template: `<dx-chart
6671
[dataSource]="dataSource"
6772
[series]="seriesAsArray"
6873
[commonSeriesSettings]="{ argumentField: 'name' }"
@@ -92,7 +97,7 @@ describe('DxChart', () => {
9297
it('should not repainting twice in change detection cycle after detect changes in arrays', () => {
9398
TestBed.overrideComponent(TestContainerComponent, {
9499
set: {
95-
template: `<dx-chart
100+
template: `<dx-chart
96101
[dataSource]="dataSource"
97102
[series]="seriesAsArray"
98103
[commonSeriesSettings]="{ argumentField: 'name' }"
@@ -145,4 +150,31 @@ describe('DxChart', () => {
145150

146151
expect(instance.option('argumentAxis.strips[0].label.text')).toBe('label2');
147152
});
153+
154+
it('should remove component by dispose method', () => {
155+
TestBed.overrideComponent(TestContainerComponent, {
156+
set: {
157+
template: `
158+
<dx-chart [dataSource]="[{ ID: 1, Text: 'A', Value: '2' }]" (onDisposing)='onDisposing()'>
159+
<dxi-series argumentField='Text' valueField='Value' type='bar' color='#f9ce2d'>
160+
<dxo-label [visible]='false'> </dxo-label>
161+
</dxi-series>
162+
</dx-chart>`
163+
}
164+
});
165+
166+
jasmine.clock().uninstall();
167+
jasmine.clock().install();
168+
let fixture = TestBed.createComponent(TestContainerComponent);
169+
fixture.detectChanges();
170+
171+
const testComponent = fixture.componentInstance;
172+
const instance = testComponent.chart.instance;
173+
instance.dispose();
174+
fixture.detectChanges();
175+
fixture.destroy();
176+
expect(testComponent.disposed);
177+
jasmine.clock().uninstall();
178+
179+
});
148180
});

tests/src/ui/data-grid.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import DxDataGrid from 'devextreme/ui/data_grid';
2323
})
2424
class TestContainerComponent {
2525
showComponent = true;
26+
selectionCount = 0;
2627
dataSource = [{
2728
id: 1,
2829
string: 'String',
@@ -56,6 +57,10 @@ class TestContainerComponent {
5657
this.columsChanged++;
5758
}
5859
}
60+
61+
selectionChanged() {
62+
this.selectionCount++;
63+
}
5964
}
6065

6166

@@ -235,6 +240,35 @@ describe('DxDataGrid', () => {
235240
jasmine.clock().uninstall();
236241
});
237242

243+
it('should not call onSelectionChanged when selection is resetting', () => {
244+
TestBed.overrideComponent(TestContainerComponent, {
245+
set: {
246+
template: `<dx-data-grid *ngIf="showComponent"
247+
[dataSource]="[{id: 1, text: 'text'}, {id: 2, text: 'text2'}]"
248+
keyExpr="id"
249+
[selectedRowKeys]="[2]"
250+
(onSelectionChanged)="selectionChanged()">
251+
<dxo-selection mode="single"></dxo-selection>
252+
<dxi-column dataField="text"></dxi-column>
253+
</dx-data-grid>`
254+
}
255+
});
256+
257+
jasmine.clock().uninstall();
258+
jasmine.clock().install();
259+
260+
let fixture = TestBed.createComponent(TestContainerComponent);
261+
262+
fixture.detectChanges();
263+
264+
jasmine.clock().tick(101);
265+
let testComponent = fixture.componentInstance;
266+
testComponent.showComponent = false;
267+
fixture.detectChanges();
268+
expect(testComponent.selectionCount).toBe(0);
269+
jasmine.clock().uninstall();
270+
});
271+
238272
it('should destroy devextreme components in template correctly', () => {
239273
@Component({
240274
selector: 'test-container-component',

0 commit comments

Comments
 (0)