- 相変わらずまじめに開発入っちゃってるからこれまた相変わらずネタがない。
- jQueryが楽しいせいで本体が疎かに…。デザインは楽しいけど終わりないのが問題。やっぱりこーゆーのは分業したい。そして自分はデザイン側に…(飽きたら逆のことを言い出す気もするけど)。
- でもプラグインはどこまで使うは悩む。頼りきると将来的に痛い目を見る気がする。かといってあるものをいちいち書くのか?って気もしてる。
- とりあえず逆説的だけどシンプルなものは取り入れて、複雑なものは敬遠、どうしても必要なら書く方向で…。
2012/10/26
【雑記】本日の箇条書き
loading..
|
loading..
|
2012/10/18
【ASP.Net】部分ビューの更新【MVC】
久しぶりの更新です。
唐突ですが、カテゴリーの配下にサブカテゴリーがある場合、入力時なんかはカテゴリーが選択された後はサブカテゴリーはそのカテゴリーの配下のものに絞られた方が便利ですよね。Winフォームとかだったらカテゴリー決定後にイベント発生させてってできるわけなんですがWebになるとどうすりゃいいいのって話になるわけです。
というわけでとりあえずモデルを提示しときます。
カテゴリーモデル
サブカテゴリーモデル
商品モデル
イメージとしては商品入力時、カテゴリードロップダウンリストを決定したら、サブカテゴリードロップダウンリストの部分のみを更新って感じでしょうか。1部分のみの更新といえば部分ビューの出番ですね。
さっさと結果から言っちゃうとできたことはできたんですが、カテゴリーをAJAXヘルパーを使うため商品モデルの拡張とビュー側でドロップダウンリストが使えないという問題が…。以下、サンプルコードです。
商品モデル拡張
コントローラー(一部抜粋)
Createビュー(フォーム部分のみ)
SubCategoryDropDownListビュー
何とも分かりにくいサンプルになってしまいましたが、ポイントはCreateビューの「@AJAX.ActionLink()」メソッドと「@Html.Partial()」メソッドです。初回呼出時にはPartialメソッドによって部分ビュー(SubCategoryDropDownList)が呼び出され、カテゴリーのリンククリック時にはActionLinkメソッドで部分ビューが更新されます。ActionLinkメソッドでは動的なパラメータは使えませんと怒られたのでカテゴリーをわざわざViewBagではなくてモデルに配置しています。カテゴリーはとりあえず<li>要素で表してますが、jQueryのプラグインでドロップダウンに見せることもできるでしょう(適当)。
実行結果
以上、久しぶりの更新なのにかなり駆け足でしたが、今日はここまで。…正直、ここまで試行錯誤しすぎて疲れました。
唐突ですが、カテゴリーの配下にサブカテゴリーがある場合、入力時なんかはカテゴリーが選択された後はサブカテゴリーはそのカテゴリーの配下のものに絞られた方が便利ですよね。Winフォームとかだったらカテゴリー決定後にイベント発生させてってできるわけなんですがWebになるとどうすりゃいいいのって話になるわけです。
というわけでとりあえずモデルを提示しときます。
カテゴリーモデル
public class Category { public int Id { get; set; } public string Name { get; set; } public virtual List<SubCategory> SubCategories { get; set; } }
サブカテゴリーモデル
public class SubCategory { public int Id { get; set; } public int CategoryId { get; set; } public string Name { get; set; } public virtual Category Category { get; set; } public virtual List<Article> Articles { get; set; } }
商品モデル
public class Article { public int Id { get; set; } public string Name { get; set; } public int SubCategoryId { get; set; } public virtual SubCategory SubCategory { get; set; } }
イメージとしては商品入力時、カテゴリードロップダウンリストを決定したら、サブカテゴリードロップダウンリストの部分のみを更新って感じでしょうか。1部分のみの更新といえば部分ビューの出番ですね。
さっさと結果から言っちゃうとできたことはできたんですが、カテゴリーをAJAXヘルパーを使うため商品モデルの拡張とビュー側でドロップダウンリストが使えないという問題が…。以下、サンプルコードです。
商品モデル拡張
public class ArticleEdit { public Article Article { get; set; } public List<Category> Categories { get; set; } }
コントローラー(一部抜粋)
public ActionResult Create() { var model = new ArticleEdit { Categories = db.Categories.ToList() }; ViewBag.SubCategoryId = new SelectList(db.SubCategories, "Id", "Name"); return View(model); } [HttpPost] public ActionResult Create(ArticleEdit model) { if (ModelState.IsValid) { db.Articles.Add(model.Article); db.SaveChanges(); return RedirectToAction("Index"); } ViewBag.SubCategoryId = new SelectList(db.SubCategories, "Id", "Name", model.Article.SubCategoryId); return View(model); } //これが部分ビューのアクション public ActionResult SubCategoryDropDownList(int categoryId = 1) { ViewBag.SubCategoryId = new SelectList(db.SubCategories.Where(p => p.CategoryId == categoryId), "Id", "Name"); return View(); }
Createビュー(フォーム部分のみ)
@using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>Article</legend> <div class="editor-label"> @Html.LabelFor(model => model.Article.Name) </div> <div class="editor-field"> @Html.EditorFor(model => model.Article.Name) @Html.ValidationMessageFor(model => model.Article.Name) </div> <div class="editor-label"> カテゴリー </div> <div class="editor-field"> <ul> @foreach (var category in Model.Categories) { <li> @Ajax.ActionLink(category.Name, "SubCategoryDropDownList", new { categoryId = category.Id}, new AjaxOptions{UpdateTargetId="subcategoryarea"}) </li> } </ul> </div> <div class="editor-label"> サブカテゴリー </div> <div id="subcategoryarea" class="editor-field"> @Html.Partial("SubCategoryDropDownList") @Html.ValidationMessageFor(model=>model.Article.SubCategoryId) </div> <p> <input type="submit" value="Create" /> </p> </fieldset> }
SubCategoryDropDownListビュー
<div class="editor-field"> @Html.DropDownList("Article.SubCategoryId", ViewBag.SubCategoryId as SelectList) </div>
何とも分かりにくいサンプルになってしまいましたが、ポイントはCreateビューの「@AJAX.ActionLink()」メソッドと「@Html.Partial()」メソッドです。初回呼出時にはPartialメソッドによって部分ビュー(SubCategoryDropDownList)が呼び出され、カテゴリーのリンククリック時にはActionLinkメソッドで部分ビューが更新されます。ActionLinkメソッドでは動的なパラメータは使えませんと怒られたのでカテゴリーをわざわざViewBagではなくてモデルに配置しています。カテゴリーはとりあえず<li>要素で表してますが、jQueryのプラグインでドロップダウンに見せることもできるでしょう(適当)。
実行結果
以上、久しぶりの更新なのにかなり駆け足でしたが、今日はここまで。…正直、ここまで試行錯誤しすぎて疲れました。
loading..
|
loading..
|
2012/10/12
【ASP.Net】PartialViewをjQueryでお手軽モーダル表示…に失敗【MVC】
なんだかあと一歩な感じなんでメモっとく。
何がしたいかというとリストから新規作成を選択したらページ遷移せずにモーダルウィンドウで表示してみたいんです。で、色々考えてみた結果、PartialViewとjQueryのjqModalプラグインを使えばできるんじゃねーかという結論にいたったので実験したわけです。結果的に失敗しているので今回もちゃっちゃっとサンプルを載せます。
jqModalプラグインについてはこちら→jqModal :: Minimalistic Modaling for jQuery
モデル等については例によって使い回しです。
Userモデル
検証確認のため、適当な検証をくっつけてます。
Userコントローラー
これまたいい加減ですが、Create実行時に目的であるPartialViewで表示するように設定してます。
Indexビュー
今回の肝であるjqModalプラグインを設定しています。流れとしては「リンククリックイベント(createShow())→ダイアログの定義→ダイアログの表示」って感じです。
Createビュー
こっちは何の変哲もないビューです。使ってみて初めて気がつきましたが、どうやら部分ビューは部分ビューでjquery等を定義してないと動かない模様です。最初これ飛ばしててクライアント検証が発生しなくてかなり困りました。
実行結果(Index表示時)
実行結果(Createクリック時)
おぉー、出来た出来た!…と思いきや。
実行結果(サーバーサイド検証時)
ちょっと分かりづらいんですがサーバーサイドで検証に引っかかってもう一度PartialView()が実行されるとモーダル表示が解けるどころかページ遷移が発生しちゃってるんですよ。アドレスも思いっきり「User/Create」って出ちゃってます。
…そして今に至るわけです。ちょっと手の込んだことしないと解決できなそうな気がする。何とかできたら追記するかも?
何がしたいかというとリストから新規作成を選択したらページ遷移せずにモーダルウィンドウで表示してみたいんです。で、色々考えてみた結果、PartialViewとjQueryのjqModalプラグインを使えばできるんじゃねーかという結論にいたったので実験したわけです。結果的に失敗しているので今回もちゃっちゃっとサンプルを載せます。
jqModalプラグインについてはこちら→jqModal :: Minimalistic Modaling for jQuery
モデル等については例によって使い回しです。
Userモデル
public class User:IValidatableObject { [Required] public int Id { get; set; } [Required] public string Name { get; set; } public int CategoryId { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { if(10 < this.Name.Length) { yield return new ValidationResult("サーバーサイドで引っかかりました", new[] { "Name" }); } } }
検証確認のため、適当な検証をくっつけてます。
Userコントローラー
public class UserController : Controller { public ActionResult Index() { var categoris = new List<Category> { new Category { Id = 1, Name = "A" }, new Category { Id = 2, Name = "B" } }; var useres = new List<User> { new User { Id = 1, CategoryId = 1, Name = "Tanaka" }, new User { Id = 2, CategoryId = 2, Name = "Sato" } }; return View(useres); } public ActionResult Create() { return PartialView(); } [HttpPost] public ActionResult Create(User target) { if (ModelState.IsValid) { //追加処理は省略 return RedirectToAction("Index"); } return PartialView(target); } }
これまたいい加減ですが、Create実行時に目的であるPartialViewで表示するように設定してます。
Indexビュー
@model IEnumerable<MvcTestProject01.Models.User> @{ ViewBag.Title = "Index"; Layout = "~/Views/Shared/_Layout.cshtml"; } @section styles{ <link href="~/Content/jqModal.css" rel="stylesheet" type="text/css" /> } @section scripts{ <script src="~/Scripts/jqModal.js" type="text/javascript"></script> <script type="text/javascript"> function createShow() { $('#jqmdialog').jqm({ modal: true, ajax: '/User/Create' }); $('#jqmdialog').jqmShow(); } </script> } <h2>Index</h2> <div id="jqmdialog" class="jqmWindow"></div> <p> <a href="#" onclick="createShow()">Create New</a> </p> <table> <tr> <th> @Html.DisplayNameFor(model => model.Name) </th> <th> @Html.DisplayNameFor(model => model.CategoryId) </th> <th></th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Name) </td> <td> @Html.DisplayFor(modelItem => item.CategoryId) </td> <td> @Html.ActionLink("Edit", "Edit", new { id=item.Id }) | @Html.ActionLink("Details", "Details", new { id=item.Id }) | @Html.ActionLink("Delete", "Delete", new { id=item.Id }) </td> </tr> } </table>
今回の肝であるjqModalプラグインを設定しています。流れとしては「リンククリックイベント(createShow())→ダイアログの定義→ダイアログの表示」って感じです。
Createビュー
@model MvcTestProject01.Models.User <script src="~/Scripts/jquery-1.7.1.min.js"></script> <script src="~/Scripts/jquery.validate.min.js"></script> <script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script> @using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>User</legend> <div class="editor-label"> @Html.LabelFor(model => model.Name) </div> <div class="editor-field"> @Html.EditorFor(model => model.Name) @Html.ValidationMessageFor(model => model.Name) </div> <div class="editor-label"> @Html.LabelFor(model => model.CategoryId) </div> <div class="editor-field"> @Html.EditorFor(model => model.CategoryId) @Html.ValidationMessageFor(model => model.CategoryId) </div> <p> <input type="submit" value="Create" /> </p> </fieldset> } <div> @Html.ActionLink("Back to List", "Index") </div>
こっちは何の変哲もないビューです。使ってみて初めて気がつきましたが、どうやら部分ビューは部分ビューでjquery等を定義してないと動かない模様です。最初これ飛ばしててクライアント検証が発生しなくてかなり困りました。
実行結果(Index表示時)
実行結果(Createクリック時)
おぉー、出来た出来た!…と思いきや。
実行結果(サーバーサイド検証時)
ちょっと分かりづらいんですがサーバーサイドで検証に引っかかってもう一度PartialView()が実行されるとモーダル表示が解けるどころかページ遷移が発生しちゃってるんですよ。アドレスも思いっきり「User/Create」って出ちゃってます。
…そして今に至るわけです。ちょっと手の込んだことしないと解決できなそうな気がする。何とかできたら追記するかも?
loading..
|
loading..
|
2012/10/09
【雑記】久しぶりに本日の箇条書き
- 最近まじめにコード書いてるのでネタが無い。
- CodeFirstのSeedメソッドで詰む。パッケージマネージャーコンソールが「System.Data.Entity.Validation.DbEntityValidationException: 1 つ以上のエンティティで検証が失敗しました。詳細については 'EntityValidationErrors' プロパティを参照してください。」と仰る。モデルに付けた検証が引っかかってるみたいだけどコンソールじゃプロパティ見れないよって話で…。仕方が無いのでコードから手動でSeedメソッド呼び出した。
- ディスプレイを1台LEDのフルHDにしてから目がチカチカしてたのに限界を感じ、今まで面倒で避けてたけどNVIDIAのコントロールパネルで微調整してみた。ついで古い方の黄ばみ消しにも挑戦してみた。新しい方は明るさ・コントラスト・ガンマをがっつり下げてこれってほんとにLED?(笑)みたいな暗さにしてやった。古い方の黄ばみは青色強くしたりしてみたけど、やっぱり黄色い、これはもう修正不可能なレベルなんだなぁと思い知った。ついでに開発環境も配色テーマを濃色に変えて暗くしてやった。
loading..
|
loading..
|
2012/10/06
【C#】列挙型での日本語の取り扱い…ついてでにMVCも【MVC】
列挙型で日本語っつーか全角文字列の扱いってどうすりゃいんだ?っての話の始まりです。どういうことかというと下記のコードを見てもらった方が早いでしょう。
列挙型A
列挙型B
どっちでいいだろ?って言われたらお終いなんですが、今まで半角英数で統一してんのに列挙型だけ例外とするのも気持ちが悪いというか…。当然、開発上のルールとしてメンバは必ず半角英数でって話だってあると思うのでとりあえず何とかしたいと思ったわけです(そんなものとは無縁の世界で生きてはいますが…)。
一番簡単な案としては列挙型をやめちゃってDictionaryなりListなりのユーザー定義型でやっちゃえばいいんですが、それだと根本的な趣旨から外れちゃうんで却下。で、色々と調べたところカスタム属性を使う方法等もあったんですが今回は拡張メソッドで何とかなりそうだぞってことが分かったのでこれでやってみます。(参考にさせていただいた記事:当面C#と.NETな記録)(ついでにMSDN:方法 : 列挙型対応の新しいメソッドを作成する)
列挙型Aに拡張メソッドを実装
コード自体はシンプルです。さて、これで今日は終了としても良いですが、このままじゃ中身のないマルパク記事じゃねーかって話になっちゃうので使い回しでこの間作ったサンプルにこの列挙型を追加して、MVCで表示してみます。
Userモデルを修正
コントローラーを修正
ビューを修正(一部抜粋)
実行結果
駆け足ですがこんな感じになります。コントローラーでのViewBagのSelectListに値と表示を定義するためにLINQを使った以外はほとんど過去記事と変わりません。
では今日はここまで。
列挙型A
public enum Positions { Start, Middle, End }
列挙型B
public enum Positions { 最初, 間, 最後 }
どっちでいいだろ?って言われたらお終いなんですが、今まで半角英数で統一してんのに列挙型だけ例外とするのも気持ちが悪いというか…。当然、開発上のルールとしてメンバは必ず半角英数でって話だってあると思うのでとりあえず何とかしたいと思ったわけです(そんなものとは無縁の世界で生きてはいますが…)。
一番簡単な案としては列挙型をやめちゃってDictionaryなりListなりのユーザー定義型でやっちゃえばいいんですが、それだと根本的な趣旨から外れちゃうんで却下。で、色々と調べたところカスタム属性を使う方法等もあったんですが今回は拡張メソッドで何とかなりそうだぞってことが分かったのでこれでやってみます。(参考にさせていただいた記事:当面C#と.NETな記録)(ついでにMSDN:方法 : 列挙型対応の新しいメソッドを作成する)
列挙型Aに拡張メソッドを実装
public enum Positions { Start, Middle, End } public static class PositonExtensions { public static string GetDisplayName(this Positions position) { switch (position) { case Positions.Start: return "最初"; case Positions.Middle: return "間"; case Positions.End: return "最後"; default: return string.Empty; } } }
コード自体はシンプルです。さて、これで今日は終了としても良いですが、このままじゃ中身のないマルパク記事じゃねーかって話になっちゃうので
Userモデルを修正
public class User { //・・・中略 public Positions Position { get; set; } }
コントローラーを修正
public ActionResult Edit() { //・・・中略 //SelectListで使えるようLinqを使って定義してみた var divisons = from p in (Positions[])Enum.GetValues(typeof(Positions)) select new { Value = p, Display = p.GetDisplayName() }; ViewBag.Positions = new SelectList(divisons, "Value", "Display", user.Position); return View(user); }
ビューを修正(一部抜粋)
<div class="editor-label"> @Html.LabelFor(model => model.Position) </div> <div class="editor-field"> @Html.DropDownListFor(model => model.Position, ViewBag.Positions as SelectList) @Html.ValidationMessageFor(model => model.Position) </div>
実行結果
駆け足ですがこんな感じになります。コントローラーでのViewBagのSelectListに値と表示を定義するためにLINQを使った以外はほとんど過去記事と変わりません。
では今日はここまで。
loading..
|
loading..
|
2012/10/03
【ASP.Net】ViewBagを利用してDropDownList【MVC】
最近こればっかだけどまたまたMVCネタです。
以前の記事でDropDownListはモデル側にListを用意してやらんといかんと書きましたが、ViewBagを利用することで簡単に作成することができることが分かっちゃったので…。
大した内容じゃないのでちゃちゃっとサンプルのみ書きます。
Userモデル
Categoryモデル
コントローラー(Editのみ)
ビュー
ViewBagっていまいち分かってなかったけど便利なんですね。でも何でもかんでも突っ込むと碌なことになさそうな気もするなぁ。
今日はこんだけ。
以前の記事でDropDownListはモデル側にListを用意してやらんといかんと書きましたが、ViewBagを利用することで簡単に作成することができることが分かっちゃったので…。
大した内容じゃないのでちゃちゃっとサンプルのみ書きます。
Userモデル
public class User { public int Id { get; set; } public string Name { get; set; } public int CategoryId { get; set; } }
Categoryモデル
public class Category { public int Id { get; set; } public string Name { get; set; } }
コントローラー(Editのみ)
public ActionResult Edit() { //今回は横着します var user = new User { Id = 1, CategoryId = 1, Name = "Tanaka" }; var categoris = new List<Category> { new Category { Id = 1, Name = "A" }, new Category { Id = 2, Name = "B" } }; //ViewBag定義 ViewBag.Categories = new SelectList(categoris, "Id", "Name", user.CategoryId); return View(user); }
ビュー
@using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>User</legend> @Html.HiddenFor(model => model.Id) <div class="editor-label"> @Html.LabelFor(model => model.Name) </div> <div class="editor-field"> @Html.EditorFor(model => model.Name) @Html.ValidationMessageFor(model => model.Name) </div> <div class="editor-label"> @Html.LabelFor(model => model.CategoryId) </div> <div class="editor-field"> @Html.DropDownListFor(model => model.CategoryId, ViewBag.Categories as SelectList) @Html.ValidationMessageFor(model => model.CategoryId) </div> <p> <input type="submit" value="Save" /> </p> </fieldset> }
ViewBagっていまいち分かってなかったけど便利なんですね。でも何でもかんでも突っ込むと碌なことになさそうな気もするなぁ。
今日はこんだけ。
loading..
|
loading..
|
2012/10/02
【ASP.Net】個人的な検証機能のおさらい クライアントサイド編【MVC】
サーバーサイド編に続き、今回はクライアント編です。
ひとえにクライアント編とは言ってもクライアント側であればスクリプト次第で色々と方法はあるとは思いますが、今回はjQueryのValidateプラグインを使ってみます。
まずは基本的な使い方から。
Editビューへの追加(一部抜粋)
マスターページやレイアウトページがあるならjQuery本体やValidateプラグインはそっちに配置してしまえばOKです。検証の設定は見ての通りって感じですが、一応解説すると「$(’form’).validate(…)」でこのページのすべてのformを検証の対象としています。「rules:」プロパティは各name属性に検証のルールを定義しています。「messages:…」は引っかかった際のメッセージを定義しています。プロパティ等の詳細についてはこちら(Plugins/Validation - jQuery Wiki)を確認してください。
実行結果
やっぱりプラグインは便利ですね~。でも、これじゃサーバーサイドの定義とクライアントサイドの定義で2度手間じゃねーの?って感じちゃいます。そこでMS製のValidateプラグインの派生プラグイン(?)「jquery.validate.unobtrusive」を利用します。これはNuGetで取得できます。これを読み込むだけでサーバーサイドで定義した検証をクライアントサイドで動作させることができます。
Editビューへの追加(一部抜粋)
ほんとに読み込んだだけですが、実行してみましょう。
実行結果(Post発生前)
見ての通り、Post発生前でありながら前回のPost発生後と同じ画面になっています。たったこれだけで、前半部分が無駄になっちゃいました。まぁValidateプラグインの知識のひとつということにしときます。
さて、次は独自の検証機能を持った属性を用意してサーバーサイドとクライアントサイドで連携してみたいと思います。前回紹介しなかった「System.ComponentModel.DataAnnotations.ValidationAttribute」クラスの出番です。これと「System.Web.Mvc.IClientValidatable」インターフェースを組み合わせて利用します。早速、独自検証クラスを作ります。検証内容は「Birthdayが18歳以上か?」で考えてみます。
独自検証クラス
インターフェースのメンバである「GetClientValidationRulesメソッド」って分かりづらい感ばりばりですが、今回は「userminor」というクライアントで使用する検証の名前とエラーメッセージを定義してるだけです。検証に別のパラメータ等を使用する場合はここで定義することができます(今回はややっこしくなるのでやめました)。次にモデルにこの属性を定義します。
Userモデルへの追加
こっちは「Birthday」に「UserMinor」属性をつけただけでお終いです。ちなみにこのままで実行すると他の属性とは異なり、サーバーサイドでの検証は発生しますが、クライアントサイドでの検証は発生しません。一応、実行結果載せます。
実行結果(Post発生後)
これをクライアントサイドでも検証が発生するようにスクリプトを定義します。これに関しては、はっきり言って2度手間です。
Editビューへの追加(一部抜粋)
サーバーサイドでの検証はValidateプラグインの「addMethod」関数とUnobtrusiveプラグインの「addBool」関数を利用します。前者で検証内容を定義し、後者で検証のパラメータ等を登録します。今回はパラメータがないので「addBool」関数を使用していますが、パラメータがある場合、別の関数を利用したり、独自に定義したりすることができます。以下、実行結果です。
実行結果(Post発生前)
う~ん、何かここまですると面倒くさいから独自検証に関してはサーバーサイドだけでいいんじゃ?って気もしないでもないですね。まぁ、無駄なトラフィックを発生させたくなければって感じでしょうか?
というわけで今回はここまで。
ひとえにクライアント編とは言ってもクライアント側であればスクリプト次第で色々と方法はあるとは思いますが、今回はjQueryのValidateプラグインを使ってみます。
まずは基本的な使い方から。
Editビューへの追加(一部抜粋)
<script src="~/Scripts/jquery-1.8.2.min.js" type="text/javascript"></script> <script src="~/Scripts/jquery.validate.min.js" type="text/javascript"></script> <script type="text/javascript"> $(function () { $('form').validate({ rules: { UserName: { maxlength: 50 }, Birthday: { date: true }, Email: { required: true, email: true }, CompareEmail: { required: true, equalTo: '#Email' }, Tell: { number: true } }, messages: { UserName: 'UserNameは最大50字までです' , Birthday: 'Birthdayが不正です', Email: { required: 'Emailは必須項目です', email: 'Emailが不正です' }, CompareEmail: { required: 'Emailは必須項目です', equalTo: '入力されたEmailと異なっています' }, Tell: 'Tellが不正です' } }) }); </script>
マスターページやレイアウトページがあるならjQuery本体やValidateプラグインはそっちに配置してしまえばOKです。検証の設定は見ての通りって感じですが、一応解説すると「$(’form’).validate(…)」でこのページのすべてのformを検証の対象としています。「rules:」プロパティは各name属性に検証のルールを定義しています。「messages:…」は引っかかった際のメッセージを定義しています。プロパティ等の詳細についてはこちら(Plugins/Validation - jQuery Wiki)を確認してください。
実行結果
やっぱりプラグインは便利ですね~。でも、これじゃサーバーサイドの定義とクライアントサイドの定義で2度手間じゃねーの?って感じちゃいます。そこでMS製のValidateプラグインの派生プラグイン(?)「jquery.validate.unobtrusive」を利用します。これはNuGetで取得できます。これを読み込むだけでサーバーサイドで定義した検証をクライアントサイドで動作させることができます。
Editビューへの追加(一部抜粋)
<script src="~/Scripts/jquery-1.8.2.min.js" type="text/javascript"></script> <script src="~/Scripts/jquery.validate.min.js" type="text/javascript"></script> <script src="~/Scripts/jquery.validate.unobtrusive.min.js" type="text/javascript"></script> <!-- とりあえず以下はコメントアウトしときます <script type="text/javascript"> $(function () { $('form').validate({ rules: { UserName: { maxlength: 50 }, Birthday: { date: true }, Email: { required: true, email: true }, CompareEmail: { required: true, equalTo: '#Email' }, Tell: { number: true } }, messages: { UserName: 'UserNameは最大50字までです', Birthday: 'Birthdayが不正です', Email: { required: 'Emailは必須項目です', email: 'Emailが不正です' }, CompareEmail: { required: 'Emailは必須項目です', equalTo: '入力されたEmailと異なっています' }, Tell: 'Tellが不正です' } }) }); </script> -->
ほんとに読み込んだだけですが、実行してみましょう。
実行結果(Post発生前)
見ての通り、Post発生前でありながら前回のPost発生後と同じ画面になっています。たったこれだけで、前半部分が無駄になっちゃいました。まぁValidateプラグインの知識のひとつということにしときます。
さて、次は独自の検証機能を持った属性を用意してサーバーサイドとクライアントサイドで連携してみたいと思います。前回紹介しなかった「System.ComponentModel.DataAnnotations.ValidationAttribute」クラスの出番です。これと「System.Web.Mvc.IClientValidatable」インターフェースを組み合わせて利用します。早速、独自検証クラスを作ります。検証内容は「Birthdayが18歳以上か?」で考えてみます。
独自検証クラス
public class UserMinorAttribute : ValidationAttribute, IClientValidatable { //エラーメッセージの初期化 public UserMinorAttribute() { this.ErrorMessage = "18未満の方は登録できません。(サーバーサイド)"; } //検証 public override bool IsValid(object value) { var birthday = value as DateTime?; if (birthday != null && birthday <= DateTime.Now.AddYears(-18)) { return true; } else { return false; } } //クライアントサイド用定義 public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) { var rule = new ModelClientValidationRule { ErrorMessage = "18未満の方は登録できません。(クライアントサイド)", ValidationType = "userminor" }; yield return rule; } }
インターフェースのメンバである「GetClientValidationRulesメソッド」って分かりづらい感ばりばりですが、今回は「userminor」というクライアントで使用する検証の名前とエラーメッセージを定義してるだけです。検証に別のパラメータ等を使用する場合はここで定義することができます(今回はややっこしくなるのでやめました)。次にモデルにこの属性を定義します。
Userモデルへの追加
public class User : IValidatableObject { public int UserId { get; set; } [StringLength(50)] public string UserName { get; set; } [Column("Gender", TypeName = "int")] [EnumDataType(typeof(Genders))] public Genders Gender { get; set; } [UserMinor] public DateTime Birthday { get; set; } [RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}", ErrorMessage = "アドレスが不正です。")] public string Email { get; set; } [Compare("Email", ErrorMessage = "入力されたアドレスと異なっています。")] public string CompareEmail { get; set; } [RegularExpression(@"[0-9]*$", ErrorMessage = "半角数値のみで入力して下さい。")] public string Tell { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { using (var context = new MyContext()) { var overlap = context.Users.Where((p) => p.UserName == this.UserName).FirstOrDefault(); if(overlap !=null) { yield return new ValidationResult("そのユーザー名は既に使われています。", new[] { "UserName" }); } } } }
こっちは「Birthday」に「UserMinor」属性をつけただけでお終いです。ちなみにこのままで実行すると他の属性とは異なり、サーバーサイドでの検証は発生しますが、クライアントサイドでの検証は発生しません。一応、実行結果載せます。
実行結果(Post発生後)
これをクライアントサイドでも検証が発生するようにスクリプトを定義します。これに関しては、はっきり言って2度手間です。
Editビューへの追加(一部抜粋)
<script type="text/javascript"> $.validator.addMethod('userminor', function (value, element, params) { var today = new Date(); var overYear = today.getFullYear() - 18; var overBirthday = new Date(overYear, today.getMonth(), today.getDay()); if (!value) { return true; } else { if (overBirthday < Date.parse(value)) { return false; } else { return true; } } }); $.validator.unobtrusive.adapters.addBool('userminor'); </script>
サーバーサイドでの検証はValidateプラグインの「addMethod」関数とUnobtrusiveプラグインの「addBool」関数を利用します。前者で検証内容を定義し、後者で検証のパラメータ等を登録します。今回はパラメータがないので「addBool」関数を使用していますが、パラメータがある場合、別の関数を利用したり、独自に定義したりすることができます。以下、実行結果です。
実行結果(Post発生前)
う~ん、何かここまですると
というわけで今回はここまで。
loading..
|
loading..
|
登録:
投稿 (Atom)