【Angular】チェックボックスを自作しよう!

ANGULAR

みなさんこんにちは、現役エンジニアのサメハックです

未経験からWebエンジニアに転職し、
正社員として5年働いたのちフリーランスとして独立しました。

Angularの解説シリーズです。

今回は自作のチェックボックスを作成するについて学んでいきましょう!

駆け出しエンジニアや未経験の方、
また新入社員を指導する先輩社員にとっても
わかりやすいように解説していきます!

この記事を読むと・・・
  • 自作のチェックボックスが作れる
  • Angular-Materialに頼らないデザインができる

※PCにnpm、nodeがインストールされている前提で記述します。
 yarn等をお使いの方は読み替えてください。

実行環境

Angularのバージョンが古いと動かないことがあるよ!
うまく動かなければアップデートしてね!

自作のチェックボックスを作ろう!

ここでは自作デザインのチェックボックスを作成して
共通のコンポーネントとして使い回せるようにします。

Material等を使うとバージョンアップなどでデザインが崩れることが多いので
選択肢の一つとして覚えておくと便利です。

ちょっと設定がややこしいけど基本的にコピペでOKだと!

初期設定

ターミナルを開いて以下のコマンドを実行してください。

ng new my-app --no-standalone
ng generate component custom-checkbox
cd my-app
ng serve -o
ng newした際にSCSSを選択してね!

補足

ng new my-app   →   新規Angularアプリの作成
ng generate component custom-checkbox → コンポーネント作成
cd my-app       →   ディレクトリ移動
ng serve -o     →   作成したAngularアプリの起動

app.component.html

app.component.html

<form>
  <!-- forms配列をループしてカスタムチェックボックスを生成します -->
  <div *ngFor="let form of forms">
    <app-custom-checkbox
      [(ngModel)]="form.isChecked"
      name="customCheckbox{{ form.id }}"
      [label]="form.userName"
      [color]="'pink'"
      [disabled]="form.id === 3"
      (valueChange)="onCheckboxChange($event, form.id)"
    >
    </app-custom-checkbox>
  </div>
</form>

<p>Form values:</p>
<ul>
  <li *ngFor="let form of forms">{{ form.userName }}: {{ form.isChecked }}</li>
</ul>

<!-- <app-custom-checkbox
  [(ngModel)]="値を連動させる"
  name="各チェックボックスに一意の名前を付けるために使用"
  [label]="チェックボックス横のテキスト"
  [disabled]="disabledとする際の条件"
  [color]="'$Colorsより選択'"
  (valueChange)="変更を受け取る関数"
>
</app-custom-checkbox> -->


app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss',
})
export class AppComponent {
  title = 'custom-checkbox';
  checkBoxValue: boolean = false;

  forms = [
    { id: 1, userName: 'サメハック', isChecked: false },
    { id: 2, userName: 'いぬハック', isChecked: false },
    { id: 3, userName: 'ねこハック', isChecked: false },
    { id: 4, userName: 'ぶたハック', isChecked: false },
  ];

  // チェックボックス変更を検知するメソッド
  onCheckboxChange(isChecked: boolean, id: number) {
    console.log('id', id);
    console.log('isChecked', isChecked);
  }
}

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CustomCheckboxComponent } from './custom-checkbox/custom-checkbox.component';
import { FormsModule } from '@angular/forms';

@NgModule({
  declarations: [AppComponent, CustomCheckboxComponent],
  imports: [BrowserModule, AppRoutingModule, FormsModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

custom-checkbox.component.html

<!-- ダイナミックにクラスを設定する
     クリックされた時に toggle() メソッドを呼び出す -->
<div [ngClass]="getColor()" (click)="toggle()">
  <!-- チェックボックスの状態を value プロパティにバインド
       無効状態を disabled プロパティにバインド -->
  <input type="checkbox" [checked]="value" [disabled]="disabled" />

  <!-- カスタムチェックボックスのスタイルを適用するための要素 -->
  <span class="custom-checkbox"></span>

  <!-- チェックボックスのラベル -->
  <label>{{ label }}</label>
</div>

custom-checkbox.component.ts

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

@Component({
  selector: 'app-custom-checkbox', // コンポーネントのセレクタ
  templateUrl: './custom-checkbox.component.html', // テンプレートファイル
  styleUrls: ['./custom-checkbox.component.scss'], // スタイルファイル
  providers: [
    {
      provide: NG_VALUE_ACCESSOR, // NG_VALUE_ACCESSORトークンを提供
      useExisting: forwardRef(() => CustomCheckboxComponent), // コンポーネント自体を使用
      multi: true, // 複数のプロバイダーを許可
    },
  ],
})
export class CustomCheckboxComponent implements ControlValueAccessor {
  @Input() label: string = ''; // 外部からラベルを受け取るためのInputプロパティ
  @Input() disabled: boolean = false; // 外部からdisabled状態を受け取るためのInputプロパティ
  @Input() color: string = 'default'; // 外部から色情報を受け取るためのInputプロパティ
  @Output() valueChange = new EventEmitter(); // 変更を親に渡す
  value: boolean = false; // チェックボックスの内部状態

  PLEFIX = 'custom-checkbox-';

  // クラス名の取得
  getColor() {
    return this.PLEFIX + this.color;
  }

  // コールバック関数の初期化
  onChange = (value: boolean) => {};
  onTouched = () => {};

  // フォームコントロールから新しい値を受け取ったときに呼ばれる
  writeValue(value: boolean): void {
    this.value = value;
  }

  // フォームコントロールに値の変更を伝えるための関数を登録
  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  // フォームコントロールにタッチイベントを伝えるための関数を登録
  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  // フォームコントロールからdisabled状態を受け取ったときに呼ばれる
  setDisabledState?(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  // チェックボックスの状態をトグルする
  toggle(): void {
    if (!this.disabled) {
      this.value = !this.value;
      this.onChange(this.value); // 新しい値をフォームコントロールに伝える
      this.onTouched(); // タッチイベントをフォームコントロールに伝える
      this.valueChange.emit(this.value); // 親に値を渡す
    }
  }
}

custom-checkbox.component.scss

$colors: (
  default: (
    // 文字色(白)
    label-color: #343a40,
    // 未チェックの際の枠の色(黒)
    un-checked-frame: #343a40,
    // チェックボックス内の色(黒)
    checkbox: #343a40,
    // チェックドのマークの色(白)
    checked-mark: #ffffff,
    // チェックドの際の枠の色(黒)
    checked-frame: #343a40,
  ),
  red: (
    // 文字色
    label-color: #dc3545,
    // 未チェックの際の枠の色
    un-checked-frame: #dc3545,
    // チェックボックス内の色
    checkbox: #dc3545,
    // チェックドのマークの色
    checked-mark: #721c24,
    // チェックドの際の枠の色
    checked-frame: #dc3545,
  ),
  blue: (
    // 文字色
    label-color: #007bff,
    // 未チェックの際の枠の色
    un-checked-frame: #007bff,
    // チェックボックス内の色
    checkbox: #007bff,
    // チェックドのマークの色
    checked-mark: #004085,
    // チェックドの際の枠の色
    checked-frame: #007bff,
  ),
  yellow: (
    // 文字色
    label-color: #ffc107,
    // 未チェックの際の枠の色
    un-checked-frame: #ffc107,
    // チェックボックス内の色
    checkbox: #ffc107,
    // チェックドのマークの色
    checked-mark: #856404,
    // チェックドの際の枠の色
    checked-frame: #ffc107,
  ),
  pink: (
    // 文字色
    label-color: #e83e8c,
    // 未チェックの際の枠の色
    un-checked-frame: #e83e8c,
    // チェックボックス内の色
    checkbox: #e83e8c,
    // チェックドのマークの色
    checked-mark: #6f0421,
    // チェックドの際の枠の色
    checked-frame: #e83e8c,
  ),
  green: (
    // 文字色
    label-color: #28a745,
    // 未チェックの際の枠の色
    un-checked-frame: #28a745,
    // チェックボックス内の色
    checkbox: #28a745,
    // チェックドのマークの色
    checked-mark: #155724,
    // チェックドの際の枠の色
    checked-frame: #28a745,
  ),
);

@each $color-name, $color in $colors {
  $label-color: map-get($color, label-color);
  $un-checked-frame: map-get($color, un-checked-frame);
  $checkbox: map-get($color, checkbox);
  $checked-mark: map-get($color, checked-mark);
  $checked-frame: map-get($color, checked-frame);

  .custom-checkbox-#{$color-name} {
    input[type="checkbox"] {
      display: none; /* チェックボックスを非表示にする */
    }

    /* 自作のチェックボックスの装飾用 */
    .custom-checkbox {
      display: inline-block; /* インラインブロック要素として表示 */
      width: 20px; /* 幅指定 */
      height: 20px; /* 高さ指定 */
      border: 2px solid $un-checked-frame; /* 枠線の赤 */
      background-color: transparent; /* デフォルトの背景色 */
      vertical-align: middle; /* 垂直方向の中央揃え */
      line-height: 1; /* ラインハイトをリセット */
      box-sizing: border-box; /* border-boxモデルを使用して要素のサイズを計算 */
      margin-right: 5px; /* チェックボックスとテキストの間隔 */
      top: -0.05em; /* 少し下にずれるので微調整 */
      position: relative; /* 相対位置指定 */
      border-radius: 2.5px; /* 丸み※20pxくらいにすると円になる */
    }

    /* チェックされたときの装飾 */
    input[type="checkbox"]:checked + .custom-checkbox {
      background: $checkbox;
      border-color: $checked-frame; /* 枠の色 */
    }

    /* チェックボックスのチェックマークのスタイル */
    input[type="checkbox"]:checked + .custom-checkbox::after {
      content: ""; /* コンテンツを追加 */
      position: absolute; /* 絶対位置指定 */
      top: 0%; /* 上端を中央に配置 */
      left: 25%; /* 左端を中央に配置 */
      width: 5px; /* 幅指定 */
      height: 10px; /* 高さ指定 */
      border: solid $checked-mark; /* チェックマークの色 */
      border-width: 0 2px 2px 0; /* チェックマークの形状を指定 */
      transform: rotate(45deg); /* チェックマークを回転させる */
    }

    /* テキストのスタイル */
    label {
      color: $label-color; /* テキストの色を指定 */
    } /* チェックボックスがdisabledの場合のスタイル */
    input[type="checkbox"]:disabled + .custom-checkbox {
      pointer-events: none; /* マウスイベントを無効化してクリックを無効にする */
      border-color: darkgray;
    } /* チェックボックスがdisabledの場合のテキストのスタイル */
    input[type="checkbox"]:disabled + .custom-checkbox + label {
      color: darkgray; /* テキストの色をダークグレーにする */
    } /* チェックボックスがdisabledでかつチェックされた場合のスタイル */
    input[type="checkbox"]:disabled:checked + .custom-checkbox {
      background-color: darkgray; /* チェックされていてdisabledの場合の背景色を指定 */
    }
  }
}

GitHubのサンプルコード

今回作ったものはGitHubにあげているので
うまく動作しなかった方は是非ダウンロードしてみてください。

GitHub - same-hack/angular-custom-checkbox
Contribute to same-hack/angular-custom-checkbox development by creating an account on GitHub.

満足いただけたら、1クリックなのでSNSフォローしてもらえると嬉しいです🦈

タイトルとURLをコピーしました