frontend/src/app/spot/components/checkbox/checkbox.component.ts
import {
Component,
ElementRef,
EventEmitter,
forwardRef,
HostBinding,
Input,
Output,
ViewChild,
ChangeDetectionStrategy,
ChangeDetectorRef,
} from '@angular/core';
import {
ControlValueAccessor,
NG_VALUE_ACCESSOR,
} from '@angular/forms';
export type SpotCheckboxState = true|false|null;
@Component({
selector: 'spot-checkbox',
templateUrl: './checkbox.component.html',
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SpotCheckboxComponent),
multi: true,
}],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SpotCheckboxComponent implements ControlValueAccessor {
@HostBinding('class.spot-checkbox') public className = true;
@ViewChild('input') public input:ElementRef;
/**
* The tabindex for the underlying HTML input
*/
@Input() tabindex = 0;
/**
* Whether the input should be disabled
*/
@Input() disabled = false;
/**
* The name of the input. Will be autogenerated if not given,
* but especially useful to provide in a hybrid Rails <-> Angular context
* where a submit of a form is handled without JS.
*/
@Input() name = `spot-checkbox-${+(new Date())}`;
/**
* If you're not using Angular Reactive Forms (Which you should be using!)
* then you can manually set the checked state via this input.
*/
@Input() public checked = false;
/**
* Emits when the checked state changes.
*/
@Output() checkedChange = new EventEmitter<boolean>();
constructor(
readonly cdRef:ChangeDetectorRef,
) {}
onStateChange():void {
const value = (this.input.nativeElement as HTMLInputElement).checked;
this.checkedChange.emit(value);
this.onChange(value);
this.onTouched(value);
}
writeValue(value:SpotCheckboxState):void {
// This is set in a timeout because the initial value is set before the template is ready,
// which causes the input nativeElement to not be available yet.
setTimeout(() => {
const input = this.input.nativeElement as HTMLInputElement;
input.indeterminate = value === null;
this.checked = !!value;
this.cdRef.detectChanges();
});
}
onChange = (_:SpotCheckboxState):void => {};
onTouched = (_:SpotCheckboxState):void => {};
registerOnChange(fn:(_:SpotCheckboxState) => void):void {
this.onChange = fn;
}
registerOnTouched(fn:(_:SpotCheckboxState) => void):void {
this.onTouched = fn;
}
}