2010-06-20 17 views
9

Questa è probabilmente ancora una domanda per principianti.Viste parziali ASP.Net MVC che mantengono lo stato del modello?

Quando creo un'applicazione ASP.NET MVC2, un controller account con un account di accesso di azione viene creato in questo modo:

[HttpPost] 
public ActionResult LogOn(LogOnModel model, string returnUrl) 
{ 
    if (ModelState.IsValid) 
    { 
     if (MembershipService.ValidateUser(model.UserName, model.Password)) 
     { 
     FormsService.SignIn(model.UserName, model.RememberMe); 
     if (!String.IsNullOrEmpty(returnUrl)) 
     { 
      return Redirect(returnUrl); 
     } 
     else 
     { 
      return RedirectToAction("Index", "Home"); 
     } 
     } 
     else 
     { 
      ModelState.AddModelError("", "The user name or password provided is incorrect."); 
     } 
    } 

     // If we got this far, something failed, redisplay form 
     return View(model); 
    } 

Ora, io non voglio avere una pagina di login, voglio avere controlli di accesso come parte di una pagina più grande. Così, ho cambiato Login.aspx a Login.ascx e sto integrando nel mio vista principale sia con Html.RenderPartial o Html.RenderAction.

Entrambe le opere come un fascino se il login è successo. Se non lo è, il

return View(model) 

mi sta uccidendo. Quello che voglio è quello di tornare alla mia pagina principale (lo chiamano Home/Index), ma con le informazioni di errore della vista parziale.

return RedirectToAction("Index", "Home") 

Ovviamente non funziona.

Suggerimenti?

risposta

3

Questa non è certo una domanda da principianti e ho pettinato su e giù per il web per una risposta a questo problema e finora la soluzione migliore che ho trovato è stata in qualche modo nascosta in questo tutorial here. Questo è ciò che Darin Dimitrov stava suggerendo con l'aggiornamento Ajax.Cercherò di riassumere le parti importanti di quel link e perché questo non è facilmente risolto:/

Ajax rinfrescare basa sulla strana amante

La soluzione con l'ajax aggiornamento praticamente cerniere sul seguente funzione (strano amante utilizza ControllerContext ma non esiste per me, così ho ControllerExtension):

ControllerExtension.RenderPartialViewToString(this,"mypartial", (object)model) 

Questa funzione è quello che prende il proprio modello + ModelState e rigenera la vostra vista parziale in una stringa HTML. È quindi possibile prendere quella stringa e inviarla di nuovo in un oggetto json ad alcuni javascript per aggiornare la vista. Ho usato jQuery e sembra che questo,

$(document).ready(function() { 
    var partialViewUpdate = function (e) { 
      e.preventDefault(); //no postback 
      var partialDiv = $(this).parent(".partial"); 
      $.post($(this).attr("action"), 
        $(this).serialize(), 
        function (json) { 
        if (json.StatusCode != 0) { 
         // invalid model, return partial 
         partialDiv.replaceWith(json.Content); 
        } 
        else if (json.Content != null && json.Content != "") { 
         window.location.replace(data.Content); 
        }; 
      }); 

    $(".partial").find("form") 
       .unbind('submit') 
       .live("submit", partialViewUpdate); 
}; 

Jquery spiegazione:

  1. Cercare il div che contiene il mio parziale (class = "parziale") e trovare la forma all'interno di tale div
  2. Non associare nessun altro evento "invia" con quel modulo (ho ricevuto qualche strano bug di invio doppio fino a quando non l'ho fatto).
  3. uso "dal vivo" in modo che una volta che il contenuto viene sostituito rebinds nuovo
  4. Una volta che si entra nella funzione di partialViewUpdate ...
  5. Impedire la forma di finire la sostengono in modo che tutto può essere gestito da ajax.
  6. recuperare i div che contiene il mio parziale (utilizzerà questo più tardi)
  7. Setup il jquery posta URL prendendolo dalla forma, $ (this) .attr ("azione")
  8. assumere la forma (vale a dire il nostro modello) e serializzarlo per la funzione controller, $ (this) .serialize()
  9. Creare la funzione che gestirà il valore restituito ajax.
  10. Uso il mio oggetto json personale, in cui uno StatusCode 1 è danneggiato. Quindi, se è male, prendo ciò che è contenuto, questa è la stringa che mi ha dato la stringa RenderPartialViewToString e sostituisco semplicemente il contenuto del div che contiene il mio partial.

Perché non "solo di lavoro" normalmente

Quindi la ragione le parziali non solo lavorare con la convalida ModelState è che non è possibile tornare View (modello) con il POST perché MVC lo risolverà all'indirizzo di route della vista parziale (login.ascx) invece di dove è incorporato il partial (index.aspx).

Non è inoltre possibile utilizzare RedirectAction() perché invierà a (index.aspx) la funzione controller, che equivale a cancellare tutto e aggiornare la pagina index.aspx. Tuttavia, se si utilizza ActionFilter suggerito da Chino e Thabaza, quando la pagina viene aggiornata e la funzione di controllo login.ascx viene nuovamente disattivata, riprenderà quel tempdata. Ciò tuttavia non funziona se l'aggiornamento della pagina causa una seccatura con codice lato client come le modalit popup (ad esempio, se si aggiorna il popup scompare).

Dire che non è così

preferirei se "solo lavorato" così se qualcuno conosce il modo migliore corretto/di fare questa condivisione pleaaaase esso!Sento ancora che l'aggiornamento Ajax e le soluzioni ActionFilter non sono un modo pulito di farlo perché sembra quasi che viste parziali con forme siano impossibili da usare senza una sorta di "trucco".

1

Sì RedirectToAction ma fornire un messaggio di errore con esso nel TempData così si dovrebbe fare qualcosa di simile

TempData["errorMsg"] = "incorrect values provided"; 
return RedirectToAction("Index", "Home") 

Naturalmente nell'indice principale si dovrebbe avere un div che visualizza il messaggio

<%= html.Encode(TempData["errorMsg"]) %> 

EDIT vedo che si desidera mantenere la ModelState che potrebbe essere un problema, ma quello che si potrebbe fare è passare il ModelState nell'azione indice o passare l'oggetto ModelState nel TempData. Quello che puoi fare è controllare se ci sono errori di stato di modello nell'oggetto e se ci sono controlli nel campo e aggiungi l'errore nel campo corretto.

+0

Hm, probabilmente potrei inserire l'intero LogOnModel nel TempData. Ma per me questo sembra essere un hack. Quale sarebbe la soluzione pulita qui? – Sparhawk

+0

Sì, è un trucco la soluzione pulita è scrivere un filtro per questo. Guarda questa domanda e la mia risposta per un esempio http://stackoverflow.com/questions/2503032/where-to-use-controller-httpcontext/2503085#2503085 Nel filtro si controlla se lo stato del modello è valido se non lo è aggiungi i valori, naturalmente sarebbe meglio metterli in un attributo personalizzato perché non credo che ti serva in tutte le azioni di un controller. È quindi possibile utilizzare l'attributo sulle azioni in cui si desidera mantenere lo stato del modello. – Chino

+0

Meh. Ecco a cosa serve TempData. La mia ipotesi è che non ti piacciono le stringhe magiche e la mancanza di dati fortemente tipizzati. È giusto, ma è una scelta di stile, non un trucco. – a7drew

0

Si potrebbe specificare esplicitamente la vista per tornare:

return View("~/Views/Home/Index.aspx", model); 

In questo modo le informazioni errore verrà conservato e corretta visione resa.

+0

Se lo facessi, passerei il LogOnModel alla Home/Index-View. Probabilmente non funzionerebbe. Forse la domanda non è così principiante dopotutto :) – Sparhawk

+1

@Sparhawk, hai ragione. Cosa succede se hai restituito il modello della vista Home/Indice? Il modello di visualizzazione LogOn dovrebbe essere un sottoinsieme del modello di visualizzazione Home/Indice. In alternativa, è possibile ajaxificare il modulo LogOn e richiamare questa azione in modo asincrono che, in caso di successo, il reindirizzamento verrebbe eseguito sul client (window.location.href) e in caso di errore restituire un 'PartialView' e la chiamata di script questa azione aggiornerebbe il div che contiene questo modulo. –

+1

Attualmente non è un sottoinsieme del modello di visualizzazione Home/Indice. Ho un portale e ogni pagina (come Home/Index) ha un numero di controlli su di esso. Sto rendendo i controlli con RenderAction nella pagina. Quindi, perché la vista Home/Index dovrebbe ora qualcosa su un LogonModel? Certo, potrei usare l'ajax. Ma sarebbe come aggirare il problema. Non esiste una soluzione pulita per le viste parziali con logica di validazione? – Sparhawk

0

Date un'occhiata a pratica # 13 su questo blog. Questo metodo funziona bene per passare informazioni sullo stato del modello quando stai codificando nello stile PRG (Post-Redirect-Get). Dovrai solo creare un paio di filtri azione e applicarli alle tue azioni di acquisizione e pubblicazione come appropriato.

0

Ho avuto lo stesso problema quando usavo Ajax.BeginForm, dove avevo bisogno di restituire una vista parziale, ma tutti gli errori dello stato di modello erano spariti. qual è il trucco per isolare la parte Ajax.BeginForm in una vista separata e RenderPartial questa vista all'interno del div UpdateTargetId in un'altra, contenente la vista.

In questo modo è possibile restituire il modello di visualizzazione con gli errori del modello quando sono disponibili, oppure mostrare semplicemente un messaggio di successo di propria scelta (se presente). ecco una spiegazione dettagliata e dettagliata: http://xhalent.wordpress.com/2011/02/05/using-unobtrusive-ajax-forms-in-asp-net-mvc3/

Problemi correlati