diff --git a/src/core/component.ts b/src/core/component.ts index 6c8abc98c..2ee2cbd28 100644 --- a/src/core/component.ts +++ b/src/core/component.ts @@ -21,6 +21,7 @@ export abstract class DxComponent implements INestedOptionContainer, ICollection eventHelper: EmitterHelper; templates: DxTemplateDirective[]; instance: any; + changedOptions = {}; protected _events: { subscribe?: string, emit: string }[]; @@ -44,10 +45,19 @@ export abstract class DxComponent implements INestedOptionContainer, ICollection } private _initEvents() { this.instance.on('optionChanged', e => { - let changeEventName = e.name + 'Change'; - this.eventHelper.fireNgEvent(changeEventName, [e.value]); + this.changedOptions[e.name] = e.value; + this.eventHelper.fireNgEvent(e.name + 'Change', [e.value]); }); } + _shouldOptionChange(name: string, value: any) { + if (this.changedOptions.hasOwnProperty(name)) { + const prevValue = this.changedOptions[name]; + delete this.changedOptions[name]; + + return value !== prevValue; + } + return true; + } protected _getOption(name: string) { if (this.instance) { return this.instance.option(name); @@ -57,11 +67,16 @@ export abstract class DxComponent implements INestedOptionContainer, ICollection } protected _setOption(name: string, value: any) { if (this.instance) { - this.instance.option(name, value); + this._updateOption(name, value); } else { this._initialOptions[name] = value; } } + protected _updateOption(name: string, value: any) { + if (this._shouldOptionChange(name, value)) { + this.instance.option(name, value); + }; + } protected abstract _createInstance(element, options) protected _createWidget(element: any) { this._initTemplates(); diff --git a/src/core/iterable-differ-helper.ts b/src/core/iterable-differ-helper.ts index 94d3293b5..328df6766 100644 --- a/src/core/iterable-differ-helper.ts +++ b/src/core/iterable-differ-helper.ts @@ -21,23 +21,33 @@ export class IterableDifferHelper { } setup(prop: string, changes: SimpleChanges) { - if (prop in changes) { + if (prop in changes) { const value = changes[prop].currentValue; - if (value && Array.isArray(value)) { - if (!this._propertyDiffers[prop]) { - try { - this._propertyDiffers[prop] = this._differs.find(value).create(null); - } catch (e) { } - } - } else { - delete this._propertyDiffers[prop]; + this.setupSingle(prop, value); + } + } + + setupSingle(prop: string, value: any) { + if (value && Array.isArray(value)) { + if (!this._propertyDiffers[prop]) { + try { + this._propertyDiffers[prop] = this._differs.find(value).create(null); + } catch (e) { } } + } else { + delete this._propertyDiffers[prop]; + } + } + + getChanges(prop: string, value: any) { + if (this._propertyDiffers[prop]) { + return this._propertyDiffers[prop].diff(value); } } doCheck(prop: string) { if (this._propertyDiffers[prop]) { - const changes = this._propertyDiffers[prop].diff(this._host[prop]); + const changes = this.getChanges(prop, this._host[prop]); if (changes && this._host.instance) { this._host.instance.option(prop, this._host[prop]); } diff --git a/templates/component.tst b/templates/component.tst index bae3be26d..065e71c06 100644 --- a/templates/component.tst +++ b/templates/component.tst @@ -145,6 +145,15 @@ export class <#= it.className #>Component extends <#= baseClass #> <#? implement ngDoCheck() {<#~ collectionProperties :prop:i #> this._idh.doCheck('<#= prop #>');<#~#> this._watcherHelper.checkWatchers(); + } + + _updateOption(name: string, value: any) { + if (Array.isArray(value)) { + this._idh.setupSingle(name, value); + this._idh.getChanges(name, value); + } + + super._updateOption(name, value); }<#?#> <#? !it.isExtension #> ngAfterViewInit() { diff --git a/tests/src/ui/list.spec.ts b/tests/src/ui/list.spec.ts index 9102ea674..480f2fd3c 100644 --- a/tests/src/ui/list.spec.ts +++ b/tests/src/ui/list.spec.ts @@ -317,7 +317,8 @@ describe('DxList', () => { testComponent.complexItems[0].text = 'Changed'; fixture.detectChanges(); - expect(instance.option).toHaveBeenCalledTimes(0); + expect(instance.option).toHaveBeenCalledTimes(1); + expect(instance.option.calls.allArgs().length).toBe(1); expect(instance.option('items').length).toBe(2); expect(instance.element().find('.dx-item-content').length).toBe(2); expect(instance.element().find('.dx-item-content').eq(0).text()).toBe('Changed'); diff --git a/tests/src/ui/tag-box.spec.ts b/tests/src/ui/tag-box.spec.ts new file mode 100644 index 000000000..052f30968 --- /dev/null +++ b/tests/src/ui/tag-box.spec.ts @@ -0,0 +1,61 @@ +/* tslint:disable:component-selector */ + +import { + Component, + ViewChild +} from '@angular/core'; + +import { + TestBed, + async +} from '@angular/core/testing'; + +import { + DxTagBoxModule, + DxTagBoxComponent +} from '../../../dist'; + +@Component({ + selector: 'test-container-component', + template: '' +}) +class TestContainerComponent { + @ViewChild(DxTagBoxComponent) tagBox: DxTagBoxComponent; + + value: number[] = []; + testMethod() {} +} + +describe('DxTagBox', () => { + + beforeEach(() => { + TestBed.configureTestingModule( + { + declarations: [TestContainerComponent], + imports: [DxTagBoxModule] + }); + }); + + // spec + it('value change should be fired once', async(() => { + let testSpy = spyOn(TestContainerComponent.prototype, 'testMethod'); + TestBed.overrideComponent(TestContainerComponent, { + set: { + template: ` + + + ` + } + }); + let fixture = TestBed.createComponent(TestContainerComponent); + fixture.detectChanges(); + + expect(testSpy).toHaveBeenCalledTimes(0); + + let instance: any = fixture.componentInstance.tagBox.instance; + instance.option('value', [2]); + + fixture.detectChanges(); + expect(testSpy).toHaveBeenCalledTimes(1); + })); +});