LoginSignup
4
3

[Hotwire] 数字を自動フォーマット

Last updated at Posted at 2023-09-22

はじめに

桁数が多い数字は桁数が合っているか判断しづらいです。判断しやすくするために、3桁区切りでカンマを入れます。自ら3桁区切りでカンマを入れることも可能ですが、それはやりたくないでしょう。

そこで、HotwireのStimulusを使って、数字が自動フォーマットされるようにしました。

Sep-18-2023 22-34-39.gif

環境

  • Rails 7.0.7
  • Ruby 3.2.2

実装

ただ、数字をフォーマットすればいいだけに思いますが、実際は違います。データベースに保存する時にはカンマを取り除く必要があります。カンマがついた状態では、データベースで数値として扱えないからです。数値として扱えないと、年収が1,000,000円以上のユーザーを検索するのも難しくなります。数値として扱うために、データベースに保存する前に、数字からカンマを取り除く必要があります。

つまり、以下のことができればいいということです。

  1. 入力時に数字にカンマをつける
  2. 登録するボタンを押した時に数字からカンマを取り除く

1. 入力時に数字にカンマをつける

数字を入力する<input>要素に入力があるたびに、数字にカンマがつくようにします。

数字にカンマをつけるためのstimulus controllerを作成します。

app/javascript/controllers/numeral_controller.js
import { Controller } from '@hotwired/stimulus'
import numeral from 'numeral'

// Connects to data-controller="numeral"
export default class extends Controller {
  format(event) {
    event.target.value = numeral(event.target.value).format();
  }
}

numeral により、数字をフォーマットしたり、フォーマットしたものを数字に戻すことができます。

formatメソッドが実行されると、そのトリガーとなったHTML要素のvalueをフォーマットします。つまり、3桁区切りでカンマがつきます。

以下のようにdata-controllerdata-actionをHTML要素の属性として持たせれば、入力のたびに数字がフォーマットされます。

app/views/users/_form.html.haml
= simple_form_for user, html: { 'data-controller': :numeral } do |f|
  = f.input :name
  = f.input :annual_income, as: :string, input_html: { 'data-action': 'numeral#format' }
  = f.input :phone_number
  = f.submit class: 'btn btn-primary'

2. 登録するボタンを押した時に数字からカンマを取り除く

このままだと、数字にカンマが入ったままになっているので、数値としてデータベースに保存できません。なので、以下のようにして、登録するボタンを押した時に数字からカンマが取り除かれるようにします。

app/javascript/controllers/numeral_controller.js
import { Controller } from '@hotwired/stimulus'
import numeral from 'numeral'

// Connects to data-controller="numeral"
export default class extends Controller {
  static targets = ['input'];

  format(event) {
    event.target.value = numeral(event.target.value).format();
  }

  submit() {
    this.inputTargets.forEach(input => input.value = numeral(input.value).value());
  }
}
app/views/users/_form.html.haml
= simple_form_for user, html: { 'data-controller': :numeral } do |f|
  = f.input :name
  = f.input :annual_income, as: :string, input_html: { 'data-action': 'numeral#format', 'data-numeral-target': 'input' }
  = f.input :phone_number
  = f.submit class: 'btn btn-primary', 'data-action': 'numeral#submit'

submitメソッドをすると、数字からカンマが取り除かれます。<button>要素に'data-action': 'numeral#submit'属性を持たせて、登録するボタンを押した時にsubmitメソッドが実行されるようにしています。

数字からカンマを取り除く<input>要素は、Stimulusのtargetsを使って参照できるようにしています。<input>要素に'data-numeral-target': 'input'属性を持たせて、stimulus controllerでstatic targets = ['input']を定義することで、<input>要素をstimulus controller内で参照できます。詳しくはリファレンスを参照ください。

submitメソッドではthis.inputTargetsで、'data-numeral-target': 'input'属性を持ったHTML要素の配列を参照しています。この配列の各要素のvalueからカンマを取り除いています。

まとめ

HotwireのStimulusを使って、数字が自動フォーマットされるようにしました。

Sep-18-2023 22-34-39.gif

Stimulusのtargetsを利用して、数字からカンマを取り除きました。targetsは、stimulus controller内でHTML要素を参照したいときに便利です。targetsとなり得るHTML要素は、data-controller属性を持ったHTML要素内に絞れるところも良いところだと思います。

メンテナブルな改善

@aki77 さんから以下の指摘を受けて、numeral_controller を以下のように改善してみました。

  • ブラウザの標準機能でやった方がメンテナブルだと思う
  • numeral は、6年間更新がない
app/javascript/controllers/numeral_controller.js
import { Controller } from '@hotwired/stimulus'

// Connects to data-controller="numeral"
export default class extends Controller {
  static targets = ['input'];

  connect() {
    this.numberFormat = new Intl.NumberFormat();
  }

  inputTargetConnected(element) {
    element.value = this.numberFormat.format(element.value);
  }

  format(event) {
    event.target.value = this.numberFormat.format(event.target.value);
  }

  submit() {
    this.inputTargets.forEach(input => input.value = this._unformat(input.value));
  }

  _unformat(formattedNumber) {
    return formattedNumber.replace(/,/g, '');
  }
}
4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3