Skip to content

Commit dead5c4

Browse files
authored
Feat/toggle switch control value accessor (#1440)
* feat(checkbox): add control value accessor implementation * test(checkbox): add supporting test cases for control value accessor * feat(checkbox): fix lint issue * fix(checkbox): type coercion fix * feat(toggle): add control value accessor * feat(toggle): add change detection * fix(toggle): add checked getter to not make breaking change * fix(toggle): add getter for disabled * fix(toggle): fix lint issue
1 parent 972dd16 commit dead5c4

File tree

2 files changed

+78
-13
lines changed

2 files changed

+78
-13
lines changed

projects/components/src/toggle-switch/toggle-switch.component.test.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { CommonModule } from '@angular/common';
22
import { fakeAsync } from '@angular/core/testing';
3-
import { FormsModule } from '@angular/forms';
3+
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
44
import { MatSlideToggle, MatSlideToggleChange, MatSlideToggleModule } from '@angular/material/slide-toggle';
55
import { createHostFactory, Spectator } from '@ngneat/spectator/jest';
66
import { ToggleSwitchSize } from './toggle-switch-size';
@@ -12,7 +12,7 @@ describe('Toggle Switch Component', () => {
1212
const createHost = createHostFactory({
1313
component: ToggleSwitchComponent,
1414
shallow: true,
15-
imports: [MatSlideToggleModule, FormsModule, CommonModule]
15+
imports: [MatSlideToggleModule, FormsModule, CommonModule, ReactiveFormsModule]
1616
});
1717

1818
test('should pass properties to Mat Slide toggle correctly', fakeAsync(() => {
@@ -41,4 +41,20 @@ describe('Toggle Switch Component', () => {
4141
spectator.triggerEventHandler(MatSlideToggle, 'change', new MatSlideToggleChange(matToggleComponent!, false));
4242
expect(onCheckedChangeSpy).toHaveBeenCalledWith(false);
4343
}));
44+
45+
test('should work correctly with control value accessor', () => {
46+
const formControl = new FormControl(false);
47+
spectator = createHost(`<ht-toggle-switch [label]="label" [formControl]="formControl"></ht-toggle-switch>`, {
48+
hostProps: {
49+
formControl: formControl
50+
}
51+
});
52+
expect(spectator.component.isChecked).toBe(false);
53+
54+
formControl.setValue(true);
55+
expect(spectator.component.isChecked).toBe(true);
56+
57+
formControl.disable();
58+
expect(spectator.component.isDisabled).toBe(true);
59+
});
4460
});
Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
1+
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
2+
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
23
import { MatSlideToggleChange } from '@angular/material/slide-toggle';
34
import { ToggleSwitchSize } from './toggle-switch-size';
45

@@ -10,33 +11,81 @@ import { ToggleSwitchSize } from './toggle-switch-size';
1011
<div class="toggle-switch">
1112
<mat-slide-toggle
1213
color="primary"
13-
[checked]="this.checked"
14-
[ngClass]="{ 'small-slide-toggle': this.size === '${ToggleSwitchSize.Small}', disabled: this.disabled }"
15-
[disabled]="this.disabled"
14+
[checked]="this.isChecked"
15+
[ngClass]="{ 'small-slide-toggle': this.size === '${ToggleSwitchSize.Small}', disabled: this.isDisabled }"
16+
[disabled]="this.isDisabled"
1617
(change)="this.onToggle($event)"
1718
>
1819
<div class="label">{{ this.label }}</div>
1920
</mat-slide-toggle>
2021
</div>
21-
`
22+
`,
23+
providers: [
24+
{
25+
provide: NG_VALUE_ACCESSOR,
26+
useExisting: ToggleSwitchComponent,
27+
multi: true
28+
}
29+
]
2230
})
23-
export class ToggleSwitchComponent {
31+
export class ToggleSwitchComponent implements ControlValueAccessor {
2432
@Input()
25-
public checked?: boolean;
33+
public label?: string = '';
2634

2735
@Input()
28-
public label?: string = '';
36+
public size: ToggleSwitchSize = ToggleSwitchSize.Small;
2937

3038
@Input()
31-
public disabled?: boolean;
39+
public set checked(checked: boolean | undefined) {
40+
this.isChecked = checked ?? false;
41+
}
42+
43+
public get checked(): boolean {
44+
return this.isChecked;
45+
}
3246

3347
@Input()
34-
public size: ToggleSwitchSize = ToggleSwitchSize.Small;
48+
public set disabled(disabled: boolean | undefined) {
49+
this.isDisabled = disabled ?? false;
50+
}
51+
52+
public get disabled(): boolean {
53+
return this.isDisabled;
54+
}
3555

3656
@Output()
3757
public readonly checkedChange: EventEmitter<boolean> = new EventEmitter();
3858

59+
public isChecked: boolean = false;
60+
public isDisabled: boolean = false;
61+
62+
private onTouched!: () => void;
63+
private onChanged!: (value: boolean) => void;
64+
65+
public constructor(private readonly cdr: ChangeDetectorRef) {}
66+
3967
public onToggle(toggleChange: MatSlideToggleChange): void {
40-
this.checkedChange.emit(toggleChange.checked);
68+
this.isChecked = toggleChange.checked;
69+
this.checkedChange.emit(this.isChecked);
70+
this.onChanged(this.isChecked);
71+
this.onTouched();
72+
}
73+
74+
public registerOnChange(fn: (value: boolean) => void): void {
75+
this.onChanged = fn;
76+
}
77+
78+
public registerOnTouched(fn: () => void): void {
79+
this.onTouched = fn;
80+
}
81+
82+
public setDisabledState(isDisabled: boolean): void {
83+
this.isDisabled = isDisabled;
84+
this.cdr.markForCheck();
85+
}
86+
87+
public writeValue(isChecked: boolean | undefined): void {
88+
this.isChecked = isChecked ?? false;
89+
this.cdr.markForCheck();
4190
}
4291
}

0 commit comments

Comments
 (0)