2009-12-10 14 views
9

Dies ist ein Follow-up zu einer previous question, die ich zuvor über die Übergabe eines Fehlers zurück an den Client hatte, sondern bezieht sich auch auf den ModelState.In ASP.Net MVC kann ModelState mit einem Ajax-Update verwendet werden?

Hat jemand erfolgreich den Nerd Dinner-Ansatz verwendet, aber mit Ajax? Also macht Nerd Dinner ein Update als so.

[AcceptVerbs(HttpVerbs.Post)] 
    public ActionResult Edit(int id, FormCollection formValues) 
{ 
    Dinner dinner = dinnerRepository.GetDinner(id); 
    try 
    { 
     UpdateModel(dinner); 
     dinnerRepository.Save(); 
     return RedirectToAction("Details", new { id=dinner.DinnerID }); 
    } 
    catch 
    { 
     foreach (var issue in dinner.GetRuleViolations()) { 
     ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage); 
    } 
     return View(dinner); 
    } 
} 

jQuery $ .ajax

function hijack(form, callback, errorFunction, format) { 
    $.ajax({ 
     url: form.action, 
     type: form.method, 
     dataType: format, 
     data: $(form).serialize(), 
     success: callback, 
     error: function(xhr, textStatus, errorThrown) { 
      errorFunction(xhr, textStatus, errorThrown); 
     } 
    }); 
} 

Ajax verwenden, die "versuchen" Teil der Steuerung wird

try 
{ 
    UpdateModel(dinner); 
    dinnerRepository.Save(); 
    return PartialView("PartialDetails", new { id=dinner.DinnerID }); 
} 

, aber was tun Sie, um die Klinke?

Eine einfache Fehlerbehandlung Lösung

catch(Exception ex) 
{ 
    Response.StatusCode = 500;     
    return Content("An Error occured."); 
    //throw ex; 
} 

, aber die nicht durch das robuste Model in MVC gebaut passieren wäre ein Fehler zurück zu senden. Ich dachte an eine Reihe von Optionen, aber ich möchte wirklich 2 Dinge:

  1. Ich möchte den Fehler in jQuery Fehler Attribut behandelt werden.
  2. Ich möchte integrierte ASP.Net MVC-Prüflogik so viel wie möglich verwenden.

Ist das möglich? Wenn nicht, welche sind die besten Alternativen, die Sie kennen?

Vielen Dank.

Update Ich habe dies noch nicht als beantwortet markiert, weil ich noch nicht implementiert habe, was ich denke, wird am besten funktionieren.

Ich habe entschieden, dass ich den Erfolg nicht wirklich mag => aktualisierte Liste senden, Fehler => sende Fehlermeldung Ansatz, den ich nahm. Ich habe dies getan, um die Anzahl der Anrufe zu reduzieren, aber eine aktualisierte Liste wird wirklich auf die Seite gesetzt. Der Versuch, beides zu tun, bindet das Popup an seine gesamte Seite.

Ich werde ein benutzerdefiniertes jQuery-Ereignis hinzufügen, um die Masterseitenliste zu aktualisieren, wenn das Dialogfeld geschlossen wird. Im Wesentlichen ist es das Beobachtermuster. Ich mag die Idee, dass die Seite zum Popup sagt "Sag mir, wenn du fertig bist" (aka geschlossen), ohne das Popup warum zu sagen. Es erfordert einen zusätzlichen Anruf, aber ich sehe das nicht als großes Problem.

Ich bin mir immer noch nicht sicher, wie gut, dass ich serverseitige Validierung mag/nicht mag und ich erwäge, mit der clientseitigen Validierung zu gehen. Während serverseitige Validierung scheint wie saubere Schichtung, hat es auch eine Reihe von Problemen, einschließlich:

1) Es setzt Qualitätschecks am Ende, anstatt des Anfangs. Eine Analogie zur Herstellung wäre ein Auto, das getestet wird, wenn es beim Händler ankommt, statt an den Stellen, an denen es gebaut wird.
2) Es verletzt die Absicht von Ajax. Bei Ajax geht es nicht nur darum, asynchrone Ereignisse zu senden, es geht auch darum, nur das zu senden, was ich brauche, und nur das zu empfangen, was ich brauche. Das Zurücksenden des gesamten Modellstatus, um Fehlerdetails bereitzustellen, scheint nicht mit Ajax zu gehen.

Was ich darüber denke, ist clientseitige Validierung, aber dieser Servercode und ein benutzerdefiniertes Ansichtsmodell können verwendet werden, um dem Client mitzuteilen, wie diese Validierungsregeln dynamisch erstellt werden.

Ich vermute auch, dass eine dynamische Sprache wie IronRuby oder IronPython einen eleganteren Weg bieten könnte, um diese Probleme zu lösen, aber es könnte etwas länger dauern, bevor ich auf diese Möglichkeit eingehe.

+0

Nun ich denke, es hängt wirklich vom Szenario ab, wenn 2 Anfrage ist kein Problem, ich werde dafür gehen.Persönlich eine Menge Validierung mit JavaScript auf der Client-Seite zu tun ist nicht etwas, das ich liebe (ich weiß nicht warum, aber ich sehe js wie etwas nicht sehr vertrauenswürdig/sicher/gleichermaßen implementiert (in diesem einen jquery speichern den Tag)), speziell weil manchmal kann man nicht alle Validierungen auf der Client-Seite, Sie müssen eine Art von Sever Side-Checks, wie (diese Entität ist bereits in der DB?), und unterstützen js deaktivierte Clients, aber wie ich es zu Beginn gesagt hängt vom Szenario ab. – JOBG

+0

Ich stimme zu, dass Sie nicht alle Validierung auf der Client-Seite tun können, scheint es nur wie das Validierungs-Framework scheint hauptsächlich auf Feldfehler ausgerichtet (zu lange, kein Datum, etc.). Zumindest in den Beispielen, die ich gesehen habe. Wenn Sie Benutzereingaben überprüfen, scheint Javascript der geeignete Ort für die Überprüfung zu sein. – John

+0

Ich habe mich entschlossen, zwei Anrufe zu tätigen, anstatt nur eine, weil die Detailansicht aus der darunter liegenden Seitenansicht entfernt wird. Ich möchte, dass das Popup zur Seite "Ich bin geschlossen" zurücksagt, aber nichts darüber weiß, was die Seitenansicht bewirken wird. jQuery sollte dies ohne viel Aufwand ermöglichen. Dadurch kann ich mir keine Sorgen darüber machen, dass Änderungen an meiner Detailansicht die Seite beeinflussen und umgekehrt. – John

Antwort

4

Wenn ich richtig verstehe, was Sie zu tun versuchen, meine erste Antwort wird nein sein, Sie können den Modellzustand nicht verwenden, wie es durch und Ajax-Anforderung ist. Vielleicht können Sie das Model Verhalten emulieren, um die Fehler anzuzeigen:

  1. eine List<KeyValuePair<string,string>> (Eigenschaft, message) von JSON Passing (dies erfordert, dass Sie die modelErrors die Model an die neue Struktur bilden passieren) und tun die HTML-Konstruktion einer Validierungszusammenfassung von JS/jQuery (von der ich denke, dass sie eine Lösung zum Übertöten ist).

  2. Wenn Sie auf den Server gehen, und es gibt irgendwelche Fehler, nur eine teilweise Rendering der Html.ValidationSummary(), übergeben Sie es durch JSON und vor dem Formular. Wenn alles in Ordnung war, geben Sie einfach die Ansicht PartialDetails zurück und ersetzen Sie den tatsächlichen Inhalt. Dies erfordert eine Art von Status-Parameter, damit Sie wissen, was vom Server auf dem Ajax-Callback zurückkommt.

EDIT: Diese letzte Option klingt gut, aber schwierig, weil Sie eine Teilansicht in einer String-Form von JsonResult zurückkehren müssen, ist hier eine Frage und Lösung darüber Render a view as a string hacken.

Persönlich glaube ich nicht, dass die Verwendung des Fehler-Attributs überhaupt etwas bringt, ich benutze es nur für sehr spezifische Situationen wie Timeout-Fehler und Server-Ausnahmen, nicht App-Ausnahmen.

EDIT: JSON Mit

[AcceptVerbs(HttpVerbs.Post)] 
     public ActionResult Edit(int id, FormCollection formValues) 
     { 
      Dinner dinner = dinnerRepository.GetDinner(id); 
      try 
      { 
       UpdateModel(dinner); 
       dinnerRepository.Save(); 
       return Json(new 
       { 
        result = "success", 
        html = this.RenderToString("PartialDetails", dinner) 
       }); 

      } 
      catch 
      { 
       foreach (var issue in dinner.GetRuleViolations()) 
       { 
        ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage); 
       } 
       return Json(new 
       { 
        result = "failed", 
        html = this.RenderToString("PartialEdit", dinner) 
       }); 
      } 
     } 

Hier das Ergebnis Parameter lassen Sie wissen, welche Maßnahmen in jedem Fall zu tun, müssen es nur auf den Rückruf überprüfen.

+0

Danke. Ein Teil der Herausforderung besteht jedoch darin, dass ich die Ergebnisse lieber als PartialView-Ergebnis und nicht als JSON-Ergebnis zurücksenden möchte. Für 2, machst du 2 Anrufe? Eins, um Erfolg/Fehler zurückzugeben und ein anderes, um die Ergebnisse zu liefern? Das ist eine Option, an die ich gedacht habe. – John

+0

Es ist wirklich ein Aufruf mit dieser Struktur wie {"status": [{"code": "error"}], "html": [{"html": "viel html"}]}. Wenn Sie möchten, dass es nur ein partialView-Ergebnisobjekt ist, warum sollte nicht einfach das Loch Teilansicht bearbeiten zurückgegeben werden, falls das Modell Fehler aufweist? Wenn keine Fehler auftreten, funktioniert der Modellstatus auf diese Weise ohne Probleme. – JOBG

+0

Der Teil, der mein Szenario von den meisten Beispielen unterscheidet, ist, dass ich zwei sehr unterschiedliche Aktionen ausführen möchte, je nachdem, ob es ein Erfolg oder ein Fehler ist. Wenn es sich um einen Fehler handelt, kann ich die Teilansicht bearbeiten zurückgeben und den Dialog neu erstellen. Wenn es jedoch erfolgreich ist, möchte ich den Dialog schließen und die Liste auf der Hauptseite aktualisieren. Also muss der clientseitige Code wissen, ob es erfolgreich ist/fehlgeschlagen ist, um zu wissen, welches Objekt zu aktualisieren ist - der Dialog oder die Liste. – John

2

Hinzufügen meiner bevorzugten Ansatz mit MVC3. Mit Ihrer bearbeiten Methode, die Sie so etwas wie

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Edit(int id, FormCollection formValues) 
{ 
    Dinner dinner = dinnerRepository.GetDinner(id); 
    try 
    { 
     UpdateModel(dinner); 
     dinnerRepository.Save(); 
     return Json (new { Success = true, Url = Url.Action("DetailsPartial", dinner), Div = "#DivToUpdateId" }); 
    } 
    catch 
    { 
     foreach (var issue in dinner.GetRuleViolations()) { 
     ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage); 
    } 
     //I am replacing this with a partial view which will contain the model state errors. For people using MVC 3 with razor they should be able to use their normal views as partials 
     return PartialView(dinner); 
    } 
} 

tun konnten, und dann können Sie eine Erfolgsfunktion wie

success: function(data) { 
    if (data.Success) { 
     $.post(data.Url, function(partial) { 
      $(data.Div).html(partial); 
     }); 
    } 
    else 
    { 
     $('#formDiv').html(data) 
    } 
} 

also im Grunde verwenden, wenn das Ergebnis Json dann data.Success wahr ist. Anschließend aktualisiert es das in json (data.Div) angegebene div mit den Ergebnissen der in der URL angegebenen Aktion. Wenn das Ergebnis nicht Json ist, weil die Post-Methode fehlgeschlagen ist und das FormDiv mit dem Teilformular aktualisiert wird, das die ModelState-Fehler enthält. Dies ist der Ansatz, den ich für Dialoge verwende, aber es funktioniert ziemlich gut. Offensichtlich würde ich mit MVC3 einige der Bearbeitungsmethoden wie TryUpdateModel usw. ändern, aber nur versuchen, ein Beispiel meiner Vorgehensweise zu geben. Durch das Übergeben der URL und das Veröffentlichen der Ergebnisse der Methode muss nicht versucht werden, HTML in eine Zeichenfolge zu rendern, um eine partielle zu übergeben.

+0

Interessant. Wären Sie in der Lage, Ihren Code für die Teilansicht bearbeiten zu veröffentlichen? – John