title: A custom angular two-state checkbox component issue
body: <p>I'm trying to create a custom checkbox component to replace an old-old NG Prime version that had been being used but wanted to make it leaner/cleaner with some added aria points and utilizing more of the checkbox attributes directly. The problem I'm running into though (I'm guessing) is with updating the value accessor / ngmodel and could use some guidance on what I'm apparently missing...</p>
  <p>I thought what I had was simplistic enough but apparently not. The idea is that <code>@Input() binary</code> is set to <code>true</code> by default expecting <code>ngModel</code> to have a boolean to use but I don't get the value from <code>ngModel</code> when it's initialized. I need <code>ngModel</code> to communicate that to the component though when it's used standalone as like <code>&lt;app-checkbox [ngModel]=&quot;blah&quot;&gt;&lt;/app-checkbox&gt;</code> but also have <code>value_accessor</code> working as expected if it's used in say a reactive angular form etc.</p>
  <p>I also get a &quot;NG0303: Can't bind to 'checked' since it isn't a known property of 'app-checkbox'&quot; in my instance, but it's not on the stackblitz which I don't understand.</p>
  <p> <a href="https://stackblitz.com/edit/angular-ivy-kmbgm4" rel="nofollow noreferrer">A Stackblitz showing the implementation / problem</a></p>
  <p>(the example is angular 12, I use 13, but it's fine for the example)</p>
  <p>And for quick code reference;</p>
  <p>.ts</p>
  <pre><code>import {
    Component,
    OnInit,
    Input,
    Output,
    EventEmitter,
    forwardRef,
    ViewChild,
    ElementRef,
    ChangeDetectorRef,
    ChangeDetectionStrategy,
  } from '@angular/core';
  import {
    NG_VALUE_ACCESSOR,
    ControlValueAccessor,
    FormControl,
  } from '@angular/forms';
  import { ObjectUtils } from '../objectUtils';
  
  export const CHECKBOX_VALUE_ACCESSOR: any = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() =&gt; CheckboxComponent),
    multi: true,
  };
  
  @Component({
    selector: 'app-checkbox',
    templateUrl: './checkbox.component.html',
    styleUrls: ['./checkbox.component.css'],
    providers: [CHECKBOX_VALUE_ACCESSOR],
    changeDetection: ChangeDetectionStrategy.OnPush,
  })
  export class CheckboxComponent implements OnInit, ControlValueAccessor {
    @Input() value: any;
    @Input() name: string;
    @Input() disabled: boolean;
  
    @Input() label: string;
    @Input() ariaLabelledBy: string;
    @Input() ariaLabel: string;
    @Input() tabindex: number;
    @Input() id: string;
    @Input() labelStyleClass: string;
    @Input() formControl: FormControl;
    @Input() required: boolean = false;
    @Input() isValid: boolean = true;
    @Input() invalidMessage: string;
  
    // This is set true by default now since every instance at the time this replacement component was made requires it for a boolean value bound to ngModel
    // If in the future we start using angular reactive forms more it can be toggled at the instance.
    @Input() binary: boolean = true;
  
    @ViewChild('cb') inputViewChild: ElementRef;
  
    @Output() onChange: EventEmitter&lt;any&gt; = new EventEmitter();
  
    isChecked: boolean = false;
  
    focused: boolean = false;
    model: any;
    onModelChange: Function = () =&gt; {};
    onModelTouched: Function = () =&gt; {};
  
    constructor(private cd: ChangeDetectorRef) {}
  
    ngOnInit(): void {
      this.id = this.id
        ? this.id
        : `tcl-cb-${Math.random()
            .toString()
            .substr(2, length ? length : 6)}`;
    }
  
    updateModel(event) {
      let newModelValue;
  
      if (!this.binary) {
        if (this.isChecked) {
          newModelValue = this.model.filter(
            (val) =&gt; !ObjectUtils.equals(val, this.value)
          );
        } else {
          newModelValue = this.model ? [...this.model, this.value] : [this.value];
        }
  
        this.onModelChange(newModelValue);
        this.model = newModelValue;
  
        if (this.formControl) {
          this.formControl.setValue(newModelValue);
        }
      } else {
        newModelValue = this.isChecked;
        this.model = newModelValue;
        this.onModelChange(newModelValue);
      }
  
      this.onChange.emit({ checked: newModelValue, originalEvent: event });
    }
  
    handleChange(event) {
      this.isChecked = event.srcElement.checked;
      if (!this.disabled) {
        this.updateModel(event);
      }
    }
  
    onFocus() {
      this.focused = true;
    }
  
    onBlur() {
      this.focused = false;
      this.onModelTouched();
    }
  
    focus() {
      this.inputViewChild.nativeElement.focus();
    }
  
    writeValue(model: any): void {
      this.model = model;
      this.cd.markForCheck();
    }
  
    registerOnChange(fn: Function): void {
      this.onModelChange = fn;
    }
  
    registerOnTouched(fn: Function): void {
      this.onModelTouched = fn;
    }
  
    setDisabledState(val: boolean): void {
      this.disabled = val;
      this.cd.markForCheck();
    }
  }
  
  </code></pre>
  <p>html</p>
  <pre><code>&lt;div role=&quot;group&quot; class=&quot;app-checkbox&quot;&gt;
    &lt;input
      #cb
      type=&quot;checkbox&quot;
      [attr.id]=&quot;id&quot;
      [attr.name]=&quot;name&quot;
      [disabled]=&quot;disabled&quot;
      [value]=&quot;value&quot;
      [checked]=&quot;isChecked&quot;
      [attr.tabindex]=&quot;tabindex&quot;
      [attr.required]=&quot;required&quot;
      [attr.aria-labelledby]=&quot;ariaLabelledBy&quot;
      [attr.aria-label]=&quot;ariaLabel&quot;
      [attr.aria-checked]=&quot;isChecked&quot;
      (focus)=&quot;onFocus()&quot;
      (blur)=&quot;onBlur()&quot;
      (change)=&quot;handleChange($event)&quot;
    /&gt;
    &lt;label *ngIf=&quot;label&quot; [attr.for]=&quot;id&quot; [class]=&quot;labelStyleClass&quot;&gt;
      {{ label }} &lt;sup *ngIf=&quot;required&quot;&gt;*&lt;/sup&gt;
    &lt;/label&gt;
    &lt;div *ngIf=&quot;!isValid&quot; class=&quot;tcl-checkbox-invalid-msg&quot;&gt;
      {{ invalidMessage }}
    &lt;/div&gt;
  &lt;/div&gt;
  
  </code></pre>
  <p>Any insight is appreciated especially if there's anything additional being forgotten!</p>