Skip to content

Commit 972dd16

Browse files
authored
Feat/checkbox control value accessor (#1432)
* 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(checkbox): add change detection * fix(checkbox): add checked getter * fix(checkbox): add disabled getter * fix(checkbox): fix linting issue
1 parent 5b04a42 commit 972dd16

File tree

2 files changed

+83
-14
lines changed

2 files changed

+83
-14
lines changed

projects/components/src/checkbox/checkbox.component.test.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { fakeAsync } from '@angular/core/testing';
2+
import { FormControl, ReactiveFormsModule } from '@angular/forms';
23
import { RouterTestingModule } from '@angular/router/testing';
34
import { createHostFactory, Spectator } from '@ngneat/spectator/jest';
45
import { CheckboxComponent } from './checkbox.component';
@@ -9,7 +10,7 @@ describe('Checkbox component', () => {
910

1011
const createHost = createHostFactory({
1112
component: CheckboxComponent,
12-
imports: [TraceCheckboxModule, RouterTestingModule],
13+
imports: [TraceCheckboxModule, RouterTestingModule, ReactiveFormsModule],
1314
providers: [],
1415
declareComponent: false
1516
});
@@ -52,12 +53,32 @@ describe('Checkbox component', () => {
5253

5354
// Click will toggle the values to true
5455
spectator.click(inputElement);
55-
expect(spectator.component.checked).toBe(true);
56+
expect(spectator.component.isChecked).toBe(true);
5657
expect(checkboxChangeSpy).toHaveBeenCalledWith(true);
5758

5859
// Click will toggle the values to false
5960
spectator.click(inputElement);
60-
expect(spectator.component.checked).toBe(false);
61+
expect(spectator.component.isChecked).toBe(false);
6162
expect(checkboxChangeSpy).toHaveBeenCalledWith(false);
6263
}));
64+
65+
test('should work correctly with control value accessor', () => {
66+
const formControl = new FormControl(false);
67+
spectator = createHost(
68+
`<ht-checkbox [label]="label" [formControl]="formControl">
69+
</ht-checkbox>`,
70+
{
71+
hostProps: {
72+
formControl: formControl
73+
}
74+
}
75+
);
76+
expect(spectator.component.isChecked).toBe(false);
77+
78+
formControl.setValue(true);
79+
expect(spectator.component.isChecked).toBe(true);
80+
81+
formControl.disable();
82+
expect(spectator.component.isDisabled).toBe(true);
83+
});
6384
});
Lines changed: 59 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 { MatCheckboxChange } from '@angular/material/checkbox';
34

45
@Component({
@@ -8,31 +9,78 @@ import { MatCheckboxChange } from '@angular/material/checkbox';
89
template: `
910
<mat-checkbox
1011
labelPosition="after"
11-
[checked]="this.checked"
12-
[disabled]="this.disabled"
13-
(change)="onCheckboxChange($event)"
12+
[checked]="this.isChecked"
13+
[disabled]="this.isDisabled"
14+
(change)="this.onCheckboxChange($event)"
1415
class="ht-checkbox"
15-
[ngClass]="{ disabled: this.disabled }"
16+
[ngClass]="{ disabled: this.isDisabled }"
1617
>
1718
<ht-label class="label" *ngIf="this.label !== undefined && this.label !== ''" [label]="this.label"></ht-label>
1819
</mat-checkbox>
19-
`
20+
`,
21+
providers: [
22+
{
23+
provide: NG_VALUE_ACCESSOR,
24+
useExisting: CheckboxComponent,
25+
multi: true
26+
}
27+
]
2028
})
21-
export class CheckboxComponent {
29+
export class CheckboxComponent implements ControlValueAccessor {
2230
@Input()
2331
public label?: string;
2432

2533
@Input()
26-
public checked: boolean | undefined;
34+
public set checked(checked: boolean | undefined) {
35+
this.isChecked = checked ?? false;
36+
}
37+
38+
public get checked(): boolean {
39+
return this.isChecked;
40+
}
2741

2842
@Input()
29-
public disabled: boolean | undefined;
43+
public set disabled(disabled: boolean | undefined) {
44+
this.isDisabled = disabled ?? false;
45+
}
46+
47+
public get disabled(): boolean {
48+
return this.isDisabled;
49+
}
3050

3151
@Output()
3252
public readonly checkedChange: EventEmitter<boolean> = new EventEmitter();
3353

54+
public isChecked: boolean = false;
55+
public isDisabled: boolean = false;
56+
57+
private onTouched!: () => void;
58+
private onChanged!: (value: boolean) => void;
59+
60+
public constructor(private readonly cdr: ChangeDetectorRef) {}
61+
3462
public onCheckboxChange(event: MatCheckboxChange): void {
35-
this.checked = event.checked;
36-
this.checkedChange.emit(this.checked);
63+
this.isChecked = event.checked;
64+
this.checkedChange.emit(this.isChecked);
65+
this.onChanged(this.isChecked);
66+
this.onTouched();
67+
}
68+
69+
public registerOnChange(fn: (value: boolean) => void): void {
70+
this.onChanged = fn;
71+
}
72+
73+
public registerOnTouched(fn: () => void): void {
74+
this.onTouched = fn;
75+
}
76+
77+
public setDisabledState(isDisabled: boolean): void {
78+
this.isDisabled = isDisabled;
79+
this.cdr.markForCheck();
80+
}
81+
82+
public writeValue(isChecked: boolean | undefined): void {
83+
this.isChecked = isChecked ?? false;
84+
this.cdr.markForCheck();
3785
}
3886
}

0 commit comments

Comments
 (0)