前提・実現したいこと
JavaScriptを利用して、Html.TextBoxForに"1,234,567"のように3桁コンマ区切り表示したでデータを、クライアントサイドの検証にひっかからずにForm送信し、データベースのDecimal型のデータを更新できるようにしたいです。submit時にJavaScriptでコンマ無し数値に変更すればよいように思いますが、具体的なソースコードが思い浮かばず困っております。
どうぞよろしくお願いいたします。
環境
Windows10
VS2017(Visual Studio 2017 Community version 15.9.4)
(SQLServer: MSSQLLOCALDB)
Asp.Net MVC で開発
事象詳細
VSからWebアプリを実行し、Chromeにアプリが開きます。
そして、データベースに保存済の情報がブラウザに表示されます。
(データ”Price”はdecimal型でデータベースに保存されています。)
Priceのデータが3桁区切り数値(例:1,234,567)と表示されます。
Form送信するために保存ボタンを押すと、検証にひっかかり、Form送信できません。
3桁コンマ区切り表示を実現するためのJavaScriptは、下記サイトを参考に実装しました。
https://webllica.com/add-comma-as-thousands-separator/
これにより、Html.TextBoxFor で、編集時(Focus時)は"1234567"のようにコンマ無しで入力し、表示時(Blur時)は"1,234,567"のように3桁コンマ区切り表示できるようになったのですが、submitでForm送信するとクライアントサイドの検証により、「値'1,234,567'はPriceに対して無効です」とエラーになりFrom送信できません。
ソースコード
//Model: Part.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Contract.Models
{
public class Part
{
public int ID { get; set; }
[StringLength(50)]
public string Name { get; set; }
[Column("money")]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
}
}
//Model: PartContext.cs
using System.Data.Entity;
namespace Contract.Models
{
public class PartContext : DbContext
{
public PartContext() : base("PartContext")
{
}
public DbSet<Part> Parts { get; set; }
}
}
//App_Start: BundleCOnfig.cs
using System.Web.Optimization;
namespace Contract
{
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.validate*"));
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
"~/Scripts/modernizr-*"));
}
}
}
//Controllers: PartController.cs
using Contract.Models;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web.Mvc;
namespace Contract.Controllers
{
public class PartController : Controller
{
private PartContext db = new PartContext();
// GET: Part
public ActionResult Index()
{
return View(db.Parts.ToList());
}
// GET: Part/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Part part = db.Parts.Find(id);
if (part == null)
{
return HttpNotFound();
}
return View(part);
}
// POST: Part/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "ID,Name,Price")] Part part)
{
if (ModelState.IsValid)
{
db.Entry(part).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(part);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
}
//View:Shared: _Layout.cshtml
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
</head>
<body>
<div class="container body-content">
@RenderBody()
</div>
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@Scripts.Render("~/bundles/jqueryval")
@RenderSection("scripts", required: false)
<script src="~/Scripts/myScript.js?timestamp=@DateTime.Now.Ticks.ToString()"></script>
</body>
</html>
//View: Index.cshtml
@model IEnumerable<Contract.Models.Part>
<table class="table">
<tr>
<th>@Html.DisplayNameFor(model => model.Name)</th>
<th>@Html.DisplayNameFor(model => model.Price)</th>
</tr>
@foreach (var item in Model)
{
<tr>
<td>@Html.DisplayFor(modelItem => item.Name)</td>
<td>@Html.DisplayFor(modelItem => item.Price)</td>
<td>@Html.ActionLink("Edit", "Edit", new { id = item.ID })</td>
</tr>
}
</table>
//View:Edit.cshtml
@model Contract.Models.Part
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
<div class="form-horizontal">
@Html.HiddenFor(model => model.ID)
<div class="form-group">
Name
<div>
@Html.EditorFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name, "")
</div>
Price
<div>
@Html.EditorFor(model => model.Price, "{0:F0}")
@Html.ValidationMessageFor(model => model.Price, "数値にしてください", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div>
<input type="submit" value="保存" formmethod="post"/>
</div>
</div>
</div>
}
//出典: https://webllica.com/add-comma-as-thousands-separator/
//**** 画面表示完了後に、契約金額の表示をコンマ区切りとする ****
window.onload = function () {
elm.value = addFigure(document.getElementById("Price").value);
};
// * 数値の3桁カンマ区切り
// * 入力値をカンマ区切りにして返却
// * [引数] numVal: 入力数値
// * [返却値] String(): カンマ区切りされた文字列
// */
function addFigure(numVal) {
// 空の場合そのまま返却
if (numVal === '') {
return '';
}
// 全角から半角へ変換し、既にカンマが入力されていたら事前に削除
numVal = toHalfWidth(numVal).replace(/,/g, "").trim();
// 数値でなければそのまま返却
if (!/^[+|-]?(\d*)(\.\d+)?$/.test(numVal)) {
return numVal;
}
// 整数部分と小数部分に分割
var numData = numVal.toString().split('.');
// 整数部分を3桁カンマ区切りへ
numData[0] = Number(numData[0]).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
// 小数部分と結合して返却
return numData.join('.');
}
///**
// * カンマ外し
// * 入力値のカンマを取り除いて返却
// * [引数] strVal: 半角でカンマ区切りされた数値
// * [返却値] String(): カンマを削除した数値
// */
function delFigure(strVal) {
return strVal.replace(/,/g, "");
}
///**
// * 全角から半角への変換関数
// * 入力値の英数記号を半角変換して返却
// * [引数] strVal: 入力値
// * [返却値] String(): 半角変換された文字列
// */
function toHalfWidth(strVal) {
// 半角変換
var halfVal = strVal.replace(/[!-~]/g,
function (tmpStr) {
// 文字コードをシフト
return String.fromCharCode(tmpStr.charCodeAt(0) - 0xFEE0);
});
return halfVal;
}
///**
// * 処理を適用するテキストボックスへのイベント設定
// * Blur : カンマ区切り処理実施
// * Focus : カンマ削除処理実施
// */
var elm = document.getElementById('Price');
elm.addEventListener('blur',
function () {
this.value = addFigure(this.value);
}, false);
elm.addEventListener('focus',
function () {
this.value = delFigure(this.value);
}, false);
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+3
わんくま掲示板 http://bbs.wankuma.com/index.cgi?mode=al2&namber=90016 の回答とダブりますし、質問に対する直接の回答ではありませんが、わんくま掲示板の経緯をご存じない回答者・閲覧者の方への情報も含めてレスを書いておきます。
わんくま掲示板では、
ASP.NET MVC アプリで、ユーザーがテキストボックスに、例えば 1000 と入力すると \1,000 に書き換えるスクリプトを実装した。しかし、それを送信しようとすると数字として不正な文字が含まれているので ASP.NET MVC に実装されているクライアント側での検証に引っかかって送信できない。だから、送信する前にテキストボックスの値を \1,000 から 1000 に書き戻したい。
・・・という話でした。
上記の「送信する前にテキストボックスの値を \1,000 から 1000 に書き換えるにはどうすれば良いか」がこのスレッドの質問と理解しています。
問題は、それが実装できたとしても、ASP.NET MVC による検証は依然として回避できない(\1,000 から 1000 に書き換える前に ASP.NET MVC に実装されているクライアントサイドでの検証がかかってしまう)可能性が高いということです。(onchange で検証がかかるはずなので)
質問者さんが諦めない心でとにかく何でもトライしてみるのは良いとは思いますが、そのあたりの事情は回答者・閲覧者の方も知っておいた方がよさそうです。
回避できない場合は、\1,000 1000 から 1000 \1,000 に書き換えのために追加したスクリプトの動作と(1000 と \1,000 が逆でした。訂正します)、ASP.NET MVC の検証用のスクリプトの動作に整合が取れるように、どちらかもしくは両方を書き直して対応できないかを検討するという話になると思います。
でも、書き直すことで対処できるかどうかは分かりません。そもそも、検討するにしても、1,600 行を超える ASP.NET MVC のスクリプトを調べて、好ましからざる副作用が出ないよう完璧に書き直すのは現実的ではないと思われます。時間の無駄に終わるかもしれませんし。
その方向に進むのが難しいまたは現実的ではないと判断されたら、以下のどちらかに進むしかなさそうに思います。
(1) 1000 に書き戻してもクライアント側の検証に引っかかるなら、クライアント側での検証を無効にする。(価格以外の入力もあるでしょうからクライアント側での検証を無効にしてしまうのは問題かもしれませんが)
(2) Model のプロパティを decimal 型ではなく string 型とし、検証はデータアノテーション属性を付与して正規表現で行なう。(DB のフィールドの型が money 型のようですので、サーバー側での取り扱いを考えると難しいかもしれませんが)
自分が考えるとすると (2) かなぁ・・・という感じです。
【追記】
上の質問に対する 2019/02/10 19:37 の私のコメントで「ご参考に検証に使ったコードを回答欄にアップしておきます」書きましたが、それを以下に書いておきます。
先に自分が自分の環境で検証したコードに加えて、入力できる金額の範囲などを検証するデータアノテーション属性を追加し、質問者さんが質問にアップされたスクリプトと、shinji709 さんがアップされたコードの this を elm に変更したスクリプトを追加したものです。
結果、独自に追加したスクリプトと ASP.NET MVC の検証用スクリプトがバッティングすることなくクライアント側での検証は期待通り動きました。(さらなる検証は必要だと思いますが基本的な動きは OK でした ← 下の 2019/02/11 10:15 の私のコメントを見てください)
変な先入観を持たず、諦めない心で何でもやってみるというのは大切なのだと今さらながら思い出し、自分としても勉強になりました。
不明点があれば質問してください。
Model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Globalization;
namespace Mvc5App2.Models
{
public class PurchaseRecord
{
[Display(Name = "ID")]
[Required(ErrorMessage = "{0} は必須")]
[RegularExpression(@"^\d{1,5}$", ErrorMessage = "数字 1 ~ 5 文字")]
public int ID { get; set; }
[Display(Name = "契約日")]
[Required(ErrorMessage = "{0} は必須")]
[RegularExpression(@"^\d{4}/\d{2}/\d{2}( \d{1,2}:\d{2}:\d{2})?$",
ErrorMessage = "yyyy/MM/dd 形式")]
[DisplayFormat(DataFormatString = "{0:yyyy年M月d日}")]
public DateTime ContractDate { get; set; }
[Display(Name = "価格")]
[Required(ErrorMessage = "{0} は必須")]
// RegularExpression がないと 1x というような文字列を入力すると "The field 価格 must be a number."
// という data-val-number 属性に設定されたエラーメッセージが出る。
[RegularExpression(@"^\d{1,6}$", ErrorMessage = "数字 1 ~ 6 文字")]
[Range(100, 10000, ErrorMessage = "{0}は{1}~{2}の間で入力してください。")]
[DisplayFormat(DataFormatString = "{0:N0}", ApplyFormatInEditMode = true)]
public decimal Price { get; set; }
}
}
View
@model Mvc5App2.Models.PurchaseRecord
@{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>PurchaseRecord</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(model => model.ID, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.ID, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.ID, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.ContractDate, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@*@Html.EditorFor(model => model.ContractDate, new { htmlAttributes = new { @class = "form-control" } })*@
@Html.TextBoxFor(model => model.ContractDate, "{0:yyyy/MM/dd}", new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.ContractDate, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Price, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Price, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Price, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
//<![CDATA[
// これは shinji709 さんのコードの this を elm に変更
var form = document.getElementsByTagName('form')[0]
form.addEventListener('submit',
function () {
elm.value = delFigure(elm.value);
}, false);
// ここから下は質問者さんのコードをそのままコピペ
//出典: https://webllica.com/add-comma-as-thousands-separator/
//**** 画面表示完了後に、契約金額の表示をコンマ区切りとする ****
window.onload = function () {
elm.value = addFigure(document.getElementById("Price").value);
};
// * 数値の3桁カンマ区切り
// * 入力値をカンマ区切りにして返却
// * [引数] numVal: 入力数値
// * [返却値] String(): カンマ区切りされた文字列
// */
function addFigure(numVal) {
// 空の場合そのまま返却
if (numVal === '') {
return '';
}
// 全角から半角へ変換し、既にカンマが入力されていたら事前に削除
numVal = toHalfWidth(numVal).replace(/,/g, "").trim();
// 数値でなければそのまま返却
if (!/^[+|-]?(\d*)(\.\d+)?$/.test(numVal)) {
return numVal;
}
// 整数部分と小数部分に分割
var numData = numVal.toString().split('.');
// 整数部分を3桁カンマ区切りへ
numData[0] = Number(numData[0]).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
// 小数部分と結合して返却
return numData.join('.');
}
///**
// * カンマ外し
// * 入力値のカンマを取り除いて返却
// * [引数] strVal: 半角でカンマ区切りされた数値
// * [返却値] String(): カンマを削除した数値
// */
function delFigure(strVal) {
return strVal.replace(/,/g, "");
}
///**
// * 全角から半角への変換関数
// * 入力値の英数記号を半角変換して返却
// * [引数] strVal: 入力値
// * [返却値] String(): 半角変換された文字列
// */
function toHalfWidth(strVal) {
// 半角変換
var halfVal = strVal.replace(/[!-~]/g,
function (tmpStr) {
// 文字コードをシフト
return String.fromCharCode(tmpStr.charCodeAt(0) - 0xFEE0);
});
return halfVal;
}
///**
// * 処理を適用するテキストボックスへのイベント設定
// * Blur : カンマ区切り処理実施
// * Focus : カンマ削除処理実施
// */
var elm = document.getElementById('Price');
elm.addEventListener('blur',
function () {
this.value = addFigure(this.value);
}, false);
elm.addEventListener('focus',
function () {
this.value = delFigure(this.value);
}, false);
//]]>
</script>
}
Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Mvc5App2.Models;
namespace Mvc5App2.Controllers
{
public class HomeController : Controller
{
public ActionResult Edit()
{
PurchaseRecord model = new PurchaseRecord
{
ID = 1,
ContractDate = DateTime.Now,
Price = 1234m
};
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(PurchaseRecord model)
{
if (ModelState.IsValid)
{
// DB の編集処理
return RedirectToAction("Index");
}
return View(model);
}
}
}
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+1
こうですかね?
var form = document.getElementsByTagName('form')[0]
form.addEventListener('submit',
function () {
this.value = delFigure(this.value);
}, false);
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 89.99%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
質問への追記・修正、ベストアンサー選択の依頼
SurferOnWww
2019/02/09 20:28 編集
あなたはわんくま掲示板 http://bbs.wankuma.com/index.cgi?mode=al2&namber=90016 で質問していた人でしょう? であれば、そこでの話の続きであることを書いていただければと思います。で、ここの質問のスクリプトが実装できたとして、ASP.NET MVC に実装されているクライアントサイドでの検証と整合が取れるように、1600 行を超える jQuery のスクリプトを見て必要な処置ができるのですか? 見通しは立ってなくても、少なくとも、この先の課題としてそういうことがあるのも書いていただければと思います。
jiriyama
2019/02/10 04:02
Surfer様 フォローありがとうございます。あれから、ずっとjQueryのスクリプトの挙動をChromeのデバッガで追ったりして糸口を探っていましたが、おっしゃるとおり必要な処置ができる能力がないと判断し、セカンドオピニオンを得たいと思った次第です。恐縮です。ご気分を害しましたら申し訳ありません。
この先の課題の追記等、ありがとうございます。
後ほど、shinji709様の回答欄にも記載致しますが、shinji709様の回答をやってみましたが、やはり検証が先にかかってしまいます。
(デバッグの仕方含め経験不足で手探りで見当違いかもしれませんが)
これにより、気づいたのですが、Chromeのデバッガで、Networkプロセスを見てみると、以下の順にプロセスが実行されている(BundleConfig.csで記載した順)ので、順番を入れ替えたらどうかな?とも考えています。
SurferOnWww
2019/02/10 12:32
わんくまの方はクローズされているので、別のサイトに移動されることには異存ありません。ただ、今までの経緯や ASP.NET MVC のスクリプトとの競合の可能性があることを書いていただいた方が、回答者・閲覧者にとって親切なので、そういう配慮をいただきたいということです。(事情を知らないと、回答者が回答に費やした時間が無駄になるかもしれませんし)
SurferOnWww
2019/02/10 12:46
> 順番を入れ替えたらどうかな?とも考えています。
それはでは解決しません。試したわけではないですが、仕組みを考えると自信度 100% です。
送信前に 1,234,567 から 1234567 に書き戻すために追加したスクリプトの動作と、ASP.NET MVC の検証用のスクリプトの動作に整合が取れるように、どちらかもしくは両方を書き直して対応できないかを検討する。それがダメなら、私のレスの (1) または (2) いずれかしか方法はないと思います。
質問に書いてあったエラーメッセージ「値'1,234,567'はPriceに対して無効です」はサーバー側の検証で出るもののはずですが、ということは (1) はすでにやってみたのでは? 当該 TextBoxFor / EditorFor だけクライアント側での検証を無効にして、データアノテーション属性を付与してサーバー側での検証を十分行なうようにし、クライアント側の検証の無効化の影響が最小限になるよう考えてはいかがですか。
SurferOnWww
2019/02/10 13:17
上に「送信前に 1,234,567 から 1234567 に書き戻すために追加したスクリプトの動作と」と書きましたが違いましたね。その前に「1234567 を 1,234,567 に書き直すスクリプト」を実装したと思いますがそちらの方でした。勘違い、すみません。
jiriyama
2019/02/10 14:05
>質問に書いてあったエラーメッセージ「値'1,234,567'はPriceに対して無効です」はサーバー側の検証で出るもののはずですが、ということは (1) はすでにやってみたのでは?
SurferOnWww様
すみません。不足しておりました。
エラーは、上記(実際は文字化けしていますが)①に加えて、下記で設定した②「数値にしてください」も表示されております。
①!Q65vE!The value '7,658.00' is not valid for Price. 表©鷗字㌍! (@Html.ValidationSummary(false)にて)
②数値にしてください。
(@Html.ValidationMessageFor(model => model.Price, "数値にしてください", new { @class = "text-danger" })にて)
SurferOnWww
2019/02/10 14:41
失礼を承知で言わせていただけると、迷走しているようで、ちょっとフォローしきれません。ValidationSummary(false) はスキャフォールディング機能で自動生成すると true になるはずですし、ValidationMessageFor の第二引数は "" になるはずですが、自分で変更しましたか? そうするとどういう結果なるか分かってやってますか? もし分かってやってないのなら、話が通じなくなりますのでスキャフォールディング機能で生成されたところに戻しませんか?
SurferOnWww
2019/02/10 15:10
スキャフォールディング機能で生成されたところに戻した上で・・・
自分の環境で質問者さんのコードを試してみました。html ソースの input 要素には「data-val="true" data-val-number="The field 価格 must be a number." data-val-required="価格 フィールドが必要です。"」という属性は付与されるものの、クライアント側での検証はかかりませんでした。
質問者さんの ① のメッセージは 7,658.00 が送信されてサーバー側の検証で出たものですが、上記のようなわけで、クライアント側の検証を無効にしなくても送信されてサーバー側で検証されて出たということのようです。(① が文字化けするのは日本語のサテライトアセンブリに問題があって最新版にアップデートすると直ると思いますが、それはちょっと置いときましょう)
そもそもクライアント側での検証はかかってないので、クライアント側のスクリプトでユーザーが入力する際に 1234567 ⇒ 1,234,567 にしようと、送信前に 1,234,567 ⇒ 1234567 に戻そうと、クライアント側での検証とは何の関係もなかったということのようです。
そして、The value '7,658.00' is not valid for Price. というエラーが出たとすると、7,658.00 がそのまま送信されたがサーバーで decimal 型にパースできなくてエラーになったということのようです。
上記が正しいか確認してください。
SurferOnWww
2019/02/10 15:56
【追伸】
decimal の場合は type="text" になって、その場合は data-val-number のクライアント側での検証はかからないようです。data-val-required の方はテキストボックスを空にするとかかります。
int の場合は type="number" となって、動作はブラウザ依存になりますが、カンマ , 交じりの文字が送信されることはないようです。
jiriyama
2019/02/10 16:24
SurferOnWww様
ありがとうございます。お手数をおかけいたします。やってみました。。
スキャフォールディングし直し、下記に戻しました。ついでに文字化けも最新版アップデートしました。
JavaScriptは、shinji709様のご提案のものに戻しました。
実行し、ブラウザ上Priceの入力フォームに「7,658.00」が表示されている状態で、「Save」ボタンを押すと、入力フォーム右側に「値 '7,658.00' は Price に対して無効」と表示され保存できませんでした。
なお、html ソースの
input 要素には「data-val="true" data-val-number="フィールド Price には数字を指定してください。" data-val-required="Price フィールドが必要です。" 」が、
span要素には「data-valmsg-for="Price" data-valmsg-replace="true">値 '7,658.00' は Price に対して無効です。」という属性が付与されています。
これは即ちSurferOnWww様がおっしゃるサーバー側検証でひっかかったということなのですね。※
※サーバ側かクライアント側かはどのように見分けるのでしょうか。
```C#
//Views: Edit.cshtml
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
・・・
@Html.EditorFor(model => model.Price, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Price, "", new { @class = "text-danger" })
・・・
```
```JavaScript
//Scripts :
var form = document.getElementsByTagName('form')[0];
form.addEventListener('submit',
function () {
this.value = delFigure(this.value);
}, false);
//var form = document.getElementsByTagName('form')[0];
//form.addEventListener('submit',
// function () {
// document.getElementById('Price').value = delFigure(document.getElementById('Price').value);
// }, false);
```
SurferOnWww
2019/02/10 16:36
> サーバ側かクライアント側かはどのように見分けるのでしょうか。
POST 側のアクションメソッドの最初の行にでもブレークポイントを設定してデバッグ実行すると、送信されている場合はそのブレークポイントで止まるはずです。
あと、ブラウザから実際どういう文字列が送信されているかは Fiddler などのキャプチャツールで見ると分かります。Chrome のディベロッパーツールでもキャプチャできますが、Fiddler がお勧めです。それなしでは HTTP 通信を行うアプリの開発はできないといっても良いと思ってます。
shinji709 さんが提案されているスクリプトは form にリスナをアタッチしていますが、そうすると this はアタッチした対象すなわち form になります。そこは元に戻さなくて質問者さんのコード(下記)で解決するはずです。
document.getElementById('Price').value = delFigure(document.getElementById('Price').value);
SurferOnWww
2019/02/10 16:40 編集
ただ、その状態で良いのかどうかの話は別だと思います。入力できる金額の範囲の検証の追加とか、スクリプトをもっとスマートに書けないかは要検討ではないでしょうか。
jiriyama
2019/02/10 16:44
おっしゃる通りのことが課題と認識しております。
まずは、shinji709様のほうにも回答を追加しておりますが、複数の金額項目がある場合に同時にコンマ無し送信ができるようにしなければならないところをスマートに書けるとよいのですが。(ForeachのようなものがJavaScriptにあれば・・・)
Fiddler の使い方の参考になる初心者向けサイトがありましたらご紹介いただけますと幸いです。
一度インストールして挫折しました。
SurferOnWww
2019/02/10 16:49
すみません、ちょっと用事ができたので 2 ~ 3 時間お待ちいただければと思います。
jiriyama
2019/02/10 16:52
とんでもありません。お気を付けて。
SurferOnWww
2019/02/10 19:37
質問者さんが質問にアップされたスクリプトと、shinji709 さんがアップされたコードの this を elm に変更したスクリプトを使って、さらに先に自分の環境で検証に使ったコードに入力できる金額の範囲などを検証するデータアノテーション属性を追加して検証してみました。
結果、独自に追加したスクリプトと ASP.NET MVC の検証用スクリプトがバッティングすることなくクライアント側での検証は期待通り動きました。(さらなる検証は必要だと思いますが基本的な動きは OK でした)
やはり、先入観を持たず、諦めない心で試してみるのが大切ですね。勉強になりました。ご参考に検証に使ったコードを回答欄にアップしておきます。