Creating an Angular Component Library - Checkbox Component


We can use the same process as we did with the toggle to create other form controls as well. We’ll create a similar control to the toggle but it will visually resemble a checkbox.

A full screen demo of we’re going to build

 

Prerequisites

 

Generating Our Checkbox

For our checkbox we will need to generate a module, component and several enum values which we will use to provide different values to customize our checkbox based on size, shape, color and label position.

We’ll use the Angular CLI to generate our module and component…

ng generate module components/checkbox --project=foo
ng generate component components/checkbox --project=foo

We need to update our module file to export our component to make it available to importing modules…

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CheckboxComponent } from './checkbox.component';

@NgModule({
  declarations: [
    CheckboxComponent
  ],
  imports: [
    CommonModule
  ],
  exports: [
    CheckboxComponent
  ]
})
export class CheckboxModule { }

Now we can create our enum values that will be used for differenct configurations for a checkbox.

ng generate enum components/checkbox/checkbox-color --project=foo
ng generate enum components/checkbox/checkbox-label-position --project=foo
ng generate enum components/checkbox/checkbox-shape --project=foo
ng generate enum components/checkbox/checkbox-size --project=foo

We’ll again break out our SCSS into smaller files to keep things more readable and maintainable….

touch projects/foo/src/lib/components/checkbox/_checkbox-colors.scss \
    projects/foo/src/lib/components/checkbox/_checkbox-label-positions.scss \
    projects/foo/src/lib/components/checkbox/_checkbox-shapes.scss \
    projects/foo/src/lib/components/checkbox/_checkbox-sizes.scss

Now we just need to update our components directory’s public_api.ts file…

export * from './checkbox/checkbox-color.enum';
export * from './checkbox/checkbox-label-position.enum';
export * from './checkbox/checkbox-shape.enum';
export * from './checkbox/checkbox-size.enum';
export * from './checkbox/checkbox.component';
export * from './checkbox/checkbox.module';

 

Creating Our Enum Values

Our enum values simply map to CSS classes to apply the appropriate styles to the checkbox component…

Our colors enum…

export enum CheckboxColor {
  PRIMARY = 'checkbox-color-primary',
  SECONDARY = 'checkbox-color-secondary',
  SUCCESS = 'checkbox-color-success',
  INFO = 'checkbox-color-info',
  WARNING = 'checkbox-color-warning',
  DANGER = 'checkbox-color-danger'
}

Our label positioning enum…

export enum CheckboxLabelPosition {
  LEFT = 'checkbox-label-position-right',
  RIGHT = 'checkbox-label-position-left'
}

Our shape enum…

export enum CheckboxShape {
  SQUARE = 'checkbox-shape-square',
  ROUNDED = 'checkbox-shape-rounded',
  CIRCLE = 'checkbox-shape-circle'
}

And finally our size enum…

export enum CheckboxSize {
  SMALL = 'checkbox-size-small',
  MEDIUM = 'checkbox-size-medium',
  LARGE = 'checkbox-size-large'
}

 

Creating Out Checkbox Component

We going to have our component implement the ControlValueAccessor interface that Angular provides to us just like we did with our toggle switch.

Our component class file… @@@ TODO say more…

import { Component, Input, forwardRef } from "@angular/core";
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";

import { CheckboxColor } from "./checkbox-color.enum";
import { CheckboxSize } from "./checkbox-size.enum";
import { CheckboxShape } from "./checkbox-shape.enum";
import { CheckboxLabelPosition } from "./checkbox-label-position.enum";

@Component({
  selector: "foo-checkbox",
  templateUrl: "./checkbox.component.html",
  styleUrls: ["./checkbox.component.scss"],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CheckboxComponent),
      multi: true
    }
  ]
})
export class CheckboxComponent implements ControlValueAccessor {
  @Input()
  public label: string;

  @Input()
  public labelPosition: CheckboxLabelPosition;

  @Input()
  public shape: CheckboxShape;

  @Input()
  public size: CheckboxSize;

  @Input()
  public color: CheckboxColor;

  @Input()
  public disabled: boolean;

  @Input()
  public set value(isChecked: boolean) {
    if (!this.disabled) {
      this.isChecked = isChecked;
      this.onChange(isChecked);
    }
  }

  public isChecked: boolean = false;
  public onChange: any = () => {};
  public onTouch: any = () => {};

  constructor() {
    this.labelPosition = CheckboxLabelPosition.RIGHT;
    this.shape = CheckboxShape.ROUNDED;
    this.size = CheckboxSize.MEDIUM;
    this.color = CheckboxColor.PRIMARY;
    this.disabled = false;
  }

  public registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  public registerOnTouched(fn: any): void {
    this.onTouch = fn;
  }

  public writeValue(checked: boolean): void {
    this.isChecked = checked;
  }

  public setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  public toggleChecked(isChecked: boolean): void {
    if (!this.disabled) {
      this.isChecked = !this.isChecked;
      this.onChange(this.isChecked);
    }
  }

  public get classes(): string {
    return `${this.labelPosition} ${this.shape} ${this.size} ${this.color}`;
  }
}

Our HTML template…

<div class="checkbox-container" [ngClass]="classes">
  <div class="checkbox" 
      [ngClass]="{ 'checked' : isChecked }" 
      (click)="toggleChecked()">
    <div class="checkmark">&#10006;</div>
  </div>
  <label class="checkbox-label">{{ label || '' }}</label>
</div>

Now there is quite a bit of styling for our checkbox hence why we chose to break our out SCSS into small files instead of throwing all the SCSS into one file.

We’ll pull in our SASS variables file for each scss files. Our _checkbox-colors.scss file will look something like…

@import '../_shared/scss/variables';

@mixin create-checkbox-color($color) {
  .checkbox {
    &.checked {
      background: $color;
      &:hover {
        background: darken($color, 10%);
      }
      &:active {
        background: darken($color, 15%);
      }
    }
  }
}

@mixin generate-checkbox-colors {
  &.checkbox-color-primary {
    @include create-checkbox-color($primary);
  }

  &.checkbox-color-secondary {
    @include create-checkbox-color($secondary);
  }

  &.checkbox-color-success {
    @include create-checkbox-color($success);
  }

  &.checkbox-color-info {
    @include create-checkbox-color($info);
  }

  &.checkbox-color-warning {
    @include create-checkbox-color($warning);
  }

  &.checkbox-color-danger {
    @include create-checkbox-color($danger);
  }
}

Our _label-positions.scss file…

@import '../_shared/scss/variables';

@mixin generate-checkbox-label-positions {
  &.checkbox-label-position-left {
    flex-direction: row;
    .checkbox-label {
      margin-left: 0.25rem;
    }
  }
  
  &.checkbox-label-position-right {
    flex-direction: row-reverse;
    justify-content: flex-end;
    .checkbox-label {
      margin-right: 0.25rem;
    }
  }
}

Our _checkbox-shapes.scss file…

@import '../_shared/scss/variables';

@mixin create-checkbox-shape($radius) {
  .checkbox{
    border-radius: $radius;
    .checkmark {
      border-radius: $radius;
    }
  }
}

@mixin generate-checkbox-shapes {
  &.checkbox-shape-square {
    @include create-checkbox-shape(0);
  }

  &.checkbox-shape-rounded {
    @include create-checkbox-shape(4px);
  }

  &.checkbox-shape-circle {
    @include create-checkbox-shape(3rem);
  }
}

Our _checkbox-sizes.scss file…

@import '../_shared/scss/variables';

@mixin create-checkbox-size($checkbox-size, $font-size) {
  .checkbox {
    width: $checkbox-size;
    height:  $checkbox-size;
    font-size:  $font-size;
    .checkmark {
      line-height: $checkbox-size;
    }
  }
  .checkbox-label {
    font-size: $font-size;
    line-height: $checkbox-size;
  }
}

@mixin generate-checkbox-sizes {
  &.checkbox-size-small {
    @include create-checkbox-size(22px, 1rem);
  }

  &.checkbox-size-medium {
    @include create-checkbox-size(32px, 1.2rem);
  }

  &.checkbox-size-large {
    @include create-checkbox-size(42px, 1.5rem);
  }
}

And finally our checkbox.component.scss file…

@import '../_shared/scss/variables';
@import './_checkbox-colors';
@import './_checkbox-shapes';
@import './_checkbox-sizes';
@import './_checkbox-label-positions';

.checkbox-container {
  display: inline-flex;

  .checkbox {
    box-sizing: border-box;
    background: lighten($default, 20%);
    color: $card-background;
    transition: background 0.2s, visibility 0.2s;
    cursor: pointer;

    .checkmark {
      visibility: hidden;
      width: 100%;
      height: 100%;
      text-align: center;
      vertical-align: middle;
    }

    &:hover {
      background: lighten($default, 15%);
    }

    &:active {
      background: lighten($default,  10%);
    }

    &.checked {
      .checkmark {
        visibility: visible;
      }
    }
  }

  @include generate-checkbox-label-positions;
  @include generate-checkbox-colors;
  @include generate-checkbox-sizes;
  @include generate-checkbox-shapes;
}

 

Stackblitz Result

Below is an embeded Stackblitz showing our checkbox being used with our [dashabord layout][14] we create a few posts back. I’ve created two examples, one with our checkbox being used in a reactive form and the other being used with a template driven form.

A full screen demo can also be viewed here

 

 

The completed github repository can be found here

 


comments powered by Disqus