Utiliser la validation ASP.NET MVC avec jquery ajax?

119

J'ai une action ASP.NET MVC simple comme ceci:

public ActionResult Edit(EditPostViewModel data)
{

}

Les EditPostViewModelattributs de validation ont comme ceci:

[Display(Name = "...", Description = "...")]
[StringLength(100, MinimumLength = 3, ErrorMessage = "...")]
[Required()]
public string Title { get; set; }

Dans la vue, j'utilise les aides suivantes:

 @Html.LabelFor(Model => Model.EditPostViewModel.Title, true)

 @Html.TextBoxFor(Model => Model.EditPostViewModel.Title, 
                        new { @class = "tb1", @Style = "width:400px;" })

Si je fais un envoi sur un formulaire que cette zone de texte est placée dans une validation se fera d'abord sur le client puis sur le service ( ModelState.IsValid).

Maintenant, j'ai quelques questions:

  1. Cela peut-il être utilisé avec jQuery ajax submit à la place? Ce que je fais, c'est simplement supprimer le formulaire et en cliquant sur le bouton Soumettre, un javascript recueillera des données, puis exécutera le fichier $.ajax.

  2. Le côté serveur ModelState.IsValidfonctionnera-t-il?

  3. Comment puis-je renvoyer le problème de validation au client et le présenter comme si j'utilisais le build int validation ( @Html.ValidationSummary(true))?

Exemple d'appel Ajax:

function SendPost(actionPath) {
    $.ajax({
        url: actionPath,
        type: 'POST',
        dataType: 'json',
        data:
        {
            Text: $('#EditPostViewModel_Text').val(),
            Title: $('#EditPostViewModel_Title').val() 
        },
        success: function (data) {
            alert('success');
        },
        error: function () {
            alert('error');
        }
    });
}

Modifier 1:

Inclus sur la page:

<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>
Lierre
la source
Belle réponse ci-dessous. Voici une question connexe. La réponse permet une validation côté client ou côté serveur. Je suis amoureux du code JQuery qu'ils fournissent. (Non, ce n'était pas ma réponse.) Stackoverflow.com/questions/28987752/…
Aventure

Réponses:

155

Côté client

En utilisant le jQuery.validate bibliothèque devrait être assez simple à configurer.

Spécifiez les paramètres suivants dans votre Web.configfichier:

<appSettings>
    <add key="ClientValidationEnabled" value="true"/> 
    <add key="UnobtrusiveJavaScriptEnabled" value="true"/> 
</appSettings>

Lorsque vous construisez votre vue, vous définiriez des choses comme ceci:

@Html.LabelFor(Model => Model.EditPostViewModel.Title, true)
@Html.TextBoxFor(Model => Model.EditPostViewModel.Title, 
                                new { @class = "tb1", @Style = "width:400px;" })
@Html.ValidationMessageFor(Model => Model.EditPostViewModel.Title)

REMARQUE: ceux- ci doivent être définis dans un élément de formulaire

Ensuite, vous devrez inclure les bibliothèques suivantes:

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

Cela devrait être en mesure de vous configurer pour la validation côté client

Ressources

Du côté serveur

REMARQUE: Ceci est uniquement pour une validation supplémentaire côté serveur en haut de la jQuery.validationbibliothèque

Peut-être que quelque chose comme ça pourrait aider:

[ValidateAjax]
public JsonResult Edit(EditPostViewModel data)
{
    //Save data
    return Json(new { Success = true } );
}

ValidateAjaxest un attribut défini comme:

public class ValidateAjaxAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (!filterContext.HttpContext.Request.IsAjaxRequest())
            return;

        var modelState = filterContext.Controller.ViewData.ModelState;
        if (!modelState.IsValid)
        {
            var errorModel = 
                    from x in modelState.Keys
                    where modelState[x].Errors.Count > 0
                    select new
                           {
                               key = x,
                               errors = modelState[x].Errors.
                                                      Select(y => y.ErrorMessage).
                                                      ToArray()
                           };
            filterContext.Result = new JsonResult()
                                       {
                                           Data = errorModel
                                       };
            filterContext.HttpContext.Response.StatusCode = 
                                                  (int) HttpStatusCode.BadRequest;
        }
    }
}

Cela renvoie un objet JSON spécifiant toutes vos erreurs de modèle.

Un exemple de réponse serait

[{
    "key":"Name",
    "errors":["The Name field is required."]
},
{
    "key":"Description",
    "errors":["The Description field is required."]
}]

Cela serait renvoyé à votre rappel de gestion des erreurs de l' $.ajaxappel

Vous pouvez parcourir les données renvoyées pour définir les messages d'erreur selon les besoins en fonction des clés renvoyées (je pense que quelque chose comme $('input[name="' + err.key + '"]')trouverait votre élément d'entrée

Andrew Burgess
la source
1
Excellente réponse, en particulier avec l'impressionnant ValidateAjaxAttribute! Je vous remercie!
René
3
Je ne comprends pas pourquoi cette réponse a obtenu autant de votes. Il ne répond pas à la question 1: comment faire la validation client lors de la publication avec $ .ajax? Je pense que la réponse @Shyju aide à cela.
Valentin
2
@Valentin - ma réponse aide cependant car les données sont également validées côté serveur. Les plugins de validation doivent permettre la validation dynamique au fur et à mesure que le formulaire est rempli, et lorsque le formulaire est soumis (cependant, l'OP le souhaite), le serveur fournira la validation finale, ce qui est préférable de toute façon car la validation côté client peut être contournée.
Andrew Burgess
8
J'utilise les intervalles de messages de validation de jQuery en parcourant les erreurs renvoyées et en insérant le message d'erreur dans la plage correcte:for (var i = 0; i < modelStateErrors.length; i++) { $('span[data-valmsg-for="' + modelStateErrors[i].key + '"]').text(modelStateErrors[i].errors[0]); }
Ian
7
Cette réponse est géniale! Mais je pense toujours que le framework ASP.NET MVC devrait fournir un moyen intégré de le faire.
Zignd
40

Ce que vous devez faire est de sérialiser vos données de formulaire et de les envoyer à l'action du contrôleur. ASP.NET MVC liera les données du formulaire à l' EditPostViewModelobjet (votre paramètre de méthode d'action), à l'aide de la fonctionnalité de liaison de modèle MVC.

Vous pouvez valider votre formulaire côté client et si tout va bien, envoyer les données au serveur. La valid()méthode vous sera utile.

$(function () {

    $("#yourSubmitButtonID").click(function (e) {

        e.preventDefault();
        var _this = $(this);
        var _form = _this.closest("form");

        var isvalid = _form .valid();  // Tells whether the form is valid

        if (isvalid)
        {           
           $.post(_form.attr("action"), _form.serialize(), function (data) {
              //check the result and do whatever you want
           })
        }

    });

});
Shyju
la source
1
Merci! J'ai essayé ce $ ("form #" + formId) .validate () mais il dit que le formulaire (qui est trouvé) n'a pas de validate ()?
Ivy
Voir Edit1 où je montre que le script de validation est inclus sur la page Web. Je vois également que la validation est en cours lors de l'utilisation d'un bouton d'envoi de type d'entrée normal.
Ivy
J'ai trouvé le problème (supprimé Scripts.Render de la masterpage). Mais vous rencontrez toujours des problèmes pour renvoyer les erreurs de validation ModelState au client? Comment suis-je censé remercier cela? Par exemple si l'utilisateur n'est plus connecté (ModelState.AddModelError ("CustomError", "validation text").
Ivy
1
vous devez inclure les fichiers jquery.validate et jquery.validate.unobtrusive js dans votre page. Vos entrées HTML ont-elles l'attribut recherché par les plugins de validation?
Shyju
1
Ivy: votre type de retour (ActionResult) est une classe de base de JsonResult. afin qu'il puisse renvoyer des données JSON. Ce que vous devez faire est, s'il s'agit d'un appel ajax (vérifiez Request.IsAjax), obtenez les erreurs de validation et créez un JSON et renvoyez-le au client. Vérifiez le json dans la méthode de rappel de $ .post et affichez les messages d'erreur.
Shyju
9

Voici une solution assez simple:

Dans le contrôleur, nous renvoyons nos erreurs comme ceci:

if (!ModelState.IsValid)
        {
            return Json(new { success = false, errors = ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage).ToList() }, JsonRequestBehavior.AllowGet);
        }

Voici quelques-uns des scripts client:

function displayValidationErrors(errors)
{
    var $ul = $('div.validation-summary-valid.text-danger > ul');

    $ul.empty();
    $.each(errors, function (idx, errorMessage) {
        $ul.append('<li>' + errorMessage + '</li>');
    });
}

C'est ainsi que nous le gérons via ajax:

$.ajax({
    cache: false,
    async: true,
    type: "POST",
    url: form.attr('action'),
    data: form.serialize(),
    success: function (data) {
        var isSuccessful = (data['success']);

        if (isSuccessful) {
            $('#partial-container-steps').html(data['view']);
            initializePage();
        }
        else {
            var errors = data['errors'];

            displayValidationErrors(errors);
        }
    }
});

De plus, je rend des vues partielles via ajax de la manière suivante:

var view = this.RenderRazorViewToString(partialUrl, viewModel);
        return Json(new { success = true, view }, JsonRequestBehavior.AllowGet);

Méthode RenderRazorViewToString:

public string RenderRazorViewToString(string viewName, object model)
    {
        ViewData.Model = model;
        using (var sw = new StringWriter())
        {
            var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext,
                                                                     viewName);
            var viewContext = new ViewContext(ControllerContext, viewResult.View,
                                         ViewData, TempData, sw);
            viewResult.View.Render(viewContext, sw);
            viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
            return sw.GetStringBuilder().ToString();
        }
    }
Alex Herman
la source
3
Votre "GetModelStateErrors" peut être complètement simplifié en le convertissant en une instruction sur une seule ligne plus simple: return ModelState.Values.SelectMany (x => x.Errors) .Select (x => x.ErrorMessage) .ToList ();
Camilo Terevinto
1
Pourquoi ne pas simplement retourner un PartialViewpour rendre à Ajax?
Sinjai
4

Ajout d'un peu plus de logique à la solution fournie par @Andrew Burgess. Voici la solution complète:

Création d'un filtre d'action pour obtenir des erreurs pour la requête ajax:

public class ValidateAjaxAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (!filterContext.HttpContext.Request.IsAjaxRequest())
                return;

            var modelState = filterContext.Controller.ViewData.ModelState;
            if (!modelState.IsValid)
            {
                var errorModel =
                        from x in modelState.Keys
                        where modelState[x].Errors.Count > 0
                        select new
                        {
                            key = x,
                            errors = modelState[x].Errors.
                                                          Select(y => y.ErrorMessage).
                                                          ToArray()
                        };
                filterContext.Result = new JsonResult()
                {
                    Data = errorModel
                };
                filterContext.HttpContext.Response.StatusCode =
                                                      (int)HttpStatusCode.BadRequest;
            }
        }
    }

Ajout du filtre à ma méthode de contrôleur comme:

[HttpPost]
// this line is important
[ValidateAjax]
public ActionResult AddUpdateData(MyModel model)
{
    return Json(new { status = (result == 1 ? true : false), message = message }, JsonRequestBehavior.AllowGet);
}

Ajout d'un script commun pour la validation jquery:

function onAjaxFormError(data) {
    var form = this;
    var errorResponse = data.responseJSON;
    $.each(errorResponse, function (index, value) {
        // Element highlight
        var element = $(form).find('#' + value.key);
        element = element[0];
        highLightError(element, 'input-validation-error');

        // Error message
        var validationMessageElement = $('span[data-valmsg-for="' + value.key + '"]');
        validationMessageElement.removeClass('field-validation-valid');
        validationMessageElement.addClass('field-validation-error');
        validationMessageElement.text(value.errors[0]);
    });
}

$.validator.setDefaults({
            ignore: [],
            highlight: highLightError,
            unhighlight: unhighlightError
        });

var highLightError = function(element, errorClass) {
    element = $(element);
    element.addClass(errorClass);
}

var unhighLightError = function(element, errorClass) {
    element = $(element);
    element.removeClass(errorClass);
}

Enfin ajouté la méthode javascript d'erreur à mon formulaire Ajax Begin:

@model My.Model.MyModel
@using (Ajax.BeginForm("AddUpdateData", "Home", new AjaxOptions { HttpMethod = "POST", OnFailure="onAjaxFormError" }))
{
}
Manprit Singh Sahota
la source
1

Vous pouvez le faire de cette façon:

( Edit: considérant que vous attendez une réponse jsonavec dataType: 'json')

.NET

public JsonResult Edit(EditPostViewModel data)
{
    if(ModelState.IsValid) 
    {
       // Save  
       return Json(new { Ok = true } );
    }

    return Json(new { Ok = false } );
}

JS:

success: function (data) {
    if (data.Ok) {
      alert('success');
    }
    else {
      alert('problem');
    }
},

Si vous en avez besoin, je peux également expliquer comment le faire en renvoyant une erreur 500 et obtenir l'erreur dans l'erreur d'événement (ajax). Mais dans votre cas, cela peut être une option

andres descalzo
la source
1
Je sais comment faire une requête Jason régulière, cela ne m'aidera cependant pas à valider les différentes propriétés ou même à utiliser la validation ASP.NET MVC intégrée que j'ai demandée. Je pourrais probablement créer un objet Jason complexé pour expliquer les erreurs de validation pour chaque propriété, mais ce sera beaucoup de travail et j'espère que vous pourrez réutiliser la fonctionnalité intégrée de validation ASP.NET MVC pour cela?
Ivy
1 - Comment exécuter votre fonction "SendPost"?, Et 2 - vos données valides sur le client?
andres descalzo
Veuillez expliquer? Vous ne recevez pas votre dernier message?
Ivy