質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Entity Framework

Entity Frameworkは、.NET Framework 3.5より追加されたデータアクセス技術。正式名称は「ADO.NET Entity Framework」です。データベースエンジンに依存しておらず、データプロバイダの変更のみで様々なデータベースに対応できます。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

ASP.NET MVC Framework

ASP.NET MVC Frameworkは、MVCパターンをベースとした、マイクロソフトのウェブアプリケーション開発用のフレームワークです。

Q&A

解決済

2回答

4011閲覧

3桁コンマ区切り数字をコンマ無しでFrom送信したい

jiriyama

総合スコア13

Entity Framework

Entity Frameworkは、.NET Framework 3.5より追加されたデータアクセス技術。正式名称は「ADO.NET Entity Framework」です。データベースエンジンに依存しておらず、データプロバイダの変更のみで様々なデータベースに対応できます。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

ASP.NET MVC Framework

ASP.NET MVC Frameworkは、MVCパターンをベースとした、マイクロソフトのウェブアプリケーション開発用のフレームワークです。

0グッド

0クリップ

投稿2019/02/09 09:38

編集2019/02/09 21:11

前提・実現したいこと

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送信できません。

ソースコード

C#

1//Model: Part.cs 2using System.ComponentModel.DataAnnotations; 3using System.ComponentModel.DataAnnotations.Schema; 4 5namespace Contract.Models 6{ 7 public class Part 8 { 9 public int ID { get; set; } 10 11 [StringLength(50)] 12 public string Name { get; set; } 13 14 [Column("money")] 15 [DataType(DataType.Currency)] 16 public decimal Price { get; set; } 17 } 18}

C#

1//Model: PartContext.cs 2using System.Data.Entity; 3 4namespace Contract.Models 5{ 6 public class PartContext : DbContext 7 { 8 public PartContext() : base("PartContext") 9 { 10 } 11 public DbSet<Part> Parts { get; set; } 12 } 13}

C#

1//App_Start: BundleCOnfig.cs 2using System.Web.Optimization; 3 4namespace Contract 5{ 6 public class BundleConfig 7 { 8 public static void RegisterBundles(BundleCollection bundles) 9 { 10 bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 11 "~/Scripts/jquery-{version}.js")); 12 13 bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( 14 "~/Scripts/jquery.validate*")); 15 16 bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( 17 "~/Scripts/modernizr-*")); 18 } 19 } 20}

C#

1//Controllers: PartController.cs 2using Contract.Models; 3using System.Data.Entity; 4using System.Linq; 5using System.Net; 6using System.Web.Mvc; 7 8namespace Contract.Controllers 9{ 10 public class PartController : Controller 11 { 12 private PartContext db = new PartContext(); 13 14 // GET: Part 15 public ActionResult Index() 16 { 17 return View(db.Parts.ToList()); 18 } 19 20 21 // GET: Part/Edit/5 22 public ActionResult Edit(int? id) 23 { 24 if (id == null) 25 { 26 return new HttpStatusCodeResult(HttpStatusCode.BadRequest); 27 } 28 Part part = db.Parts.Find(id); 29 if (part == null) 30 { 31 return HttpNotFound(); 32 } 33 return View(part); 34 } 35 36 // POST: Part/Edit/5 37 [HttpPost] 38 [ValidateAntiForgeryToken] 39 public ActionResult Edit([Bind(Include = "ID,Name,Price")] Part part) 40 { 41 if (ModelState.IsValid) 42 { 43 db.Entry(part).State = EntityState.Modified; 44 db.SaveChanges(); 45 return RedirectToAction("Index"); 46 } 47 return View(part); 48 } 49 50 protected override void Dispose(bool disposing) 51 { 52 if (disposing) 53 { 54 db.Dispose(); 55 } 56 base.Dispose(disposing); 57 } 58 } 59}

C#

1//View:Shared: _Layout.cshtml 2<!DOCTYPE html> 3<html> 4<head> 5 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 6 <meta charset="utf-8" /> 7 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 8 @Styles.Render("~/Content/css") 9 @Scripts.Render("~/bundles/modernizr") 10</head> 11<body> 12 <div class="container body-content"> 13 @RenderBody() 14 </div> 15 16 @Scripts.Render("~/bundles/jquery") 17 @Scripts.Render("~/bundles/bootstrap") 18 @Scripts.Render("~/bundles/jqueryval") 19 @RenderSection("scripts", required: false) 20 <script src="~/Scripts/myScript.js?timestamp=@DateTime.Now.Ticks.ToString()"></script> 21</body> 22</html>

C#

1//View: Index.cshtml 2@model IEnumerable<Contract.Models.Part> 3<table class="table"> 4 <tr> 5 <th>@Html.DisplayNameFor(model => model.Name)</th> 6 <th>@Html.DisplayNameFor(model => model.Price)</th> 7 </tr> 8 9 @foreach (var item in Model) 10 { 11 <tr> 12 <td>@Html.DisplayFor(modelItem => item.Name)</td> 13 <td>@Html.DisplayFor(modelItem => item.Price)</td> 14 <td>@Html.ActionLink("Edit", "Edit", new { id = item.ID })</td> 15 </tr> 16 } 17</table>

C#

1//View:Edit.cshtml 2@model Contract.Models.Part 3@using (Html.BeginForm()) 4{ 5 @Html.AntiForgeryToken() 6 @Html.ValidationSummary(true) 7 8 <div class="form-horizontal"> 9 @Html.HiddenFor(model => model.ID) 10 11 <div class="form-group"> 12 Name 13 <div> 14 @Html.EditorFor(model => model.Name) 15 @Html.ValidationMessageFor(model => model.Name, "") 16 </div> 17 Price 18 <div> 19 @Html.EditorFor(model => model.Price, "{0:F0}") 20 @Html.ValidationMessageFor(model => model.Price, "数値にしてください", new { @class = "text-danger" }) 21 </div> 22 </div> 23 24 <div class="form-group"> 25 <div> 26 <input type="submit" value="保存" formmethod="post"/> 27 </div> 28 </div> 29 </div> 30}

JavaScript

1//出典: https://webllica.com/add-comma-as-thousands-separator/ 2//**** 画面表示完了後に、契約金額の表示をコンマ区切りとする **** 3window.onload = function () { 4 elm.value = addFigure(document.getElementById("Price").value); 5}; 6 7// * 数値の3桁カンマ区切り 8// * 入力値をカンマ区切りにして返却 9// * [引数] numVal: 入力数値 10// * [返却値] String(): カンマ区切りされた文字列 11// */ 12function addFigure(numVal) { 13 // 空の場合そのまま返却 14 if (numVal === '') { 15 return ''; 16 } 17 // 全角から半角へ変換し、既にカンマが入力されていたら事前に削除 18 numVal = toHalfWidth(numVal).replace(/,/g, "").trim(); 19 // 数値でなければそのまま返却 20 if (!/^[+|-]?(\d*)(.\d+)?$/.test(numVal)) { 21 return numVal; 22 } 23 // 整数部分と小数部分に分割 24 var numData = numVal.toString().split('.'); 25 // 整数部分を3桁カンマ区切りへ 26 numData[0] = Number(numData[0]).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); 27 // 小数部分と結合して返却 28 return numData.join('.'); 29} 30 31///** 32// * カンマ外し 33// * 入力値のカンマを取り除いて返却 34// * [引数] strVal: 半角でカンマ区切りされた数値 35// * [返却値] String(): カンマを削除した数値 36// */ 37function delFigure(strVal) { 38 return strVal.replace(/,/g, ""); 39} 40 41///** 42// * 全角から半角への変換関数 43// * 入力値の英数記号を半角変換して返却 44// * [引数] strVal: 入力値 45// * [返却値] String(): 半角変換された文字列 46// */ 47function toHalfWidth(strVal) { 48 // 半角変換 49 var halfVal = strVal.replace(/[!-~]/g, 50 function (tmpStr) { 51 // 文字コードをシフト 52 return String.fromCharCode(tmpStr.charCodeAt(0) - 0xFEE0); 53 }); 54 return halfVal; 55} 56 57///** 58// * 処理を適用するテキストボックスへのイベント設定 59// * Blur : カンマ区切り処理実施 60// * Focus : カンマ削除処理実施 61// */ 62var elm = document.getElementById('Price'); 63 64elm.addEventListener('blur', 65 function () { 66 this.value = addFigure(this.value); 67 }, false); 68 69elm.addEventListener('focus', 70 function () { 71 this.value = delFigure(this.value); 72 }, false);

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/02/09 12:39 編集

あなたはわんくま掲示板 http://bbs.wankuma.com/index.cgi?mode=al2&namber=90016 で質問していた人でしょう? であれば、そこでの話の続きであることを書いていただければと思います。で、ここの質問のスクリプトが実装できたとして、ASP.NET MVC に実装されているクライアントサイドでの検証と整合が取れるように、1600 行を超える jQuery のスクリプトを見て必要な処置ができるのですか? 見通しは立ってなくても、少なくとも、この先の課題としてそういうことがあるのも書いていただければと思います。
jiriyama

2019/02/09 19:02

Surfer様 フォローありがとうございます。あれから、ずっとjQueryのスクリプトの挙動をChromeのデバッガで追ったりして糸口を探っていましたが、おっしゃるとおり必要な処置ができる能力がないと判断し、セカンドオピニオンを得たいと思った次第です。恐縮です。ご気分を害しましたら申し訳ありません。 この先の課題の追記等、ありがとうございます。 後ほど、shinji709様の回答欄にも記載致しますが、shinji709様の回答をやってみましたが、やはり検証が先にかかってしまいます。 (デバッグの仕方含め経験不足で手探りで見当違いかもしれませんが) これにより、気づいたのですが、Chromeのデバッガで、Networkプロセスを見てみると、以下の順にプロセスが実行されている(BundleConfig.csで記載した順)ので、順番を入れ替えたらどうかな?とも考えています。
退会済みユーザー

退会済みユーザー

2019/02/10 03:32

わんくまの方はクローズされているので、別のサイトに移動されることには異存ありません。ただ、今までの経緯や ASP.NET MVC のスクリプトとの競合の可能性があることを書いていただいた方が、回答者・閲覧者にとって親切なので、そういう配慮をいただきたいということです。(事情を知らないと、回答者が回答に費やした時間が無駄になるかもしれませんし)
退会済みユーザー

退会済みユーザー

2019/02/10 03:46

> 順番を入れ替えたらどうかな?とも考えています。 それはでは解決しません。試したわけではないですが、仕組みを考えると自信度 100% です。 送信前に 1,234,567 から 1234567 に書き戻すために追加したスクリプトの動作と、ASP.NET MVC の検証用のスクリプトの動作に整合が取れるように、どちらかもしくは両方を書き直して対応できないかを検討する。それがダメなら、私のレスの (1) または (2) いずれかしか方法はないと思います。 質問に書いてあったエラーメッセージ「値'1,234,567'はPriceに対して無効です」はサーバー側の検証で出るもののはずですが、ということは (1) はすでにやってみたのでは? 当該 TextBoxFor / EditorFor だけクライアント側での検証を無効にして、データアノテーション属性を付与してサーバー側での検証を十分行なうようにし、クライアント側の検証の無効化の影響が最小限になるよう考えてはいかがですか。
退会済みユーザー

退会済みユーザー

2019/02/10 04:17

上に「送信前に 1,234,567 から 1234567 に書き戻すために追加したスクリプトの動作と」と書きましたが違いましたね。その前に「1234567 を 1,234,567 に書き直すスクリプト」を実装したと思いますがそちらの方でした。勘違い、すみません。
jiriyama

2019/02/10 05: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" })にて)
退会済みユーザー

退会済みユーザー

2019/02/10 05:41

失礼を承知で言わせていただけると、迷走しているようで、ちょっとフォローしきれません。ValidationSummary(false) はスキャフォールディング機能で自動生成すると true になるはずですし、ValidationMessageFor の第二引数は "" になるはずですが、自分で変更しましたか? そうするとどういう結果なるか分かってやってますか? もし分かってやってないのなら、話が通じなくなりますのでスキャフォールディング機能で生成されたところに戻しませんか?
退会済みユーザー

退会済みユーザー

2019/02/10 06: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 型にパースできなくてエラーになったということのようです。 上記が正しいか確認してください。
退会済みユーザー

退会済みユーザー

2019/02/10 06:56

【追伸】 decimal の場合は type="text" になって、その場合は data-val-number のクライアント側での検証はかからないようです。data-val-required の方はテキストボックスを空にするとかかります。 int の場合は type="number" となって、動作はブラウザ依存になりますが、カンマ , 交じりの文字が送信されることはないようです。
jiriyama

2019/02/10 07: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">値 &#39;7,658.00&#39; は 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); ```
退会済みユーザー

退会済みユーザー

2019/02/10 07:36

> サーバ側かクライアント側かはどのように見分けるのでしょうか。 POST 側のアクションメソッドの最初の行にでもブレークポイントを設定してデバッグ実行すると、送信されている場合はそのブレークポイントで止まるはずです。 あと、ブラウザから実際どういう文字列が送信されているかは Fiddler などのキャプチャツールで見ると分かります。Chrome のディベロッパーツールでもキャプチャできますが、Fiddler がお勧めです。それなしでは HTTP 通信を行うアプリの開発はできないといっても良いと思ってます。 shinji709 さんが提案されているスクリプトは form にリスナをアタッチしていますが、そうすると this はアタッチした対象すなわち form になります。そこは元に戻さなくて質問者さんのコード(下記)で解決するはずです。 document.getElementById('Price').value = delFigure(document.getElementById('Price').value);
退会済みユーザー

退会済みユーザー

2019/02/10 07:42 編集

ただ、その状態で良いのかどうかの話は別だと思います。入力できる金額の範囲の検証の追加とか、スクリプトをもっとスマートに書けないかは要検討ではないでしょうか。
jiriyama

2019/02/10 07:44

おっしゃる通りのことが課題と認識しております。 まずは、shinji709様のほうにも回答を追加しておりますが、複数の金額項目がある場合に同時にコンマ無し送信ができるようにしなければならないところをスマートに書けるとよいのですが。(ForeachのようなものがJavaScriptにあれば・・・) Fiddler の使い方の参考になる初心者向けサイトがありましたらご紹介いただけますと幸いです。 一度インストールして挫折しました。
退会済みユーザー

退会済みユーザー

2019/02/10 07:49

すみません、ちょっと用事ができたので 2 ~ 3 時間お待ちいただければと思います。
jiriyama

2019/02/10 07:52

とんでもありません。お気を付けて。
退会済みユーザー

退会済みユーザー

2019/02/10 10:37

質問者さんが質問にアップされたスクリプトと、shinji709 さんがアップされたコードの this を elm に変更したスクリプトを使って、さらに先に自分の環境で検証に使ったコードに入力できる金額の範囲などを検証するデータアノテーション属性を追加して検証してみました。 結果、独自に追加したスクリプトと ASP.NET MVC の検証用スクリプトがバッティングすることなくクライアント側での検証は期待通り動きました。(さらなる検証は必要だと思いますが基本的な動きは OK でした) やはり、先入観を持たず、諦めない心で試してみるのが大切ですね。勉強になりました。ご参考に検証に使ったコードを回答欄にアップしておきます。
guest

回答2

0

ベストアンサー

わんくま掲示板 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); } } }

投稿2019/02/09 14:06

編集2019/02/11 01:19
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jiriyama

2019/02/10 10:51

SurferOnWwww様 初心者の私に丁寧に粘り強くご対応頂きましたこと、誠にありがとうございました。 解決とさせていただきます。
jiriyama

2019/02/10 11:16

コードも確認できました。ありがとうございました。
退会済みユーザー

退会済みユーザー

2019/02/11 01:16 編集

かなり微妙なとこるで動いていますので、上にも書きましたがさらなる検証は必要だと思います。 微妙なというのはどういうことかと言うと、上のコードの例では: (1) テキストボックスの初期表示は 1,234 ⇒ (2) ユーザーが編集動作に入る時 focus イベントが発生しスクリプトで 1234 に書き換わる ⇒ (3) ユーザーが例えば 3210 というように編集 ⇒ (4) 次の作業に移るためフォーカスを外す ⇒ (5) change イベントが発生し検証がかかる ⇒ (6) blur イベントが発生しスクリプトで 3,210 に書き換える ⇒ (7) ユーザーが送信ボタンをクリック ⇒ (3) submit イベントが発生しスクリプトで 3.210 を 3210 に書き換える ⇒ (8) サーバーで 3210 を受信、サーバー側での検証 OK となる。 ・・・という動きになっていて、ブラウザ依存のスクリプトに頼っている部分が多いということです。 問題として思いつくのは: ■ (5) ⇒ (6) の順序でイベントが発生しなければならないが、全てのブラウザでそうかは不明。(今のメジャーなブラウザは大丈夫のようですが、昔の Forefox は反対だったという話があります) ■ (3) でユーザーが数字だけ入力してくれると期待するのは無理がある。(上のサンプルでは RegularExpression 属性を追加してチェックするようにしてますが、それで十分か?) ■ ユーザーがブラウザの JavaScript を無効にした場合はサーバー側だけで検証することになる。 ・・・などです。 なので、実際に運用に使う場合は事前に十分確認したいただくようお願いします。不特定多数のユーザーが不特定多種のブラウザでアクセスしてくるインターネットに公開してような場合は特に。
jiriyama

2019/02/11 01:23

SurferOnWww様 ありがとうございます。ご懸念の点、理解できました。 ド素人への厚いサポート深謝致します。 今後とも精進に努めますので、宜しくお願い致します。
guest

0

こうですかね?

JavaScript

1var form = document.getElementsByTagName('form')[0] 2form.addEventListener('submit', 3 function () { 4 this.value = delFigure(this.value); 5 }, false);

投稿2019/02/09 10:36

shinji709

総合スコア805

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jiriyama

2019/02/09 20:44 編集

shinji709様 ありがとうございます。 下記の通りやってみましたが、変わらず検証にかかってしまいます。 追加のコメントがございましたらアドバイスよろしくお願いいたします。 SurfOnWww様のコメントにございますように、ASP.NET MVCのデフォルトでの検証が先にかかるのを回避できない状況が続いているようです。> (>onchange で検証がかかる ということなのでしょうね・・・) SurferOnWww様の案で、検証はやはり欲しいので(1)は避けたく、金額なのでDecimalにしたいですし(Stringなら精度関係ないか?)、入力桁を間違えないように3桁区切りは欲しいですし・・・悩みます ### 実施したこと myScript.js の最後にご提案のコードを追記。 _Layout.cshtml に一番先にmyScript.jsを追記(一番先に実行されると考えた) 実行した結果、やはりプロセス順が(ChromeデバッガのNetworkで確認したところ) HTML→modernizr.js→jquery.js→jquery.validate.jp→jquery.validate.unobtrusive.js →myScript.js →SupportLanguageList.json となり、検証に先にかかっているように見えます。 >何度か実行し直したところ、先にmyScript.jsが実行開始されているケースも確認できましたが、やはり検証が回避できていません。。。開始が早くても完了は最後までかかっている・・・ ```JavaScript //Scripts:myScript.js ・・・ var form = document.getElementsByTagName('form')[0] form.addEventListener('submit', function () { this.value = delFigure(this.value); }, false); ``` ```C# //View:Shared: _Layout.cshtml <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"> @Scripts.Render("~/bundles/modernizr") </head> <body> <div class="container body-content"> @RenderBody() </div> <script src="~/Scripts/myScript.js?timestamp=@DateTime.Now.Ticks.ToString()"></script> @Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/jqueryval") @RenderSection("scripts", required: false) </body> </html> ```
jiriyama

2019/02/10 04:59

jinji709様 SurfOnWwww様 お世話になっております。 なんと、ご提案いただいたJavaScriptを下記のようにしたら、検証にかからずにForm送信できるようになりました。 ただ、汎用性がなくなってしまいました。 汎用性をもたせるために、thisのままで対応できるようになりませんでしょうか? お忙しいところ恐縮ですが、引き続きアドバイスを頂けないでしょうか? ```JavaScript //Scripts:myScript.js(変更前) ・・・ var form = document.getElementsByTagName('form')[0] form.addEventListener('submit', function () { this.value = delFigure(this.value); }, false); ``` ```JavaScript //Scripts:myScript.js(変更後) ・・・ var form = document.getElementsByTagName('form')[0]; form.addEventListener('submit', function () { document.getElementById('Price').value = delFigure(document.getElementById('Price').value); }, false); ```
退会済みユーザー

退会済みユーザー

2019/02/10 06:16

上の質問の 2019/02/10 15:10 の私のコメントに書きましたが、質問者さんのコードでは、そもそもクライアント側での検証は行われていなかったようです。なので、たぶん、「変更前」も「変更後」もサーバーへの送信はできていたが、「変更前」では当該文字列のカンマ , が除去できていなかったという話ではないかと思われます。確認してください。
shinji709

2019/02/10 06:28

これは失礼しました。formのvalueに代入してどうすんだ… 出先で確認できませんがthis.querySelector('#Price').value使えませんか?
jiriyama

2019/02/10 07:39 編集

shinji709様 ありがとうございます。 実際のアプリではひとつの画面で複数の価格情報があるので、汎用的なもので複数同時にコンマなし処理できないかと考える次第です。 クラス名だとよさそうですね。やってみます。
shinji709

2019/02/10 09:14

じゃあjQueryなら $(this).find('.hoge').each(function() { $(this).val($(this).val()); //またはthis.value = delFigure(this.value); }); ES2015以降なら [...this.querySelectorAll('.hoge')].forEach(el => { el.value = delFigure(el.value); }); とかですかねぇ。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問