Utilizzare la convalida ASP.NET MVC con jquery ajax?


119

Ho una semplice azione ASP.NET MVC come questa:

public ActionResult Edit(EditPostViewModel data)
{

}

L' EditPostViewModelhanno attributi di validazione come questo:

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

Nella vista sto usando i seguenti helper:

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

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

Se eseguo un invio su un modulo in cui questa casella di testo è inserita, una convalida verrà eseguita prima sul client e poi su service ( ModelState.IsValid).

Ora ho un paio di domande:

  1. Può essere utilizzato con jQuery ajax submit invece? Quello che sto facendo è semplicemente rimuovere il modulo e facendo clic sul pulsante di invio un javascript raccoglierà i dati e quindi eseguirà il file $.ajax.

  2. Il lato server ModelState.IsValidfunzionerà?

  3. Come posso inoltrare il problema di convalida al client e presentarlo come se stessi usando build int validation ( @Html.ValidationSummary(true))?

Esempio di chiamata 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');
        }
    });
}

Modifica 1:

Incluso a pagina:

<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>

Bella risposta di seguito. Ecco una domanda correlata. La risposta consente la convalida lato client o lato server. Sono innamorato del codice JQuery che forniscono. (No, non era la mia risposta.) Stackoverflow.com/questions/28987752/…
Adventure

Risposte:


155

Dalla parte del cliente

L'utilizzo della jQuery.validatelibreria dovrebbe essere piuttosto semplice da configurare.

Specifica le seguenti impostazioni nel tuo Web.configfile:

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

Quando crei la tua vista, definisci cose come questa:

@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)

NOTA: questi devono essere definiti all'interno di un elemento del modulo

Quindi dovresti includere le seguenti librerie:

<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>

Questo dovrebbe essere in grado di configurarti per la convalida lato client

risorse

Lato server

NOTA: questo è solo per la convalida lato server aggiuntiva nella parte superiore della jQuery.validationlibreria

Forse qualcosa del genere potrebbe aiutare:

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

Dove si ValidateAjaxtrova un attributo definito come:

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;
        }
    }
}

Ciò che fa è restituire un oggetto JSON che specifica tutti gli errori del modello.

La risposta di esempio sarebbe

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

Questo verrebbe restituito al tuo errore di gestione della richiamata della $.ajaxchiamata

Puoi scorrere i dati restituiti per impostare i messaggi di errore secondo necessità in base alle chiavi restituite (penso che qualcosa di simile $('input[name="' + err.key + '"]')troverebbe il tuo elemento di input


1
Ottima risposta, soprattutto con il fantastico ValidateAjaxAttribute! Grazie!
René

3
Non capisco perché questa risposta abbia ottenuto così tanti voti. Non risponde alla domanda 1: come eseguire la convalida del client quando si pubblica con $ .ajax? Penso che la risposta di @Shyju aiuti in questo.
Valentin

2
@Valentin - la mia risposta aiuta però perché i dati sono convalidati anche sul lato server. I plug-in di convalida dovrebbero abilitare la convalida dinamica quando il modulo viene compilato e quando il modulo viene inviato (tuttavia l'OP vuole farlo), il server fornirà la convalida finale, il che è comunque preferibile poiché la convalida lato client può essere aggirata.
Andrew Burgess

8
Utilizzo gli intervalli di messaggi di convalida di jQuery ripetendo gli errori restituiti e inserendo il messaggio di errore nell'intervallo corretto:for (var i = 0; i < modelStateErrors.length; i++) { $('span[data-valmsg-for="' + modelStateErrors[i].key + '"]').text(modelStateErrors[i].errors[0]); }
Ian

7
Questa risposta è fantastica! Ma penso ancora che il framework ASP.NET MVC dovrebbe fornire un modo integrato per farlo.
Zignd

40

Quello che dovresti fare è serializzare i dati del modulo e inviarli all'azione del controller. ASP.NET MVC assocerà i dati del modulo EditPostViewModelall'oggetto (il parametro del metodo di azione), utilizzando la funzionalità di associazione del modello MVC.

Puoi convalidare il tuo modulo sul lato client e se tutto va bene, inviare i dati al server. Il valid()metodo tornerà 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
           })
        }

    });

});

1
Grazie! Ho provato questo $ ("form #" + formId) .validate () ma dice che il form (che si trova) non ha un validate ()?
Ivy

Vedi Modifica1 dove mostro che lo script di convalida è incluso nella pagina web. Vedo anche che la convalida è in esecuzione quando si utilizza un normale pulsante di invio del tipo di input.
Ivy

Ho trovato il problema (rimosso Scripts.Render da masterpage). Ma hai ancora problemi a restituire gli errori di convalida ModelState al client? Come posso ringraziarlo? Ad esempio, se l'utente non è più connesso (ModelState.AddModelError ("CustomError", "validation text").
Ivy

1
devi includere i file jquery.validate e jquery.validate.unobtrusive nella tua pagina. I tuoi input HTML hanno l'attributo che i plugin di convalida cercano?
Shyju

1
Ivy: il tipo restituito (ActionResult) è una classe base di JsonResult. in modo che possa restituire dati JSON. Quello che dovresti fare è, se si tratta di una chiamata ajax (controlla Request.IsAjax), ottieni gli errori di convalida e crea un JSON e rimandalo al client. Controlla il json nel metodo di callback di $ .post e mostra i messaggi di errore.
Shyju

9

Ecco una soluzione piuttosto semplice:

Nel controller restituiamo i nostri errori in questo modo:

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

Ecco alcuni degli script del 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>');
    });
}

Ecco come lo gestiamo tramite 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);
        }
    }
});

Inoltre, eseguo il rendering delle viste parziali tramite ajax nel modo seguente:

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

Metodo 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();
        }
    }

3
Il tuo "GetModelStateErrors" può essere semplificato completamente convertendolo in un'istruzione a riga singola più semplice: return ModelState.Values.SelectMany (x => x.Errors) .Select (x => x.ErrorMessage) .ToList ();
Camilo Terevinto

1
Perché non restituire semplicemente a PartialViewper il rendering ad Ajax?
Sinjai

4

Aggiunta una logica in più alla soluzione fornita da @Andrew Burgess. Ecco la soluzione completa:

Creato un filtro di azione per ottenere errori per la richiesta 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;
            }
        }
    }

Aggiunto il filtro al metodo del controller come:

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

Aggiunto uno script comune per la convalida di 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);
}

Infine ho aggiunto il metodo javascript di errore al mio modulo Ajax Begin:

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

1

Puoi farlo in questo modo:

( Modifica: considerando che stai aspettando una risposta jsoncon dataType: 'json')

.NETTO

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');
    }
},

Se hai bisogno posso anche spiegarti come farlo restituendo un errore 500 e ottenendo l'errore nell'evento error (ajax). Ma nel tuo caso questa potrebbe essere un'opzione


1
So come eseguire una normale richiesta di Jason, questo tuttavia non mi aiuterà con la convalida delle diverse proprietà o addirittura con la convalida MVC ASP.NET incorporata che ho richiesto. Probabilmente potrei costruire un oggetto Jason complesso per spiegare gli errori di convalida per ogni proprietà, ma questo sarà un sacco di lavoro e spero che tu possa riutilizzare la funzionalità incorporata della convalida di ASP.NET MVC per questo?
Ivy

1 - Come eseguire la funzione "SendPost" ?, E 2 - i tuoi dati validi sul client?
andres descalzo

Perché spiegare? Non ricevi il tuo ultimo post?
Ivy
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.