Forked from Fredx87/sync-control.directive.spec.ts
Created
September 17, 2018 08:17
-
-
Save djleonskennedy/916478701563db088b71c3fcd3954d07 to your computer and use it in GitHub Desktop.
Angular SyncControl directive
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component } from '@angular/core'; | |
import { async, TestBed } from '@angular/core/testing'; | |
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; | |
import { By } from '@angular/platform-browser'; | |
import { SyncControlDirective } from './sync-control.directive'; | |
class FormGroupHostComponent { | |
formGroup = new FormGroup({ | |
ctrl: new FormControl('') | |
}); | |
} | |
@Component({ | |
template: ` | |
<form [formGroup]="formGroup"> | |
<input formControlName="ctrl" appSyncControl> | |
<input formControlName="ctrl" appSyncControl> | |
</form> | |
` | |
}) | |
class FormControlNameHostComponent extends FormGroupHostComponent {} | |
@Component({ | |
template: ` | |
<div> | |
<input [formControl]="ctrl" appSyncControl> | |
<input [formControl]="ctrl" appSyncControl> | |
</div> | |
` | |
}) | |
class FormControlHostComponent { | |
ctrl = new FormControl(''); | |
} | |
@Component({ | |
template: ` | |
<form [formGroup]="formGroup"> | |
<input formControlName="ctrl" [appSyncControl]="true"> | |
<input formControlName="ctrl" [appSyncControl]="false"> | |
<input formControlName="ctrl" appSyncControl> | |
</form> | |
` | |
}) | |
class ActiveStateHostComponent extends FormGroupHostComponent {} | |
@Component({ | |
template: ` | |
<form [formGroup]="formGroup"> | |
<input formControlName="ctrl"> | |
<input formControlName="ctrl" [appSyncControl]="syncControls"> | |
<input formControlName="ctrl" [appSyncControl]="syncControls"> | |
</form> | |
` | |
}) | |
class DynamicActiveStateHostComponent extends FormGroupHostComponent { | |
syncControls: boolean; | |
} | |
describe('SyncControlDirective', () => { | |
beforeEach(async(() => { | |
TestBed.configureTestingModule({ | |
imports: [ReactiveFormsModule], | |
declarations: [ | |
SyncControlDirective, | |
FormControlNameHostComponent, | |
FormControlHostComponent, | |
ActiveStateHostComponent, | |
DynamicActiveStateHostComponent | |
] | |
}); | |
})); | |
it('should sync two controls with formControlName', () => { | |
const fixture = TestBed.createComponent(FormControlNameHostComponent); | |
fixture.detectChanges(); | |
const inputs = fixture.debugElement.queryAll(By.css('input')); | |
const firstInputEl = inputs[0].nativeElement as HTMLInputElement; | |
const secondInputEl = inputs[1].nativeElement as HTMLInputElement; | |
firstInputEl.value = 'test'; | |
firstInputEl.dispatchEvent(new Event('input')); | |
fixture.detectChanges(); | |
expect(firstInputEl.value).toEqual('test'); | |
expect(secondInputEl.value).toEqual('test'); | |
}); | |
it('should sync two controls with formControl', () => { | |
const fixture = TestBed.createComponent(FormControlHostComponent); | |
fixture.detectChanges(); | |
const inputs = fixture.debugElement.queryAll(By.css('input')); | |
const firstInputEl = inputs[0].nativeElement as HTMLInputElement; | |
const secondInputEl = inputs[1].nativeElement as HTMLInputElement; | |
firstInputEl.value = 'hello!'; | |
firstInputEl.dispatchEvent(new Event('input')); | |
fixture.detectChanges(); | |
expect(firstInputEl.value).toEqual('hello!'); | |
expect(secondInputEl.value).toEqual('hello!'); | |
}); | |
it('should sync controls with undefined or true input, and should not sync controls with false input', () => { | |
const fixture = TestBed.createComponent(ActiveStateHostComponent); | |
fixture.detectChanges(); | |
const inputs = fixture.debugElement.queryAll(By.css('input')); | |
const firstInputEl = inputs[0].nativeElement as HTMLInputElement; | |
const secondInputEl = inputs[1].nativeElement as HTMLInputElement; | |
const thirdInputEl = inputs[2].nativeElement as HTMLInputElement; | |
firstInputEl.value = 'sync me'; | |
firstInputEl.dispatchEvent(new Event('input')); | |
fixture.detectChanges(); | |
expect(firstInputEl.value).toEqual('sync me'); | |
expect(secondInputEl.value).not.toEqual('sync me'); | |
expect(thirdInputEl.value).toEqual('sync me'); | |
}); | |
it('should dinamically sync controls if input changes', () => { | |
const fixture = TestBed.createComponent(DynamicActiveStateHostComponent); | |
const component = fixture.componentInstance; | |
fixture.detectChanges(); | |
const inputs = fixture.debugElement.queryAll(By.css('input')); | |
const firstInputEl = inputs[0].nativeElement as HTMLInputElement; | |
const secondInputEl = inputs[1].nativeElement as HTMLInputElement; | |
const thirdInputEl = inputs[2].nativeElement as HTMLInputElement; | |
firstInputEl.value = 'first sync'; | |
firstInputEl.dispatchEvent(new Event('input')); | |
fixture.detectChanges(); | |
expect(firstInputEl.value).toEqual('first sync'); | |
expect(secondInputEl.value).toEqual('first sync'); | |
expect(thirdInputEl.value).toEqual('first sync'); | |
component.syncControls = false; | |
fixture.detectChanges(); | |
firstInputEl.value = 'second sync'; | |
firstInputEl.dispatchEvent(new Event('input')); | |
fixture.detectChanges(); | |
expect(firstInputEl.value).toEqual('second sync'); | |
expect(secondInputEl.value).not.toEqual('second sync'); | |
expect(thirdInputEl.value).not.toEqual('second sync'); | |
component.syncControls = true; | |
fixture.detectChanges(); | |
firstInputEl.value = 'third sync'; | |
firstInputEl.dispatchEvent(new Event('input')); | |
fixture.detectChanges(); | |
expect(firstInputEl.value).toEqual('third sync'); | |
expect(secondInputEl.value).toEqual('third sync'); | |
expect(thirdInputEl.value).toEqual('third sync'); | |
}); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Directive, Input, OnDestroy } from '@angular/core'; | |
import { NgControl } from '@angular/forms'; | |
import { Subscription } from 'rxjs'; | |
@Directive({ | |
selector: '[appSyncControl]' | |
}) | |
export class SyncControlDirective implements OnDestroy { | |
// tslint:disable-next-line:no-input-rename | |
@Input('appSyncControl') | |
set active(val: boolean | undefined) { | |
if (val === false) { | |
this.destroySubscription(); | |
} else { | |
this.createSubscription(); | |
} | |
} | |
private subscription?: Subscription; | |
constructor(private ngControl: NgControl) {} | |
ngOnDestroy() { | |
this.destroySubscription(); | |
} | |
createSubscription() { | |
if (this.subscription || !this.ngControl || !this.ngControl.control) { | |
return; | |
} | |
this.subscription = this.ngControl.control.valueChanges.subscribe(value => { | |
if (this.ngControl.valueAccessor) { | |
this.ngControl.valueAccessor.writeValue(value); | |
} | |
}); | |
} | |
destroySubscription() { | |
if (this.subscription) { | |
this.subscription.unsubscribe(); | |
this.subscription = undefined; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment