Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent valueChanges event when patchValue method is used with emitEvent=false #712

Merged
merged 10 commits into from
Mar 20, 2018
11 changes: 11 additions & 0 deletions src/core/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ export abstract class DxComponent implements OnChanges, OnInit, DoCheck, AfterCo
this.instance.endUpdate();
}
}
lockEventFire(name: string) {
this.eventHelper.lockEventFire.push(name + 'Change');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to have an array here? It seems that it's impossible to have several locked events simultaneously

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found the case, where we have two events in the array, but it's checked in the correct order. So, we can get rid of array here.

}
unlockEventFire(name: string) {
let index = this.eventHelper.lockEventFire.indexOf(name + 'Change');
if (index > -1) {
this.eventHelper.lockEventFire.splice(index, 1);
}
}
protected _setOption(name: string, value: any) {
this.lockWidgetUpdate();

Expand All @@ -101,7 +110,9 @@ export abstract class DxComponent implements OnChanges, OnInit, DoCheck, AfterCo
}

if (this.instance) {
this.lockEventFire(name);
this.instance.option(name, value);
this.unlockEventFire(name);
} else {
this._initialOptions[name] = value;
}
Expand Down
9 changes: 6 additions & 3 deletions src/core/events-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,17 @@ export class NgEventsStrategy {

export class EmitterHelper {
strategy: NgEventsStrategy;
lockEventFire: Array<string> = [];

constructor(ngZone: NgZone, public component: DxComponent) {
this.strategy = new NgEventsStrategy(component, ngZone);
}
fireNgEvent(eventName: string, eventArgs: any) {
let emitter = this.component[eventName];
if (emitter) {
emitter.next(eventArgs && eventArgs[0]);
if (this.lockEventFire.indexOf(eventName) === -1) {
let emitter = this.component[eventName];
if (emitter) {
emitter.next(eventArgs && eventArgs[0]);
}
}
}
createEmitter(ngEventName: string, dxEventName: string) {
Expand Down
17 changes: 17 additions & 0 deletions tests/src/core/component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,23 @@ describe('DevExtreme Angular widget', () => {
expect(testSpy).toHaveBeenCalledTimes(1);
}));

it('should not emit testOptionChange event when changes occur in component (T614207)', () => {
TestBed.overrideComponent(TestContainerComponent, {
set: {
template: '<dx-test-widget [(testOption)]="testOption" (testOptionChange)="testMethod()"></dx-test-widget>'
}
});
let fixture = TestBed.createComponent(TestContainerComponent);
fixture.detectChanges();

let component = fixture.componentInstance,
testSpy = spyOn(component, 'testMethod');

component.testOption = 'new value';
fixture.detectChanges();
expect(testSpy).toHaveBeenCalledTimes(0);
});

it('should change component option value', async(() => {
let fixture = TestBed.createComponent(DxTestWidgetComponent);
fixture.detectChanges();
Expand Down
74 changes: 74 additions & 0 deletions tests/src/ui/accordion.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/* tslint:disable:component-selector */

import {
Component,
ViewChildren,
QueryList
} from '@angular/core';

import { TestBed } from '@angular/core/testing';

import DxAccordion from 'devextreme/ui/accordion';

import {
DxAccordionModule,
DxAccordionComponent
} from '../../../dist';

@Component({
selector: 'test-container-component',
template: ''
})
class TestContainerComponent {
items = [{ id: 1, name: 'name1' }, { id: 2, name: 'name2' }];
selectedItem = this.items[0];
selectedIndex = 0;

@ViewChildren(DxAccordionComponent) innerWidgets: QueryList<DxAccordionComponent>;
}

describe('DxAccordion', () => {

beforeEach(() => {
TestBed.configureTestingModule(
{
declarations: [TestContainerComponent],
imports: [DxAccordionModule]
});
});

function getWidget(fixture) {
let widgetElement = fixture.nativeElement.querySelector('.dx-accordion') || fixture.nativeElement;
return DxAccordion['getInstance'](widgetElement) as any;
}

it('should change bound options', () => {
TestBed.overrideComponent(TestContainerComponent, {
set: {
template: `
<dx-accordion [items]="items" [(selectedIndex)]="selectedIndex" [(selectedItem)]="selectedItem">
</dx-accordion>
`
}
});

let fixture = TestBed.createComponent(TestContainerComponent);
fixture.detectChanges();

let accordion = fixture.componentInstance;
let instance = getWidget(fixture);

expect(instance.option('selectedIndex')).toBe(0);
expect(instance.option('selectedItem')).toBe(accordion.items[0]);
expect(accordion.selectedIndex).toBe(0);
expect(accordion.selectedItem).toBe(accordion.items[0]);

accordion.selectedIndex = 1;
fixture.detectChanges();

expect(instance.option('selectedIndex')).toBe(1);
expect(instance.option('selectedItem')).toBe(accordion.items[1]);
expect(accordion.selectedIndex).toBe(1);
expect(accordion.selectedItem).toBe(accordion.items[1]);
});
});