Podobnie jak w czystym ASP.NET, framework ASP.NET MVC pozwala na wysyłanie formularzy w tle. Załóżmy, że chcemy napisać shoutbox’a, w którym wysłanie wiadomości na serwer nie wymagałoby ponownego przeładowania całej strony. Zamiast definiowania formularza za pomocą Html.BeginForm, musimy skorzystać z Ajax.BeginForm:
<% using(Ajax.BeginForm("AddMessage",new AjaxOptions(){ OnComplete="OnFinish", UpdateTargetId="messages"})) { %> // treść <%}%>
Pierwszy parametr to nazwa akcji. W drugim parametrze przekazujemy klasę AjaxOptions określającą między innymi zdarzenie w JavaScript wywoływane po zakończeniu zapytania oraz identyfikator elementu modyfikowanego w trakcie wykonywania akcji. Stwórzmy więc prosty ShoutBox:
<div id="messages" style="width: 350px; height: 80px; overflow:auto;"> <%Html.RenderPartial("Messages"); %> </div> <% using(Ajax.BeginForm("AddMessage",new AjaxOptions(){ OnComplete="OnFinish", UpdateTargetId="messages"})) { %> <%if(Page.User.Identity.IsAuthenticated==false){ %> Nick: <br /> <%=Html.TextBox("Nick", "", new { maxlength = 50, style = "width:220px" })%> <%} %> <br /> Wiadomość: <br /> <%=Html.TextBox("Message", "Wpisz opinię, która pojawi się na mównicy...", new { maxlength = 500, title = "Wpisz opinię, która pojawi się na mównicy...", @class = "water", style = "width:220px" })%> <input type="submit" value="Wyślij" /> <%} %>
Najpierw definiujemy element div z ID ustawionym na “messages”. Następnie tworzymy asynchroniczny formularz za pomocą Ajax.BeginForm. Zawartość formularza nie zawiera już żadnej nowości – jedynie standardowe pola: jedno na nick a drugie na wiadomość. Przyjrzyjmy się jeszcze akcji AddMessage:
public ActionResult AddMessage(string message) { Repositories.Shoutbox.ShoutboxRepository repository = new Repositories.Shoutbox.ShoutboxRepository(); if(string.IsNullOrEmpty(message)) return PartialView("Messages", repository.GetNewestMessages()); Entities.ShoutBoxMessage shoutMessage = new Entities.ShoutBoxMessage() { Message = message }; shoutMessage.ID_MESSAGE = Guid.NewGuid(); shoutMessage.Date = DateTime.Now; if (User.Identity.IsAuthenticated == false) { if (!string.IsNullOrEmpty(Request["Nick"])) { shoutMessage.UserName = Request["Nick"]; } } else shoutMessage.ID_USER = new Guid(System.Web.Security.Membership.GetUser().ProviderUserKey.ToString()); repository.Create(shoutMessage); repository.SubmitChanges(); return PartialView("Messages", repository.GetNewestMessages()); }
Kod tworzy wiadomość (klasa ShoutBoxMessage) oraz dodaje ją do baz danych. Następnie akcja zwraca widok “Messages” z zawartością wszystkich wiadomości (repository.GetNewestMessages). Widok częściowy (PartialView) jest dodawany do elementu div (messages) w sposób asynchroniczny. Nie wymaga to oczywiście przeładowania strony. Na zakończenie jeszcze widok Messages.ascx (dodawany do wspomnianego już div):
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<PoliticiansPromises.Entities.ShoutBoxMessage>>" %> <%@ Import Namespace="PoliticiansPromises.Extensions" %> <% foreach(var item in Model){ %> <%if(item.ID_USER==null){ if(item.UserName==null){ %> <span class="marked_link">~Anonim</span> <%}else{ %> <span class="marked_link">~<%:item.UserName %></span> <%} %> <%}else{ %> <span class="marked_link"><%:System.Web.Security.Membership.GetUser(item.ID_USER).UserName %></span> <%} %> <span class="small_gray_style"><%: String.Format("{0:g}", item.Date)%></span> <%=Html.EncodeAndCut(item.Message,500) %> <br /> <%} %> <br />
Pomieszałeś widok z logiką w postaci pobierania w nim usera z Membership. To jest brzydkie.
I dodatkowo tworzysz NOWY obiekt repozytorium na początku wywołania akcji AddMessage. To nie jest dobry pomysł.
@Darek Tarczyński: a dlaczego nie? Przecież ta metoda jest rzadko wywoływana i po co ciągle przechowywać obiekt repozytorium w pamięci?
@SirMike: w sumie to szczegół ale teoretycznie ciężko zaprzeczyć – faktycznie nie powinno się tak robić.
@Piotr
A na jakiej zasadzie twierdzisz, że wywolywana jest rzadko? Rozumiem, że to tylko przykład, ale w systemie produkcyjnym takie stwierdzenie wymagało by przynajmniej specyfikacji przypadków użycia.
Z drugiej strony wg. mnie akurat ta metoda jest metodą wręcz kluczową w całej aplikacji, gdyż służy do wysyłania wiadomości prawda? Wystarczy, że użytkownicy zaczną pisać wiadomości i odpowadać na nie i już masz niezły ruch.
@Darek Tarczyński
Ale przeciez repozytorium nie zawiera nic skomplikowanego. Twoim zdaniem powinno to być zaimplementowane za pomocą Singleton’a?
Mógłbym prosić cały program do pobrania?