1

MicrosoftMvcJQueryValidation を使用して MVC 2 で jquery.validate を使用しています。モデルにデータ注釈があり、それが jquery バリデーターに変換されます。Soe Tunによって概説されているように、MicrosoftMvcJQueryValidation に変更を加えて、コントロールの横ではなく検証の概要にエラー メッセージを表示できるようにしています。

ページが読み込まれると、すべてが期待どおりに機能します。問題は、フォームを書き換えるために置換モードで ajax フォームを使用していることです。これを行うと、クライアント側の検証がすべて失われます。

検証は引き続きサーバー側で行われ、エラーのあるフィールドには、スタイルを変更するための css クラスが正しく与えられています。ただし、検証の概要には最後のエラー メッセージしか表示されません。

コントローラーは特別なものではありません。モデルが有効な場合は作業を行い、そうでない場合は同じモデルをビューに戻します。

これが私のajaxフォームのサンプルです

<% using (Ajax.BeginForm("AddCreditCard", "Dashboard",
       new { },
       new AjaxOptions() { 
           HttpMethod = "Post",
           InsertionMode = InsertionMode.Replace,
           UpdateTargetId = "quickpay-wrapper",
           OnSuccess = "newPaymentSetup",
           LoadingElementId = "loading-pane"
            }, new { id="new-credit-card-form" })) { %>

これが変更されたjavascriptです。

jQuery.validator.addMethod("regex", function(value, element, params) {
    if (this.optional(element)) {
        return true;
    }

    var match = new RegExp(params).exec(value);
    return (match && (match.index == 0) && (match[0].length == value.length));
});

// glue

function __MVC_ApplyValidator_Range(object, min, max) {
    object["range"] = [min, max];
}

function __MVC_ApplyValidator_RegularExpression(object, pattern) {
    object["regex"] = pattern;
}

function __MVC_ApplyValidator_Required(object) {
    object["required"] = true;
}

function __MVC_ApplyValidator_StringLength(object, maxLength) {
    object["maxlength"] = maxLength;
}

function __MVC_ApplyValidator_Unknown(object, validationType, validationParameters) {
    object[validationType] = validationParameters;
}

function __MVC_CreateFieldToValidationMessageMapping(validationFields) {
    var mapping = {};

    for (var i = 0; i < validationFields.length; i++) {
        var thisField = validationFields[i];
        mapping[thisField.FieldName] = "#" + thisField.ValidationMessageId;
    }

    return mapping;
}

function __MVC_CreateErrorMessagesObject(validationFields) {
    var messagesObj = {};

    for (var i = 0; i < validationFields.length; i++) {
        var thisField = validationFields[i];
        var thisFieldMessages = {};
        messagesObj[thisField.FieldName] = thisFieldMessages;
        var validationRules = thisField.ValidationRules;

        for (var j = 0; j < validationRules.length; j++) {
            var thisRule = validationRules[j];
            if (thisRule.ErrorMessage) {
                var jQueryValidationType = thisRule.ValidationType;
                switch (thisRule.ValidationType) {
                    case "regularExpression":
                        jQueryValidationType = "regex";
                        break;

                    case "stringLength":
                        jQueryValidationType = "maxlength";
                        break;
                }

                thisFieldMessages[jQueryValidationType] = thisRule.ErrorMessage;
            }
        }
    }

    return messagesObj;
}

function __MVC_CreateRulesForField(validationField) {
    var validationRules = validationField.ValidationRules;

    // hook each rule into jquery
    var rulesObj = {};
    for (var i = 0; i < validationRules.length; i++) {
        var thisRule = validationRules[i];
        switch (thisRule.ValidationType) {
            case "range":
                __MVC_ApplyValidator_Range(rulesObj,
                    thisRule.ValidationParameters["minimum"], thisRule.ValidationParameters["maximum"]);
                break;

            case "regularExpression":
                __MVC_ApplyValidator_RegularExpression(rulesObj,
                    thisRule.ValidationParameters["pattern"]);
                break;

            case "required":
                var fieldName = validationField.FieldName.replace(".", "_");
                if ($("#" + fieldName).get(0).type !== 'checkbox') {
                    // only apply required if the input control is NOT a checkbox.
                    __MVC_ApplyValidator_Required(rulesObj);
                }
                break;

            case "stringLength":
                __MVC_ApplyValidator_StringLength(rulesObj,
                    thisRule.ValidationParameters["maximumLength"]);
                break;

            default:
                __MVC_ApplyValidator_Unknown(rulesObj,
                    thisRule.ValidationType, thisRule.ValidationParameters);
                break;
        }
    }

    return rulesObj;
}

function __MVC_CreateValidationOptions(validationFields) {
    var rulesObj = {};
    for (var i = 0; i < validationFields.length; i++) {
        var validationField = validationFields[i];
        var fieldName = validationField.FieldName;
        rulesObj[fieldName] = __MVC_CreateRulesForField(validationField);
    }

    return rulesObj;
}

function __MVC_EnableClientValidation(validationContext) {
    // this represents the form containing elements to be validated
    var theForm = $("#" + validationContext.FormId);

    var fields = validationContext.Fields;
    var rulesObj = __MVC_CreateValidationOptions(fields);
    var fieldToMessageMappings = __MVC_CreateFieldToValidationMessageMapping(fields);
    var errorMessagesObj = __MVC_CreateErrorMessagesObject(fields);

    var options = {
        errorClass: "input-validation-error",
        errorElement: "span",
        errorPlacement: function(error, element) {
            var messageSpan = fieldToMessageMappings[element.attr("name")];
            $(messageSpan).empty();
            $(messageSpan).removeClass("field-validation-valid");
            $(messageSpan).addClass("field-validation-error");
            error.removeClass("input-validation-error");
            error.attr("_for_validation_message", messageSpan);
            error.appendTo(messageSpan);
        },
        messages: errorMessagesObj,
        rules: rulesObj,
        success: function(label) {
            var messageSpan = $(label.attr("_for_validation_message"));
            $(messageSpan).empty();
            $(messageSpan).addClass("field-validation-valid");
            $(messageSpan).removeClass("field-validation-error");
        }
    };

    var validationSummaryId = validationContext.ValidationSummaryId;
    if (validationSummaryId) {
        // insert an empty <ul> into the validation summary <div> tag (as necessary)
        $("<ul />").appendTo($("#" + validationSummaryId + ":not(:has(ul:first))"));

        options = {
            errorContainer: "#" + validationSummaryId,
            errorLabelContainer: "#" + validationSummaryId + " ul:first",
            wrapper: "li",

            showErrors: function(errorMap, errorList) {
                var errContainer = $(this.settings.errorContainer);
                var errLabelContainer = $("ul:first", errContainer);

                // Add error CSS class to user-input controls with errors
                for (var i = 0; this.errorList[i]; i++) {
                    var element = this.errorList[i].element;
                    var messageSpan = $(fieldToMessageMappings[element.name]);
                    var msgSpanHtml = messageSpan.html();
                    if (!msgSpanHtml || msgSpanHtml.length == 0) {
                        // Don't override the existing Validation Message.
                        // Only if it is empty, set it to an asterisk.
                        //messageSpan.html("*");
                    }
                    messageSpan.removeClass("field-validation-valid").addClass("field-validation-error");
                    $("#" + element.id).addClass("input-validation-error");
                }
                for (var i = 0; this.successList[i]; i++) {
                    // Remove error CSS class from user-input controls with zero validation errors
                    var element = this.successList[i];
                    var messageSpan = fieldToMessageMappings[element.name];
                    $(messageSpan).addClass("field-validation-valid").removeClass("field-validation-error");
                    $("#" + element.id).removeClass("input-validation-error");
                }

                if (this.numberOfInvalids() > 0) {
                    errContainer.removeClass("validation-summary-valid").addClass("validation-summary-errors");
                }

                this.defaultShowErrors();

                // when server-side errors still exist in the Validation Summary, don't hide it
                var totalErrorCount = errLabelContainer.children("li:not(:has(label))").length + this.numberOfInvalids();
                if (totalErrorCount > 0) {
                    $(this.settings.errorContainer).css("display", "block").addClass("validation-summary-errors").removeClass("validation-summary-valid");
                    $(this.settings.errorLabelContainer).css("display", "block");
                }
            },
            messages: errorMessagesObj,
            rules: rulesObj
        };
    }

    // register callbacks with our AJAX system
    var formElement = document.getElementById(validationContext.FormId);
    var registeredValidatorCallbacks = formElement.validationCallbacks;
    if (!registeredValidatorCallbacks) {
        registeredValidatorCallbacks = [];
        formElement.validationCallbacks = registeredValidatorCallbacks;
    }
    registeredValidatorCallbacks.push(function() {
        theForm.validate();
        return theForm.valid();
    });

    theForm.validate(options);
}

// need to wait for the document to signal that it is ready
$(document).ready(function() {
    var allFormOptions = window.mvcClientValidationMetadata;
    if (allFormOptions) {
        while (allFormOptions.length > 0) {
            var thisFormOptions = allFormOptions.pop();
            __MVC_EnableClientValidation(thisFormOptions);
        }
    }
}); 

ドキュメントの下部にある呼び出しを OnSuccess メソッドに移動しようとしましたが、うまくいきませんでした。

では、ajax の置換を行うときにクライアント側の検証を再初期化するにはどうすればよいですか?また、検証の概要にすべてのエラーを表示するにはどうすればよいでしょうか? 1 つの問題を修正すると、もう 1 つの問題が修正されることを願っています。

編集:

ここに私がしていることについてのもう少しの情報があります

ラッパーはこちら

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<QuickPayModel>" %>

<div id="quickpay-wrapper">
<% if (Model.NewPaymentMethod) { %>
    <% Html.RenderAction<DashboardController>(x => x.QuickPayNewMethod()); %>
<% } else { %>
    <% Html.RenderPartial("QuickPayMakePayment", Model); %>
<% } %>
</div>

これが支払いパネルです。

<%= Html.ClientValidationSummary(new { id = "valSumContainer" })%>
<% Html.EnableClientValidation(); %>

<% using (Ajax.BeginForm("QuickPay", "Dashboard",
       new { },
       new AjaxOptions() { 
           HttpMethod = "Post",
           InsertionMode = InsertionMode.Replace,
           UpdateTargetId = "quickpay-wrapper",
           OnSuccess = "updatePaymentHistory",
           LoadingElementId = "loading-pane"
            }, new { }))
   { %>
    <div class="horizontalline"><%= Html.Spacer() %></div>
    <% ViewContext.FormContext.ValidationSummaryId = "valSumContainer"; %>

    <p>
    <%: Html.LabelFor(x => x.PaymentMethods)%>

    <% if (Model.HasOnePaymentMethod) { %>
            <%: Html.DisplayFor(x => x.SelectedPaymentMethodName) %>
            <%: Html.HiddenFor(x => x.SelectedPaymentMethodId) %>

    <% } else { %>
        <%: Html.DropDownListFor(x => x.SelectedPaymentMethodId, Model.PaymentMethodsSelectList, "Select a Payment Method", new { })%>
            <%: Html.HiddenFor(x => x.SelectedPaymentMethodName)%>
        <script type="text/javascript">
            $(function () {
                $("#PaymentMethods").change(function () {
                    $("#SelectedPaymentMethodId").val($(this).val());

                    $("#SelectedPaymentMethodName").val($('option:selected', this).text());
                });
            });
        </script>

    <% } %>
    <%: Html.Spacer(12, 1) %><%: Ajax.ActionLink("New Payment Method", "QuickPayNewMethod", 
                                 new AjaxOptions() { InsertionMode = InsertionMode.Replace,
                                                     UpdateTargetId = "quickpay-wrapper",
                                                     OnSuccess = "newPaymentSetup",
                                                     LoadingElementId = "loading-pane"
                                 })%> 
    <%: Html.ValidationMessageFor(x => x.SelectedPaymentMethodId)%>

    </p>

    <p>
    <%: Html.LabelFor(x => x.Amount)%>
    <%: Html.TextBoxFor(x => x.Amount, new { disabled = Model.UseInvoicing ? "disabled" : String.Empty, 
    title = Model.UseInvoicing ? "the total payment amount of all selected invoices" : String.Empty,
    @class = "small" })%>
    <%: Html.ValidationMessageFor(x => x.Amount)%>
    </p>

    <p>
    <%: Html.LabelFor(x => x.PayDate)%>
    <%: Html.TextBox("PayDate", Model.PayDate.ToShortDateString(), new { @class = "medium" })%>
    <%: Html.ValidationMessageFor(x => x.PayDate)%>
    </p>

    <script type="text/javascript">
        $(function () {
            quickPaySetup();
        });
    </script>

    <div class="horizontalline"><%= Html.Spacer() %></div>
    <%= FTNI.Controls.Submit("Submit Payment") %>
    <%: Html.AntiForgeryToken() %>

    <%: Html.ValidationMessage("Payment-Result")%>
<% } %>

そして今、私の新しい支払い方法パネル

<script type="text/javascript">
    $(function () {
        newPaymentSetup();
    });
    </script>

    <h4>New Payment Method</h4> 

    <% if(Model.HasPaymentMethods) { %>
        <span style="float:right;">
            <%: Ajax.ActionLink("Cancel", "QuickPay", 
                                 new AjaxOptions() { 
                                     HttpMethod = "Get",
                                     InsertionMode = InsertionMode.Replace,
                                     UpdateTargetId = "quickpay-wrapper",
                                     OnSuccess = "quickPaySetup",
                                     LoadingElementId = "loading-pane"
                                 })%>
        </span>
    <% } %>

    <div>Enter the information below to create a new payment method.</div><br />

    <%= Html.ClientValidationSummary(new { id = "valSumContainer" })%>
    <% Html.EnableClientValidation(); %>
<div id="new-payment-method-tabs">
    <ul>
        <li><a href="#new-credit-card">Credit Card</a></li>
        <li><a href="#new-ach">E-Check</a></li>
    </ul>
    <div id="new-credit-card">
        <% Html.RenderPartial("NewCreditCard", Model.CreditCardModel); %>
    </div>
    <div id="new-ach">
        <% Html.RenderPartial("NewACH", Model.ACHModel); %>
    </div>
</div>

各フォームは次のようなものから始まります

<% using (Ajax.BeginForm("AddCreditCard", "Dashboard",
       new { },
       new AjaxOptions() { 
           HttpMethod = "Post",
           InsertionMode = InsertionMode.Replace,
           UpdateTargetId = "quickpay-wrapper",
           OnSuccess = "newPaymentSetup",
           LoadingElementId = "loading-pane"
            }, new { id="new-credit-card-form" })) { %>
    <% ViewContext.FormContext.ValidationSummaryId = "valSumContainer"; %>

初期ロードが機能します。ajax を置き換えると、フォームのコンテキストが失われ、何をしても再初期化されません。フォームがポストバックし、サーバー側で検証が行われます。すべての無効なフィールドが変更されます (css エラー クラスが追加されます) が、最後のエラーのみが概要に表示されます。

4

2 に答える 2

1

コールバックを介してページにフォームを書き込まないように、ソリューションを作り直すことになりました。これは私の意図したアプローチではありませんが、機能します。画面の 1 つの領域のコンテンツを変更するのではなく、jquery モーダルを使用してデータを表示することにしました。

理想的には、すべてのフォームをページにレンダリングする必要はなく、オンデマンドで呼び出すことができますが、ページの読み込み時にフォームが存在しない限り、jquery クライアント側の検証は接続されないようです。フォームの読み込み時にフォーム要素が必要かどうかはわかりませんが、対処しなければならない制限である可能性があります。

別の回避策は、それらをすべてページにレンダリングし、jquery を介して各フォームを表示/非表示にすることでした。モーダルを使用する場合と大差ありませんが、少なくとも検証は機能します。データ注釈による検証の概要とクライアント側の jquery 検証を使用するフォームが、コールバックを介してページに書き込まれ、接続されて正しく機能するソリューションをまだ見たいと思っています。

于 2010-11-24T14:26:05.240 に答える
1

ほんの数日前に私がどのように行動したかをお話しします。詳細については、この質問をご覧ください。

私の場合、jquery ダイアログ内で ajax 呼び出しを使用してフォームのコンテンツを表示していました。呼び出しが完了したら、ダイアログの内容をコントローラーから返された内容に置き換えるだけです。

質問で説明されているように、Microsoft スクリプト内のコードを変更し、ドキュメントの準備ができたら init メソッドを呼び出しました。これはあなたのケースでもうまくいくはずです...

2 番目のエラー (検証の概要にすべてのエラーを表示するにはどうすればよいですか?) については、参照している同じソース投稿で説明されているようにコードを変更しただけで、問題は発生しませんでした。

それが役に立てば幸い!

于 2010-11-22T21:20:39.377 に答える