2009-09-24 7 views
19

Używam ASP.NET MVC z jQuery i mam dużo żądań Ajax do kontrolerów.Widoki częściowe vs. Json (lub oba)

Użyj widoków częściowych (usercontrols) do zbudowania widoku początkowego po wczytaniu strony. Następnie, jeśli muszę dodać/zastąpić dane w oparciu o moje żądanie Ajax, buduję HTML z odpowiedzi Jsona.

Takie podejście daje mi pełną kontrolę, tj. Mogę uzyskać dodatkowe informacje od kontrolera, jeśli coś poszło nie tak, a następnie wyświetlić komunikat o błędzie oparty na tym.

Jednak ostatnio byłem bardzo zły z powodu całej dodatkowej pracy, która idzie do utrzymania struktury HTML zarówno w moich widokach częściowych, jak i części, która generuje HTML z Json.

Chciałbym utworzyć żądanie ajax jQuery, a następnie zwrócić kontroler PartialView ("mypartialview"), a następnie po prostu użyć jQuery, aby zastąpić HTML w widoku.

Jednak w ten sposób nie mogę dołączyć dodatkowych danych z kontrolera - to jest to, co daje mi widok częściowy - albo nic. Przynajmniej to moje obecne podejście do tego.

Jeśli poprawność jakiegoś sprawdzenia poprawi się w pewnym momencie działania kontrolera, nie chcę zwracać kodu HTML widoku częściowego.

Jak więc radzić sobie z tym problemem?

Dzięki za przeczytanie.

Odpowiedz

2

Wierzę, że można zwrócić renderowany html jako ciąg znaków - może to być na przemian łańcuch html zawierający komunikat o błędzie do wyświetlenia?

+1

Rzeczywiście, właśnie taka jest reakcja PartialView. I choć nie bardzo to przeliterować, jest to rozwiązanie: Niech twoje metody AJAX zwrócą zrenderowany PartialView przy użyciu tego samego kontrolka użytkownika, który był używany do renderowania tej części strony początkowo. Robisz to pisząc powrót PartialView (model) zamiast zwracać Json (model). –

+0

Dzięki. To jest rzeczywiście lepsze wytłumaczenie :) – Paddy

+0

Craig, to właśnie teraz robię. Mój problem polega na tym, że chcę zwrócić zarówno HTML, jak i JSONA - lub inaczej: chcę wynikowy html, który jest zwracany z PartialView, a następnie zawijam w Json, aby móc wysyłać również inne dane. trochę jak ten facet: http://stackoverflow.com/questions/1168791/returning-a-rentered-html-partial-in-a-json-property-in-asp-net-mvc Chyba powyższe zadziała, ale chciałbym wiedzieć, jak sobie z tym radzą inni. – bgeek

18

Na podstawie this stackoverflow anwser właśnie postanowiłem zrobić to samo.

Najpierw utwórz metodę rozszerzenia dla klasy kontrolera.

public static string RenderViewToString(this Controller controller, string viewName, object model) 
{ 
    using (var writer = new StringWriter()) 
    { 
     var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName); 
     controller.ViewData.Model = model; 
     var viewCxt = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, writer); 
     viewCxt.View.Render(viewCxt, writer); 
     return writer.ToString(); 
    } 
} 

Następnie należy zwrócić json w metodzie akcji kontrolerów.

return Json(new 
          { 
           Html = this.RenderViewToString("MyView", model), 
           SomeExtraData = data 
          }); 

Twoje żądania ajax będą teraz otrzymywać json z zawartym w nim html. Nadal eksperymentuję z tym podejściem, zwracając zwykłe fragmenty HTML.

Nadzieję, że pomaga.

EDIT Aktualizacja do pracy z maszynką

4

Oto sposób, aby skorzystać z Content-Type zwróconej przez każdego odpowiedniego rezultatu. Używamy tego do wysyłania informacji o błędach i istnieje już JS w celu wyświetlania komunikatów, więc otrzymujemy albo częściowy widok, który chcemy, albo informacje kontrolne do zgłaszania błędów itp.

Dla porównania, częściowy widok zwraca text/html i odpowiedź JSON powinna zwrócić application/json.

Jak zwykle część zabawna znajduje się po stronie javascript, a JQuery ajax() nie zawodzi tutaj!

W swoim kontrolerze zwróć odpowiednio PartialView() lub Json(model,); używamy go w formacie try/catch.

public ActionResult Edit(int id) { 
    try { 
     var model = getYourModel(); 
     return PartialView("Edit", model); 
    } 
    catch (Exception ex) { 
     var mi = new MessageInfo(MessageType.Error, String.Format("Edit failed: {0}", ex.Message), true); 
     return Json(mi, "application/json", JsonRequestBehavior.AllowGet); 
    } 
} 

Po stronie JS używamy następującej funkcji. Zauważ, że musisz przywrócić wszystkie zdarzenia JQuery, które zostały podpięte pod $(document).ready() z początkowego poziomu GET, więc mamy parametr wywołania zwrotnego.

function getPartialView(action, controller, model, divId, callback) { 
    var url = "/" + controller + "/" + action + "/"; 
    $.ajax({ 
     type: "GET", 
     url: url, 
     data: model, 
     success: function (data, textStatus, jqXHR) { 
      var ct = jqXHR.getResponseHeader("Content-Type"); 
      var mx = ct.match("text\/html"); 
      if (mx != null) { 
       $(divId).html(data); 
       if (callback) { 
        callback($(divId)); 
       } 
      } 
      else { 
       addMessage(data.type, data.title, data.text, data.sticky); 
      } 
     }, 
     error: function (jqXHR, textStatus, errorThrown) { 
      addMessage(3, "\"" + url + "\": Failed", textStatus + ": " + errorThrown, false); 
     } 
    }); 
} 

Jedynym haczykiem nieco sprawdza nagłówek w odpowiedzi Content-Type i odpowiednio zachowywać. Pamiętaj, że "oszukujemy", zakładając, że JSON nie jest HTML. Wzywamy naszą wcześniejszą funkcję addMessage(), rób, co potrzebujesz!

Oto wreszcie przykładowy element zakotwiczenia o numerze onclick kierowany na getPartialView() powyżej.

<a href="#" onclick="getPartialView('Action', 'Controller', model, '#partialviewdivid', function(dvx) { connectJqueryEvents(dvx); })">Cancel</a> 

Works Wielki ...

wyjątkiem postaci podnosi poprzez Ajax.BeginForm() gdzie ładunek JSON jest traktowany jako HTML błędnie z powodu niewystarczającej walidacji Content-Type. Rezultatem jest Twój div, do którego dodano JSON, który zasadniczo nie jest renderowany jako HTML. Wywołanie zwrotne AjaxOptions.OnSuccess nie jest wykonywane, ale w tym momencie jest już za późno na DOM!

Jest to proste rozwiązanie, ale niestety wymaga niewielkiej naprawy do jquery-unobtrusive-ajax.js ponieważ funkcja asyncOnSuccess() było krótkowzroczne jak napisane.

function asyncOnSuccess(element, data, contentType) { 
    var mode; 

    if (contentType.indexOf("application/x-javascript") !== -1) { 
    return; 
    } 
    if (contentType.indexOf("application/json") !== -1) { 
    return; 
    } 
...snip... 
} 

W wersji OOTB brakuje drugiego oświadczenia if; dodanie go jest niezbędną koniecznością, aby nie uderzać ładunkiem JSON w DOM.

Po wprowadzeniu tej poprawki ładunek JSON przechodzi do skryptu JavaScript AjaxOptions.OnSuccess i można postępować w razie potrzeby.

Yes You Can Get Zarówno

Mam nadzieję, że wiesz, od wysyłania JSON, można odesłać jakiegokolwiek modelu, i niech Javascript sortowania go; hasOwnProperty() przydaje się tam. Więc oczywiście możesz odesłać jakiś widok HTML przez wspomniany już RenderViewToString().

0

Możesz zrobić w ten sam sposób, Umieść to w swoim kontrolerze.

protected string RenderPartialViewToString(string viewName, object model) 
{ 
    if (string.IsNullOrEmpty(viewName)) 
     viewName = ControllerContext.RouteData.GetRequiredString("action"); 

    ViewData.Model = model; 

    using (StringWriter sw = new StringWriter()) { 
     ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName); 
     ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw); 
     viewResult.View.Render(viewContext, sw); 

     return sw.GetStringBuilder().ToString(); 
    } 
}