web-dev-qa-db-fra.com

La validation discrète MVC3 ne fonctionne pas après un appel Ajax

Ok, voici l'accord, j'ai vu quelques articles sur SO relatifs à ce problème, mais rien ne fonctionne pour moi.

Fondamentalement, j'ai sélectionné des listes déroulantes qui sont chargées à partir de vues partielles, j'essaie de filtrer le contenu de chaque liste déroulante suivante, en fonction de la liste déroulante précédemment sélectionnée.

Si je mets simplement l'appel à la vue partielle dans les conteneurs div et charge la page, la validation à partir des annotations de données fonctionne correctement, principalement l'attribut requis .

Cependant, si j'essaie de charger le même partiel via AJAX tel qu'il est configuré ici, la validation requise ne fonctionne pas, tout le monde peut poster le formulaire après cela et KABOOM.

J'ai trouvé des gens dire que dans le rappel Success, vous devez demander au validateur côté client d'analyser le formulaire, et j'essaie, mais cela ne semble pas fonctionner.

J'ai une vue qui ressemble à ça ...

  @model Area51.Models.Workflow.AddReportableItemToBatchActionModel
@{
    ViewBag.Title = "Add Reportable Item to Batch";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<script type="text/javascript">

    $(function () {
        var fadeDelay = 150;

        $(".jqDatePicker").datepicker({
            dateFormat: 'm/d/yy',
            onSelect: function (date) {
                $("#categoryContainer").show(fadeDelay);
            }
        });

        $('#Category').change(function () {
            RetrieveItemsForCategory();
            $("#itemContainer").show(100);
        });

        $('#Item').live('change', function () {
            RenderPartialForUOMByItem();           
        });



        function RetrieveItemsForCategory() {

            var category = $("#Category :selected").val();

            $.ajax({
                type: "POST",

                url: '@Url.Action("RenderPartialForLocationItemsByCategory","BatchWorkflow")',

                data: 'category=' + category,

                success: function (result) {
                    $("#itemContainer").html(result.toString());
                    $("#itemContainer").show(100);
                    RebindValidation();
                },

                error: function (req, status, error) {
                    alert("Sorry! Could not request items for your selection at this time.");
                }

            });


        }


        function RenderPartialForUOMByItem() {

            var item = $("#Item :selected").val();

            $.ajax({
                type: "POST",

                url: '@Url.Action("RenderPartialForUOMByItem","BatchWorkflow")',

                data: "item=" + item,

                success: function (result) {
                    $("#quantityContainer").html(result.toString());
                    $("#quantityContainer").show(100);
                    RebindValidation();
                },

                error: function (req, status, error) {
                    alert("Sorry! Could not request items for your selection at this time.");
                }

            });
        }

        function RebindValidation() {
            alert("Rebinding Validation");
            $.validator.unobtrusive.parse("#frmAddItem");
        }

    });      // End OnLoad Event
</script>

<h3 class="pageHeader">Batch : @Model.BatchName</h3>

<div align="center">

@{Html.BeginForm("AddItemToBatch", "BatchWorkflow", null, FormMethod.Post, new { id = "frmAddItem" });}

    @Html.ValidationSummary(true)

    <fieldset style="width:60%">
        <legend>Add an Item to the Batch</legend>     

     <div>       
          <h3>Select Date Item was Added</h3>
          @Html.EditorFor(x => x.EventDate,null)
          <br />
      </div>

      <div id="categoryContainer" style="display:none"> 
        <hr />
          <h3>Select an Inventory Category</h3>
          @Html.EditorFor(x => x.Category,null)
          <br />
      </div>

      <div id="itemContainer" style="display:none"> 
        @*   @{Html.RenderAction("RenderPartialForLocationItemsByCategory", "BatchWorkflow", new { category = Model.Category });}*@
      </div>


      <div id="quantityContainer" style="display:none"> 
        @*  @{Html.RenderAction("RenderPartialForUOMByItem", "BatchWorkflow", new { item = Model.Item });}*@
      </div>

      <div id="reportingDataContainer" style="display:none"> 
        <hr />
          <h3>What quantity of the batch was affected by this addition?</h3>
          @Html.EditorFor(x => x.ConsumedWineQuantity) (Gallons)
        <br />
        <hr />
          <h3>What was the increase in Batch Volume as a result of this addition?</h3>
          @Html.EditorFor(x => x.ProducedWineQuantity) (Gallons)
      </div>

        <div style="display:block">
        <div></div>        
            <span><button type="button" id="btnCancel" class="linkButton" value="Cancel" onclick="location.href='@Url.Action("Home","Home",null)';">Cancel</button></span>  
            <span><button type="submit" id="btnSubmit" class="linkButton" value="Add">Add Item</button></span>
        </div>


    </fieldset>
        @{ Html.EndForm(); }
</div>

Les vues partielles sont très simples, elles ressemblent essentiellement à ceci ...

@model Area51.Models.Workflow.AddReportableItemToBatchActionModel

      <hr />
          <h3>Select the Item to Add</h3>
          @Html.EditorFor(x => x.Item)
          <br />

Encore une fois, si je viens de RenderPartial, la validation fonctionne bien, mais lorsque j'essaie de le faire via ajax, la validation disparaît. L'alerte "Rebinding Validation" se déclenche, mais le $ .validator.unobtrusive.parse ("# frmAddItem"); ne semble rien faire.

Quelqu'un peut-il m'aider avec ce qui me manque? Ce serait vivement apprécié.

<======================= MISE À JOUR 1 ========================= =====>

OK, j'ai essayé d'ajouter le $ .validator.unobtrusive.parse ("# frmAddItem"); au bas de la vue partielle dans un événement prêt pour le document et cela ne semblait pas fonctionner non plus, fondamentalement rien n'a changé, je pouvais toujours soumettre le formulaire.

J'ai trouvé un article ici: http://xhalent.wordpress.com/2011/01/24/applying-unobtrusive-validation-to-dynamic-content/ qui a mentionné que lorsque la version MVC de jqvalidation voit qu'un formulaire a déjà des règles de validation qui lui sont liées, il ignore simplement l'appel .validator. J'ai implémenté l'extension de script que ce monsieur a utilisé, et la validation est maintenant reliée au formulaire en utilisant la nouvelle extension. Je peux tester cela en ajoutant du HTML au formulaire et en appelant la nouvelle extension, et elle se lie à la nouvelle zone de texte.

Cependant, cela n'a toujours pas complètement résolu le problème. J'ai utilisé Firebug pour vérifier le contenu réel des champs revenant de l'appel ajax et j'ai remarqué quelque chose de très étrange.

Lorsque j'utilise le RenderPartial pour appeler l'action, il écrit la sélection suivante:

<select id="Item" name="Item" data-val-required="The Item field is required." data-val-number="The field Item must be a number." data-val="true">

Cependant, lorsque je fais l'appel ajax à la même action de contrôleur exacte, cela me donne ceci:

<select id="Item" name="Item">

J'ai également essayé d'ajouter les balises de script à la vue partielle, mais cela n'a pas résolu le problème. Y a-t-il une raison pour laquelle l'appel ajax supprimerait les balises de validation discrètes?

<======================= MISE À JOUR 2 ========================= =====>

Ok, donc ce qui se passait, c'est que j'avais un modèle d'éditeur pour le menu déroulant qui a pris une liste de sélection et l'a convertie en une sélection html. J'ai trouvé un article qui mentionnait que pour obtenir des attributs de validation des données à écrire sur un modèle d'éditeur, vous devez avoir un contexte de formulaire. Étant donné que le Html.RenderPartial était effectué dans un formulaire, le modèle d'éditeur avait un contexte de formulaire avec lequel travailler. Lorsque j'essayais simplement d'appeler le partiel via ajax, il n'y avait pas de contexte de formulaire avec lequel travailler, et au lieu de me plaindre, il n'a tout simplement pas écrit les attributs de validation des données. L'ajout d'un nouveau contexte de formulaire dans le modèle d'éditeur pour SelectListDropDown a résolu le problème.

@{ // fix to stop stupid crappy brad wilson mvc3 code from stripping the jq data valdiation attributes
    if (ViewContext.FormContext == null)
    {
        ViewContext.FormContext = new FormContext();
    }
}
36
David C

$.validator.unobtrusive.parse("#frmAddItem"); fonctionnera. Notez que ce doit être dans le partiel que vous chargez via ajax (sous le formulaire dans le partiel)

<form id="frmAddItem" method="POST" action="...">
    <!-- all the items -->
</form>
<script type="text/javascript">
    $.validator.unobtrusive.parse("#frmAddItem");
</script>
75
jgauffin

J'ajoute mon expérience car les recommandations ci-dessus n'ont pas fonctionné pour moi. Cette solution a aidé et pourrait aider d'autres personnes qui sont dirigées vers cette page à partir d'un moteur de recherche:

Ajoutez OnSuccess="$.validator.unobtrusive.parse('YourFormName');" à vous AjaxOptions

Un exemple utilisant Ajax.ActionLink:

@Ajax.ActionLink("This is a test to get unobtrusive javascript working",
                 "Name_of_your_controller_action",
                 new AjaxOptions { HttpMethod = "POST", 
                                   InsertionMode = InsertionMode.Replace, 
                                   UpdateTargetId = "UserDiv", 
                                   OnSuccess="$.validator.unobtrusive.parse('UserDetailsForm');"  
                                 }
                )

Cette solution a été trouvée sur: http://blog.janjonas.net/2011-07-24/asp_net-mvc_3-ajax-form-jquery-validate-supporting-unobtrusive-client-side-validation-and- validation côté serveur

9
user1079925

J'ai écrit ce petit extrait que vous pourrez placer dans votre fichier javascript et il gérera tous vos formulaires qui sont chargés en ajax.

//enable unobtrusive validation for ajax loaded forms
$(document).ajaxSuccess(function (event, xhr, settings) {
    //process only if html was returned
    if ($.inArray('html', settings.dataTypes) >= 0) {
        //will parse the element with given id for unobtrusive validation
        function parseUnobtrusive(elementId) {
            if (elementId) {
                $.validator.unobtrusive.parse('#' + elementId);
            }
        }

        //get the form objects that were loaded.  Search within divs
        //in case the form is the root element in the string
        var forms = $('form', '<div>' + xhr.responseText + '</div>');

        //process each form retrieved by the ajax call
        $(forms).each(function () {
            //get the form id and trigger the parsing.
            //timout necessary for first time form loads to settle in
            var formId = this.id;
            setTimeout(function () { parseUnobtrusive(formId); }, 100);
        });
    }
});
7
Alex

Une autre option, plutôt astuce, qui a fonctionné pour moi. Ajoutez simplement la ligne suivante au début de la vue partielle qui est retournée par l'appel ajax

this.ViewContext.FormContext = new FormContext(); 

Référence

4
bjan

Je n'ai pu obtenir la validation que pour travailler à l'intérieur de OnComplete au lieu de OnSuccess:

Voici le code AJAX:

@using (Ajax.BeginForm("Index", null, 
                       new AjaxOptions { OnSuccess = "onSuccess", 
                                         OnComplete = "onComplete"}, 
                       new { id = "mainForm" }))

Et voici mon script:

function onComplete(result) {
    $.validator.unobtrusive.parse("#mainForm");
    alert("Complete");
};
1
eaglei22