j.nord
j.nord

Reputation: 31

Angular 2 CheckboxControlValueAccessor implements

When we implement the CheckboxControlValueAccessor we get the follow error:

Types have separate declarations of a private property '_renderer'

Code:

export class NHCheckbox implements CheckboxControlValueAccessor {

_align = "right";
_label = "";                    // Label
_required = false;
_tooltip: string = null;


constructor(
    private _renderer: Renderer,
    private _elementRef: ElementRef
) {

}

CheckboxControlValueAccessor class:

export declare class CheckboxControlValueAccessor implements ControlValueAccessor {
private _renderer;
private _elementRef;
onChange: (_: any) => void;
onTouched: () => void;
constructor(_renderer: Renderer, _elementRef: ElementRef);
writeValue(value: any): void;
registerOnChange(fn: (_: any) => {}): void;
registerOnTouched(fn: () => {}): void;
setDisabledState(isDisabled: boolean): void;

}

What are we doing wrong?

Angular Version: 4.1.3 Typescript: 2.3.4

Upvotes: 3

Views: 3262

Answers (1)

Kent Weigel
Kent Weigel

Reputation: 1178

When I needed a checkbox component in Angular 2+, it seemed intuitive to do the same thing that you are trying to do. Why start with a general control when you can start with a checkbox, right? After spending too much time on it, I decided to relent and use ControlValueAccessor instead.

As you can see in this plnkr, the checkbox component can be written like this:

import { Component, /*Input,*/ Renderer, ElementRef, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'my-checkbox',
  template: `
      <div class="checkbox-container">
        <input type="checkbox" id="{{ controlID }}"
              [checked]="checked" (change)="onChange($event)" />
        <label for="{{ controlID }}"></label>
      </div>
    `,
  styles: [`
        .checkbox-container {
            background-color: #ddd;
            width: 20px;
            height: 20px;
            position: relative;
        }
    `, `
        .checkbox-container input[type="checkbox"] {
            visibility: hidden;
        }
    `, `
        .checkbox-container label {
          width: 18px;
          height: 18px;
          position: absolute;
          top: 1px;
          left: 1px;
          background-color: white;
        }
    `,`
        .checkbox-container label:before {
            content: '';
            width: 16px;
            height: 8px;
            border: 4px solid #000;
            position: absolute;
            border-top: none;
            border-right: none;
            transform: rotate(-50deg);
            top: 1px;
            left: 1px;
            opacity: 0;
        }
    `, `
        .checkbox-container input[type="checkbox"]:checked + label:before {
            opacity: 1;
        }
    `],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CheckboxComponent),
      multi: true
    }
  ]
})
export class CheckboxComponent implements ControlValueAccessor {
  static idCounter: Number = 0;
  controlID: String;
  //@Input() checked: Boolean;
  checked: Boolean;

  constructor(private renderer: Renderer, private elementRef: ElementRef) {
    this.controlID = "myCheckbox" + CheckboxComponent.idCounter++;
  }

  propagateChange = (_: any) => { };
  onTouchedCallback: () => {};

  writeValue(value: any) {
    if ((value !== undefined) && (value !== null)) {
      this.checked = value;
    }
  }

  registerOnChange(fn: any) {
    this.propagateChange = fn;
  }

  registerOnTouched(fn: any) {
    this.onTouchedCallback = fn;
  };

  onChange(event) {
    this.checked = event.target.checked;
    this.propagateChange(event.target.checked);
  }
}

and can be used like this:

<form #form="ngForm" (ngSubmit)="submit(form.value)">
  <div>
    <h2>Checkbox Demo Using {{ framework }}</h2>
  </div>
  <div>
    <my-checkbox name="b1" [(ngModel)]="b1"></my-checkbox>
    <my-checkbox name="b2" [(ngModel)]="b2"></my-checkbox>
    <my-checkbox name="b3" [(ngModel)]="b3"></my-checkbox>
  </div>
</form>

<pre>{{ form.value | json }}</pre>

Notes:

  1. controlID must be unique, which can be accomplished as shown using a static counter.
  2. I decided against making checked be an Input() variable, since you can accomplish the same thing with outer ngModel variables.
  3. Readonly and other functionality has been omitted for simplicity.

Upvotes: 4

Related Questions