You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需JavaScript在f.text_field中动态显示格式化金额值

纯HTML/Ruby实现方案

不需要JavaScript的话,最优雅的方式是给模型加虚拟属性,实现数据库分数值和表单输入元数值的自动转换:

步骤1:在模型中添加虚拟属性

# app/models/item.rb
# 把数据库中的分转换为元,用于表单显示
def price_in_yuan
  price.to_f / 100 if price.present?
end

# 把表单输入的元转换为分,存入数据库
def price_in_yuan=(value)
  self.price = (value.to_f * 100).to_i if value.present?
end

步骤2:修改表单使用虚拟属性

把原来绑定price的输入框改成绑定虚拟属性price_in_yuan,同时设置step: '0.01'限制输入小数位数:

<%= form_with model: @item, local: true do |f| %>
...
  <div class="flex flex-col mb-3">
    <%= f.label :price_in_yuan, 'Price' %>
    <%= f.text_field :price_in_yuan, type: 'number', step: '0.01' %>
  </div>
...
<% end %>

用户输入19.99时,提交后会自动转成1999分存入数据库;编辑时,数据库里的1999分会自动转成19.99显示在输入框里,全程不需要JS。

如果不想修改模型,也可以用隐藏字段+控制器处理的方式:

<%= form_with model: @item, local: true do |f| %>
...
  <div class="flex flex-col mb-3">
    <%= f.label :price, 'Price' %>
    <%= f.text_field :display_price, type: 'number', step: '0.01', value: @item.price ? (@item.price.to_f / 100) : '' %>
    <%= f.hidden_field :price %>
  </div>
...
<% end %>

然后在控制器的强参数里做转换:

def item_params
  if params[:item][:display_price].present?
    params[:item][:price] = (params[:item][:display_price].to_f * 100).to_i
  end
  params.require(:item).permit(:price, ...) # 无需permit display_price
end

Stimulus 简易JS解决方案

如果需要实时格式化(比如自动加千分位),可以用Stimulus写个轻量控制器,实现输入值和数据库分数值的同步:

基础同步版(仅转换数值)

1. 创建Stimulus控制器

// app/javascript/controllers/price_formatter_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  // 定义目标元素:显示用的输入框和存储分的隐藏字段
  static targets = ["display", "hidden"]

  // 页面加载时初始化显示值
  connect() {
    if (this.hiddenTarget.value) {
      this.displayTarget.value = (parseInt(this.hiddenTarget.value) / 100).toFixed(2)
    }
  }

  // 输入时同步到隐藏字段
  sync() {
    const inputValue = parseFloat(this.displayTarget.value)
    if (!isNaN(inputValue)) {
      this.hiddenTarget.value = Math.round(inputValue * 100).toString()
    } else {
      this.hiddenTarget.value = ""
    }
  }
}

2. 修改表单绑定控制器

<%= form_with model: @item, local: true do |f| %>
...
  <div class="flex flex-col mb-3" data-controller="price-formatter">
    <%= f.label :price, 'Price' %>
    <%= f.text_field :display_price, 
        type: 'number', 
        step: '0.01', 
        data: { 
          target: "price-formatter.display", 
          action: "input->price-formatter#sync" 
        } %>
    <%= f.hidden_field :price, data: { target: "price-formatter.hidden" } %>
  </div>
...
<% end %>

增强格式化版(带千分位显示)

如果需要让输入的金额更易读(比如1234.56显示为1,234.56),可以修改控制器的sync方法:

sync() {
  // 过滤掉非数字和小数点的字符
  const rawValue = this.displayTarget.value.replace(/[^0-9.]/g, '')
  const inputValue = parseFloat(rawValue)
  
  if (!isNaN(inputValue)) {
    // 格式化显示为带千分位的金额
    this.displayTarget.value = inputValue.toLocaleString('zh-CN', { 
      minimumFractionDigits: 2, 
      maximumFractionDigits: 2 
    })
    // 同步为分存入隐藏字段
    this.hiddenTarget.value = Math.round(inputValue * 100).toString()
  } else {
    this.displayTarget.value = ''
    this.hiddenTarget.value = ''
  }
}

用户输入时会自动格式化显示,同时保证提交到数据库的是正确的分数值。


内容的提问来源于stack exchange,提问作者Marius Kressin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 14:35:30