web-dev-qa-db-ja.com

MVC 3 Razor @ Html.ValidationMessageForがjquery.load()を介して部分的にロードされた状態で機能しない

問題を再現するために、ここに小さな例をまとめました。強く型付けされた部分ビュー_Name.cshtmlがあります。

@model ValidationInPartial.ViewModels.MyViewModel

<h2>@ViewBag.Message</h2>

    <fieldset>
        <legend>Name</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.MyName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.MyName)
            @Html.ValidationMessageFor(model => model.MyName)
        </div>

        <a href="#" id="reload">Reload Name</a>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>

<script type="text/javascript">
    $(document).ready(function () {
        $("#reload").click(function () {
            $("#divName").load("Home/NameReload");
        });
    });
</script>

これは最初にロードされ、メインのIndex.cshtml内に表示されます

<div id="divForm">
    @using (Html.BeginForm()) {

        <div id="divName">
            @Html.Partial("_Name")
        </div>
    }
</div>

フィールドMyNameは必須であり、検証はMyViewModelのRequired属性を介して実装されます

namespace ValidationInPartial.ViewModels
{
    public class MyViewModel
    {
        [Required(ErrorMessage = "Please enter a Name.")]
        public string MyName { get; set; }
    }
}

ページが最初に読み込まれた後、[作成]ボタンをクリックしてフィールドを空のままにすると、検証メッセージ「名前を入力してください」が表示されます。はフィールドの横に表示され、フィールド自体がピンクに変わります。これは予想される動作です。ここで、ajax呼び出し(jquery.load(...))を行う[名前の再読み込み]リンクをクリックすると、部分が再読み込みされます。コントローラーコードは次のとおりです。

public PartialViewResult NameReload()
{
    MyViewModel myViewModel = new MyViewModel();
    ViewBag.Message = "Name Reloaded";
    return PartialView("_Name", myViewModel);
}

今回は、フィールドを空のままにして[作成]ボタンをクリックすると、フィールドはピンク色に変わりますが、検証メッセージはフィールドの横に表示されません。パーシャルをリロードするときに、@ Html.ValidationMessageForが検証メッセージを初めてレンダリングしないことが判明しました。

これが私が使用するjqueryファイルです

<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>

これはRazorエンジンが@ Html.ValidationMessageForをレンダリングする方法のバグなのか、それともjqueryの問題なのか。なぜこれが起こるのか考えはありますか?

また、ajax呼び出しによってページのすべてのスクリプトが失われることもどこかで読んだことがあります。実際、JavaScriptコードをパーシャル内に保持して、レンダリングして再度使用できるようにする必要があります。

その間に、@ Html.ValidationMessageForによってレンダリングされるはずだったものを部分的に手動でレンダリングするという回避策を見つけました。これは次のとおりです。

<span class="field-validation-valid" data-valmsg-replace="true" data-valmsg-for="MyName"></span>

ただし、この回避策は、検証のタイプまたはViewModelのRequired属性内の検証メッセージのみを変更する場合、ビュー内のこのハードコードされたhtmlを変更する必要があることを意味します。

14
Roberto D

@NickBorkはここで素晴らしい答えを持っています。重要なのは、ASP.NETのMVCレンダリングエンジンは、フォームがあると見なさない場合、検証スクリプトを出力しないということです。フォームに入れて購入したハックの例が返され、HTMLの内部セクションが返され、フォームの外部ラッパーが破棄されました。

あなたがただあなたの見解を得ることができるように別の方法があります:

ViewContext.FormContext = new FormContext();

この方法では、実際にはFORMコードの出力はありませんが、検証マークアップはあります。

ありがとう、ジョン

18
John Fager

検証マークアップ(スパンタグ、カスタムフィールド属性など)は、フィールドがFORM内に含まれていない限りレンダリングされません。検証プラグイン自体は、フォーム外の要素では機能しません。

ASP.NETが部分ビューをレンダリングするとき、コントロールはフォーム内にないため、要素はレンダリングされません。

部分的なコンテンツをロードするときは、jQueryセレクターを使用してHTMLを解析する必要があります。

以下のサンプルでは、​​親のビューページに行を含むTBODYがあります。行を追加する必要がある場合は、フォーム、テーブル、tbody、および行のコレクションを持つビューを呼び出します。

$.ajax({
    type: "POST",
    url: "/controller/action",
    data: ({Your: 'dataHere'}),
    dataType: "html",
    success:
        function(response){
            $('tbody').append($('tbody',$(response)).html());

            //The validation plugin can't bind to the same form twice.
            //We need to remove existing validators
            $('form').removeData("validator");

            //Refresh the validators
            $.validator.unobtrusive.parse(document);
        },
    error:
        function(){
            alert('An error occured while attempting to add the new content');
        }
});

JQueryセレクターを使用して、AJAXを使用して読み込まれるView/PartialView内の行を選択していることに注意してください。

$('tbody',$(response)).html()

ラッパーの残りの部分は、AJAX View/PartialViewから呼び出し元の親に行を追加するだけですtbody

$('tbody').append($('tbody',$(response)).html());

他のいくつかの注意点として、バリデータプラグインをフォームで実行した後は、再度追加しないと再度呼び出すことはできません( jquery.validate.unobtrusiveが動的に挿入された要素で機能しない を参照)

これを修正するには、最初に次のメソッドを呼び出してすべてのバリデーターを削除します。

$('form').removeData("validator");
$("form").removeData("unobtrusiveValidation");

次に、以下を使用してバリデーターを更新します。

$.validator.unobtrusive.parse(document);
17
Nick Bork

どこで解決策を見つけたのか思い出せません。その理由は、jquery.validator.unobtrusiveライブラリによってすでに解析されているビューにPartialViewをロードしているためです。邪魔にならないライブラリを再解析する必要があります

    function ReparseValidation(){
       jQuery.validator.unobtrusive.parse("#yourcontainer");
    }
1
Vincent