无需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
相关产品推荐
相关产品推荐

