Skip to content

Commit d5bd492

Browse files
authored
Merge pull request #324 from DevExpress/items_change
Fix incorrect option update by declaring dxi components with ng-for
2 parents 83f350f + f54c0dc commit d5bd492

File tree

4 files changed

+62
-21
lines changed

4 files changed

+62
-21
lines changed

src/core/nested-option.ts

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,38 @@ export interface INestedOptionContainer {
66

77
export interface OptionPathGetter { (): string; }
88

9-
export abstract class NestedOption implements INestedOptionContainer, ICollectionNestedOptionContainer {
9+
export abstract class BaseNestedOption implements INestedOptionContainer, ICollectionNestedOptionContainer {
1010
protected _host: INestedOptionContainer;
1111
protected _hostOptionPath: OptionPathGetter;
1212
private _collectionContainerImpl: ICollectionNestedOptionContainer;
1313
protected _initialOptions = {};
1414

15-
protected _getOptionPath() {
16-
return this._hostOptionPath() + this._optionPath + '.';
15+
protected abstract get _optionPath(): string;
16+
protected abstract _fullOptionPath(): string;
17+
18+
constructor(private _element: ElementRef) {
19+
this._collectionContainerImpl = new CollectionNestedOptionContainerImpl(this._setOption.bind(this));
1720
}
1821

1922
protected _getOption(name: string): any {
20-
if (this.instance) {
21-
return this.instance.option(this._getOptionPath() + name);
23+
if (this.isLinked) {
24+
return this.instance.option(this._fullOptionPath() + name);
2225
} else {
2326
return this._initialOptions[name];
2427
}
2528
}
2629

2730
protected _setOption(name: string, value: any) {
28-
if (this.instance) {
29-
this.instance.option(this._getOptionPath() + name, value);
31+
if (this.isLinked) {
32+
this.instance.option(this._fullOptionPath() + name, value);
3033
} else {
3134
this._initialOptions[name] = value;
3235
}
3336
}
3437

35-
protected abstract get _optionPath(): string;
36-
37-
constructor(private _element: ElementRef) {
38-
this._collectionContainerImpl = new CollectionNestedOptionContainerImpl(this._setOption.bind(this));
39-
}
40-
4138
setHost(host: INestedOptionContainer, optionPath: OptionPathGetter) {
4239
this._host = host;
4340
this._hostOptionPath = optionPath;
44-
this._host[this._optionPath] = this._initialOptions;
4541
}
4642

4743
_template(...args) {
@@ -57,6 +53,9 @@ export abstract class NestedOption implements INestedOptionContainer, ICollectio
5753
return this._host && this._host.instance;
5854
}
5955

56+
get isLinked() {
57+
return !!this.instance;
58+
}
6059
}
6160

6261
export interface ICollectionNestedOptionContainer {
@@ -65,8 +64,7 @@ export interface ICollectionNestedOptionContainer {
6564

6665
export class CollectionNestedOptionContainerImpl implements ICollectionNestedOptionContainer {
6766
private _activatedQueries = {};
68-
constructor(private _setOption: Function) {
69-
}
67+
constructor(private _setOption: Function) {}
7068
setChildren<T extends ICollectionNestedOption>(propertyName: string, items: QueryList<T>) {
7169
if (items.length) {
7270
this._activatedQueries[propertyName] = true;
@@ -81,21 +79,37 @@ export class CollectionNestedOptionContainerImpl implements ICollectionNestedOpt
8179
}
8280
}
8381

82+
export abstract class NestedOption extends BaseNestedOption {
83+
setHost(host: INestedOptionContainer, optionPath: OptionPathGetter) {
84+
super.setHost(host, optionPath);
85+
86+
this._host[this._optionPath] = this._initialOptions;
87+
}
88+
89+
protected _fullOptionPath() {
90+
return this._hostOptionPath() + this._optionPath + '.';
91+
}
92+
}
93+
8494
export interface ICollectionNestedOption {
8595
_index: number;
8696
_value: Object;
8797
}
8898

89-
export abstract class CollectionNestedOption extends NestedOption implements ICollectionNestedOption {
99+
export abstract class CollectionNestedOption extends BaseNestedOption implements ICollectionNestedOption {
90100
_index: number;
91101

92-
protected _getOptionPath() {
102+
protected _fullOptionPath() {
93103
return this._hostOptionPath() + this._optionPath + '[' + this._index + ']' + '.';
94104
}
95105

96106
get _value() {
97107
return this._initialOptions;
98108
}
109+
110+
get isLinked() {
111+
return this._index !== undefined && !!this.instance;
112+
}
99113
}
100114

101115
export class NestedOptionHost {
@@ -107,7 +121,7 @@ export class NestedOptionHost {
107121
this._optionPath = optionPath || (() => '');
108122
}
109123

110-
setNestedOption(nestedOption: NestedOption) {
124+
setNestedOption(nestedOption: BaseNestedOption) {
111125
nestedOption.setHost(this._host, this._optionPath);
112126
}
113127
}

templates/nested-component.tst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export class <#= it.className #>Component extends <#= it.baseClass #> {<#~ it.pr
5353
this.template = this._template.bind(this);
5454
<#?#>
5555
parentOptionHost.setNestedOption(this);
56-
optionHost.setHost(this, this._getOptionPath.bind(this));
56+
optionHost.setHost(this, this._fullOptionPath.bind(this));
5757
}
5858
}
5959

tests/src/core/nested-option.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export class DxiTestCollectionOptionComponent extends CollectionNestedOption {
9090
super(element);
9191

9292
this._pnoh.setNestedOption(this);
93-
this._noh.setHost(this, this._getOptionPath.bind(this));
93+
this._noh.setHost(this, this._fullOptionPath.bind(this));
9494
}
9595
}
9696

tests/src/ui/list.spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,33 @@ describe('DxList', () => {
206206
expect(instance.element().find('.dx-item-content').eq(1).text()).toBe('2');
207207
}));
208208

209+
it('should be able to replace items by ng-for', async(() => {
210+
TestBed.overrideComponent(TestContainerComponent, {
211+
set: {
212+
template: `
213+
<dx-list>
214+
<dxi-item *ngFor="let item of items" [badge]="10">{{item}}</dxi-item>
215+
</dx-list>
216+
`
217+
}
218+
});
219+
let fixture = TestBed.createComponent(TestContainerComponent),
220+
testComponent = fixture.componentInstance;
221+
222+
testComponent.items = [1, 2];
223+
fixture.detectChanges();
224+
225+
let instance = getWidget(fixture);
226+
227+
testComponent.items = [3, 4];
228+
fixture.detectChanges();
229+
230+
expect(instance.option('items').length).toBe(2);
231+
expect(instance.element().find('.dx-item-content').length).toBe(2);
232+
expect(instance.element().find('.dx-item-content').eq(0).text()).toBe('3');
233+
expect(instance.element().find('.dx-item-content').eq(1).text()).toBe('4');
234+
}));
235+
209236
it('should be able to clear items rendered with *ngFor', async(() => {
210237
TestBed.overrideComponent(TestContainerComponent, {
211238
set: {

0 commit comments

Comments
 (0)