前回ではフォームのスタイルとスキャフォールディングを弄って、デフォルト時の見た目は非常にかっこよくなりました。
しかし、実際に submit してみるとエラー表示が結構残念な感じです。当然ながら Bootstrap のクラスを使っていないからですね。
ちなみに MVC 3 ではエラーメッセージはクライアントサイドで表示されるものと、サーバーサイドで出力されるものが存在しています。クライアントサイドに関しては標準で使用している jquery.validate.unobtrusive.js が MVC 3 のデフォルト値にべったりなので、とりあえず今回は無効にしておきます。
余談ですが、クライアントサイド検証を無効にするには Web.config か HtmlHelper で設定するだけです。
<appSettings> <add key="webpages:Version" value="1.0.0.0"/> <add key="ClientValidationEnabled" value="false"/> <add key="UnobtrusiveJavaScriptEnabled" value="false"/> </appSettings>
@{ Html.EnableClientValidation(false); Html.EnableUnobtrusiveJavaScript(false); }
これでクライアントサイド検証は動きません。それでは本題に戻って、まずは Bootstrap ではエラー時にどのようなクラスが指定する必要があるか調べます。これは公式ページにサンプルがあるので安心です。
<div class="clearfix error"> <label for="errorInput">Input with error</label> <div class="input"> <input class="xlarge error" id="errorInput" name="errorInput" size="30" type="text" /> <span class="help-inline">Small snippet of help text</span> </div> </div><!-- /clearfix -->
重要なのは clearfix と同時に指定されている error とメッセージの span に指定されている help-inline です。この 2 つのクラスをエラー時に出力してあげれば ASP.NET MVC でもかっこいいエラー表示が出来るわけです。
ちなみに見た目は以下のような感じです。やっぱりかっこいいですね。
それでは ASP.NET MVC のエラー周りについて復習です。実際にエラーメッセージを出力しているのは、スキャフォールディングで生成したテンプレートに書かれている ValidationMessageFor というメソッドです。
@Html.ValidationMessageFor(model => model.Name)
そしてこのメソッドが結構残念というか、まあ仕方ないんですが出力される HTML を全くカスタマイズ出来ないという残念な仕様となってます。ちなみにエラーの場合には以下のような HTML が出力されるようになっています。
<span class="field-validation-error">Name フィールドが必要です。</span>
しかし今回は class 属性だけが必要なので、HTML 属性を指定できる ValidationMessageFor のオーバーロードメソッドを使うことにします。
以下のように匿名クラスで class の値を指定するだけです。今回の場合は help-inline を指定しました。ちなみに @class と書いている理由は class だと予約語と衝突してしまうからです。@ を頭に付けると予約語でも使えるようになります。
@Html.ValidationMessageFor(model => model.Name, null, new { @class = "help-inline" })
これでエラーメッセージに関しては対応できました。残るは clearfix と同時に指定されている error ですが、これは割と厄介な問題です。
何故厄介だというと、input やエラーメッセージの親に error クラスを付けないといけないからです。単純に考えればエラー時に if 文で出し分けすればいい話なんですが、エラーが発生したかどうかを簡単に bool 値として知る方法は用意されていません。
ということなので HTML ヘルパーを拡張することにしましょう。今回、単純に指定されたプロパティにエラーが発生しているかを bool で返す IsValid メソッドを作成しました。
public static bool IsValid<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) { var name = ExpressionHelper.GetExpressionText(expression); var fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name); ModelState modelState; return !htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState) || modelState.Errors.Count <= 0; }
ラムダ式からプロパティ名を取得し、HTML のフィールド名に変換してから ModelState にエラーが登録されていないか確認して bool 値として返しています。
それではビューにクラスを切り替えるコードを追加しましょう。
<div class="@(Html.IsValid(model => model.Name) ? "clearfix" : "clearfix error")">
全く美しくないですが、まあ一応 error の出し分けは出来るので目を瞑ります。それでは実際に動作させてみました。
Bootstrap のデモと同じようにエラー時のスタイルが当たっていることが確認できました。
やっぱり error の出し分けがあまりにも後ろ向きな対応だったので、次回で改善してみようと思います。