2

I'm trying to create a custom checkbox component styled with Switchery that can be used in a form like any other <input type="checkbox" ... /> component.

The code I have now takes care of the styling:

import {Component,ViewChild,AfterViewInit,Input} from 'angular2/core';
import switchery from 'switchery';

@Component({
  selector: 'switchery-checkbox',
  template: `<input #checkbox type="checkbox" class="js-switch"/>`,
})
export class SwitcheryComponent implements AfterViewInit {
  @Input() options: Switchery.Options = {};
  @ViewChild('checkbox') checkbox: any;
  ngAfterViewInit() {
    new switchery(this.checkbox.nativeElement,
                  this.options);
  }
}

What do I have to add to be able to use it in a template like in the following code? It should ideally implement all the functionality of <input type="checkbox" />.

<switchery-checkbox
     [(ngModel)]="model.onOrOff"
     ngControl="onOrOff"
     [disabled]="disabledCondition"
     ... >
</switchery-checkbox>
0

1 Answer 1

3

In fact you need to make your component "ngModel-compliant" but implementing a custom value accessor.

Here is the way to do:

@Component({
  selector: 'switchery-checkbox',
  template: `
    <input #checkbox type="checkbox" (change)="onChange($event.target.checked)" class="js-switch"/>
  `,
  (...)
})
export class SwitcheryComponent implements AfterViewInit, ControlValueAccessor {
  @Input() options: Switchery.Options = {};
  @Input() disabled:boolean = false;
  @ViewChild('checkbox') checkbox: any;

  value: boolean = false;

  onChange = (_) => {};
  onTouched = () => {};

  writeValue(value: any): void {
    this.value = value;
    this.setValue(this.value);
  }

  registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
  registerOnTouched(fn: () => void): void { this.onTouched = fn; }

  ngAfterViewInit() {
    this.switcher = new switchery(this.checkbox.nativeElement,
              this.options);
    this.setValue(this.value);
    this.setDisabled(this.disabled);
  }

  ngOnChanges(changes: {[propName: string]: SimpleChange}) {
    if (changes && changes.disabled) {
      this.setDisabled(changes.disabled.currentValue);
    }
  }

  setValue(value) {
    if (this.switcher) {
      var element = this.switcher.element;
      element.checked = value
    }
  }

  setDisabled(value) {
    if (this.switcher) {
      if (value) {
        this.switcher.disable();
      } else {
        this.switcher.enable();
      }
    }
  }
}

Finally you need to register the value accessor into the providers of the component:

const CUSTOM_VALUE_ACCESSOR = new Provider(
  NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => SwitcheryComponent), multi: true});

@Component({
  selector: 'switchery-checkbox',
  template: `
    <input #checkbox type="checkbox" (change)="onChange($event.target.checked)" class="js-switch"/>
  `,
  providers: [ CUSTOM_VALUE_ACCESSOR ]
})
export class SwitcheryComponent implements AfterViewInit, ControlValueAccessor {
  (...)
}

This way you can use your directive this way:

<switchery-checkbox [disabled]="disabled"
       [(ngModel)]="value" ngControl="cb"
       #cb="ngForm"></switchery-checkbox>

See this plunkr: https://plnkr.co/edit/z1gAC5U0pgMSq0wicGHC?p=preview.

See this article for more details (section "NgModel-compatible component"):

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.