Kiedyś już o tym pisałem pobieżnie (kilka lat temu), ale dzisiaj chciałbym pokazać inny przykład wraz ze wszystkimi możliwymi parametrami. Stwórzmy najpierw klasyczny formularz i kontroler:
<div id="dataView"> @Html.Action("GetData") </div> @using (Html.BeginForm()) { @Html.DropDownList("selectedGroup",new SelectList(new []{"All","GroupA","GroupB"})) <br/> <input type=submit value="Refresh"/> }
Prosty formularz z ComboBox i przyciskiem, który powoduje ponowne załadowanie strony oraz wykonanie metody GetData z nowym parametrem:
public ActionResult GetData(string selectedGroup) { var data=new List<string>(); for (int i = 0; i < 5; i++) { data.Add(string.Format("{0}: {1}",DateTime.Now.ToLongTimeString(),selectedGroup)); Thread.Sleep(500); } return PartialView("DataView", data); }
DataView to widok wyświetlający po prostu kolekcje danych:
@foreach (var item in Model) { <p>@item</p> }
Wykonanie powyższego formularza w sposób asynchroniczny jest bardzo proste i nie wymagania nawet pisania JavaScript:
<div id="dataView"> </div> @using (Ajax.BeginForm("GetData",new AjaxOptions(){UpdateTargetId = "dataView"})) { @Html.DropDownList("selectedGroup",new SelectList(new []{"All","GroupA","GroupB"})) <br/> <input type=submit value="Refresh"/> }
Przykładowy kod korzysta z helpera Ajax zamiast Html. Zaglądając do wygenerowanego kodu zobaczymy:
<div id="dataView"> </div> <form action="/Home/GetData" data-ajax="true" data-ajax-mode="replace" data-ajax-update="#dataView" id="form0" method="post"><select id="selectedGroup" name="selectedGroup"><option>All</option> <option>GroupA</option> <option>GroupB</option> </select> <br/> <input type=submit value="Refresh"/> </form>
Warto zwrócić uwagę na wygenerowane atrybuty data-ajax. Aby kod działał należy podlinkować tzw. unobtrusive ajax. Skrypty są domyślnie zdefiniowane jako bundle więc każdy nowy projekt powinien już je mieć.
Powyższy kod nie jest jednak idealny. W przypadku gdy skrypty są zablokowane w przeglądarce, formularz po prostu przekieruje nas do widoku częściowego zamiast wstrzyknąć go w zdefiniowanym div. Wystarczy jednak skorzystać z właściwości Url i przekazać adres akcji:
<div id="dataView"> @Html.Action("GetData") </div> @using (Ajax.BeginForm(new AjaxOptions(){UpdateTargetId = "dataView",Url = Url.Action("GetData")})) { @Html.DropDownList("selectedGroup",new SelectList(new []{"All","GroupA","GroupB"})) <br/> <input type=submit value="Refresh"/> }
Powyższy kod w sytuacji gdy JavaScript jest włączony, wykona asynchroniczne zapytanie, a w przeciwnym razie formularz zachowa się w klasyczny sposób i przeładuje całą stronę.
Kolejną ciekawą właściwością jest LoadingElementId. Gdy operacja trwa dość długo, wtedy prawdopodobnie chcemy wyświetlić jakiś komunikat:
<div id="dataView"> @Html.Action("GetData") </div> <div id="loadingPanel" style="display:none"> <p>Loading...</p> </div> @using (Ajax.BeginForm(new AjaxOptions(){LoadingElementId = "loadingPanel",UpdateTargetId = "dataView",Url = Url.Action("GetData")})) { @Html.DropDownList("selectedGroup",new SelectList(new []{"All","GroupA","GroupB"})) <br/> <input type=submit value="Refresh"/> }
Jeśli nie chcemy wykonywać zapytania bez zgody użytkownika wtedy możemy skorzystać z Confirm:
@using (Ajax.BeginForm(new AjaxOptions(){Confirm ="Do you want to reload data?",LoadingElementId = "loadingPanel",UpdateTargetId = "dataView",Url = Url.Action("GetData")})) { @Html.DropDownList("selectedGroup",new SelectList(new []{"All","GroupA","GroupB"})) <br/> <input type=submit value="Refresh"/> }
Po naciśnięciu przycisku zostanie wyświetlony MessageBox z pytaniem i dwoma przyciskami (Yes, No).
Możemy również kontrolować jak dane będą aktualizowane. Domyślnie są one zastępowane nowymi ale możemy te zachowanie określić za pomocą InsertionMode. Enum przyjmuje wartości InsertAfter, InsertBefore albo Replace. Ostatnią właściwością jest HttpMethod przyjmujący GET albo POST jako wartość.
W analogiczny sposób można również tworzyć ajaxowe linki:
@Ajax.ActionLink("Fetch data","GetData",new{selectedGroup="GroupB"},new AjaxOptions(){LoadingElementId = "loadingPanel",UpdateTargetId = "dataView"})
Tak jak w przypadku formularzy, wystarczy użyć specjalnego helper’a (Ajax).
Do dyspozycji są również callbacki w JavaScript. Czasami chcemy obsłużyć zapytania po stronie klienta. W takich przypadkach, możemy skorzystać z dostarczonych zdarzeń:
<script type="text/javascript"> function OnBegin() { alert("OnBegin"); } function OnSuccess(data) { alert("OnSuccess: " + data); } function OnFailure(request, error) { alert("OnFailure: " + error); } function OnComplete(request, status) { alert("OnComplete: " + status); } </script> @Ajax.ActionLink("Fetch data", "GetData", new { selectedGroup = "GroupB" }, new AjaxOptions() { OnBegin = "OnBegin", OnFailure="OnFailure", OnComplete = "OnComplete", OnSuccess = "OnSuccess", LoadingElementId = "loadingPanel", UpdateTargetId = "dataView" })
Jak dla mnie pachnie przeniesieniem trochę na siłę rozwiązań ze zwykłego ASP.NET do ASP.NET MVC. Według mnie opisywane podejście nadaje się raczej do prostych stron. Jeśli chcemy zrobić coś bardziej zaawansowanego, lepiej wykorzystac choćby jQuery czy też jakiś framework JavaScript.
Ale żeby nie było, uważam że sam wpis jak zawsze na wysokiom poziomie 😉
Masz może pomysł, jak by to zrobić, żeby loading element wyświetlał się dokładnie w miejscu ładującego się elementu? Zagnieździłem go w dataView (u mnie info), ale przy InsertionMode.InsertAfter rzecz jasna gif wyświetla się na samej górze, a nowy element na samym dole, natomiast w InsertBefore dzieje się dokładnie odwrotnie. Jakiś pomysł? 🙂
yyh sorry, ale kodu mnie można wkleić 🙂