10 marca 2009

Relacja z rozdziału 5. o widokach w Grails z DGG2

Rozdział 5. z książki "The Definitive Guide to Grails, Second Edition" dotyczy warstwy widoków w Grails. Około 40 stron naszpikowane wiadomościami dotyczącymi Groovy Server Pages (GSP). W zasadzie jestem już przy 10. rozdziale i już myślałem, że kompletnie zarzucę relacje z lektury książki ze względu na ich obszerność, ale ostatnie wydarzenia - spotkanie Warszawa JUG oraz konferencja 4Developers - upewniły mnie, że nie ma lepszego sposobu na utrwalenie wiedzy, jak właśnie jej przedstawienie na blogu (zawsze zmusza mnie do ponownego przeczytania rozdziału przynajmniej jeszcze raz). Z każdym rozdziałem ta książka upewnia mnie w przekonaniu, że jest po prostu lekturą obowiązkową każdego szanującego się Grailsowca. W międzyczasie próby z mini-aplikacją Nauczyciel w Grails i kolejne wystąpienia o Grails będą wyłącznie w stylu warsztatów (a ja nie dam się wystawić do wiatru, gdzie nic nie działa).

W Java EE 5 mamy JavaServer Pages (JSP) jako technologię widoku, a w Grails mamy Groovy Server Pages (GSP). Po co nam nowe? Z jednej przyczyny - wykorzystanie siły Groovy. Jeśli ktokolwiek próbował swoich sił w JSP czy JSF, a w szczególności w kontekście tworzenia własnych znaczników, to pamięta ile potrzeba było czasu na ich stworzenie. W GSP będzie inaczej, zdecydowanie prościej.

W GSP mamy do dyspozycji kilka atrybutów: application (egzemplarz ServletContext), flash, out (egzemplarz java.io.Writer), params (mapa parametrów żądania), request (HttpServletRequest), response (HttpServletResponse) oraz session (HttpSession).

GSP wspiera konstrukcje JSP z kilkoma własnymi rozszerzeniami. W właśnie wydanej wersji Grails 1.1 jest nawet pełne wsparcie dla znaczników JSP.

Grails jest implementacją wzorca MVC. Zgodnie z zasadami MVC, widok wyrysowywuje przekazany model. W Grails przekazanie modelu następuje z kontrolerów poprzez zwrócenie mapy z akcji, np.
 class PojecieController {
def zapytaj = {
def listaPojec = Pojecie.withCriteria {
...
}
[pojecia:listaPojec]
}
}
Dla zwrócenia uwagi: ostatnia instrukcja [pojecia:listaPojec] jest równoważna zwróceniu mapy, gdzie pod kluczem pojecia znajdziemy listę listaPojec.

Wynik wykonania akcji określa model, na którym pracuje widok i poszczególne elementy mapy są dostępne jako zmienne na stronie, jak inne atrybuty.

GSP wspiera podzbiór dyrektyw JSP - instrukcji, które występują na początku strony i określają jej działanie, np. ustawiają typ zawartości odpowiedzi - HTML, XML, "czysty" tekst - (dyrektywa <%@ page contentType %>) lub po prostu importują klasy.

W GSP możemy umieścić niewielkie kawałki dowolnego kodu grailsowego ("wstawki") między <% a %>, podobnie jak w JSP. Bardzo interesującym przykładem może być 3-krotne wyświetlenie napisu (Listing 5-4):
 <% 3.times { %>
<p>Trzykrotnie wyświetlony napis</p>
<% } %>
Dla ludzi spoza świata technologii javowych wstawki mogą przypominać RHTML, jednakże są bardziej skierowane do naszych przyzwyczajeń z JSP i dokładnie z tego samego powodu nierekomendowane na rzecz znaczników GSP.

Mamy również do dyspozycji konstrukcje <%= "tekst do wyświetlenia" %>, które zastępują konstrukcje <% out << "tekst do wyświetlenia" %>.

Różnicą między GSP a JSP jest mechanizm wbudowanych GStringów (znanych użytkownikom JSF chociażby z facelets), tj. konstrukcji ${ziarno.atrybut}. Jak to zostało w książce ujęte, GSP to jeden wielki GString, czyli mechanizm podstawiania wartości znane z Groovy w zmiennych będących ciągami znaków dotyczą i GSP, np. ${album?.title?.toUpperCase()} z użyciem operatora warunkowego ? (który przy poprzedzającej pustej wartości lub null nie wykona kolejnych metod odczytu).

Podobnie jak JSTL dla JSP, Grails posiada własną bibliotekę wbudowanych znaczników do typowych konstrukcji programistycznych - pętle, instrukcja wyboru (switch), instrukcje warunkowe (if). Każdy z wbudowanych znaczników należy do przestrzeni g i, w przeciwieństwie do JSP, nie ma konieczności importu tych, albo innych znaczników. Po prostu są widoczne i tyle. Dyrektywa taglib z JSP jest wspierana.

<g:set var="zmienna" value="wartość" /> ustawia zmienną lokalną dla strony o danej wartości. Wartość może być dowolną konstrukcją typu ${}. Opcjonalny atrybut scope określa zasięg widoczności zmiennej - application (cała aplikacja), session (sesja użytkownika), flash (to i kolejne żądanie), request (jedynie na czas trwania aktualnego żądania), page (czas wyrysowywania strony).

Znacznik g:each pozwala na dostęp do aktualnej wartości zmiennej przez it (jak w domknięciach, którymi tak na prawdę są znaczniki), lub po nazwie określonej parametrem var, np. <g:each var="song" in="${album.songs?}">. Podobnie będzie z innymi znacznikami iterującymi, jak np. g:collect.

Ograniczanie wartości, po których iterujemy, możliwe jest w g:findAll przez warunek logiczny w atrybucie expr, który jest wyliczany, dla każdego elementu w liście (atrybut in).

Ciekawostką GSP są konstrukcje typu ${it.songs?.title}, gdzie title jest atrybutem elementu w liście wskazanej przez it.songs (songs jest po prostu kolekcją elementów z danym atrybutem), a samo it jest aktualnym elementem na liście w pętli głównej (w domknięciu).

Dynamiczne znaczniki w Grails są domknięciami w klasach będących bibliotekami znaczników, które kończącą się przyrostkiem TagLib w katalogu grails-app/taglib. Nie ma znaczenia, czy znacznik jest nasz, czy dostarczany przez Grails - wszystkie domyślnie należą do przestrzeni "g". Dodatkowo, możemy je wywoływać jak metody we wstawkach GSP i wyrażeniach GString. Nabiera to znaczenia podczas tworzenia odpowiedzi będącymi poprawnymi dokumentami XML. W takim przypadku niemożliwe jest umieszczenie znacznika jako wartość atrybutu innego, np. zamiast (Listing 5-15)
 <a href="<g:createLink action="list" />">Odnośnik ("sznurek")</a>
który nie jest poprawną konstrukcją w XML możemy to zapisać jako
 <a href="${createLink(action:'list')}">Odnośnik ("sznurek")</a>
który jest już poprawny w XML.

Znacznik g:link ma 4 atrybuty: controller, action, id oraz params. Poza id, wszystkie są oczywiste. Atrybut id służy do przekazania identyfikatora, który wskaże na interesujące nas dane w klasie dziedzinowej. Jeden z atrybutów controller i action jest obowiązkowy. Brak controller wskazuje na aktualnie uruchomiony kontroler, a brak action to wskazanie na domyślną akcję danego kontrolera. Inne znaczniki "akcyjne", np. g:createLink czy g:form posiadają podobne atrybuty i ich znaczenie jest identyczne, tj. wskazują na kontroler i jego akcję.

Znacznik g:datePicker służy do obsługi pól typu java.util.Date.

Znacznik g:hasErrors umożliwia sprawdzenie, czy ze wskazanym przez atrybut bean ziarnem (a w zasadzie klasą dziedzinową) nie są związane jakiekolwiek błędy poprawności danych. Wyświetlenie błędów w bardzo podstawowej postaci to funkcja znacznika g:renderErrors.

Obsługa stronicowania to zadanie dla znacznika g:paginate.

Utworzenie własnej biblioteki znaczników to wykonanie polecenia grails create-taglib <nazwa-biblioteki>. Dowolne domknięcie to znacznik, który przyjmuje 2 parametry wejściowe - attrs i body. Parametr attrs (typ java.util.Map) jest mapą atrybutów, a body odpowiada treści znacznika (jako kolejne domknięcie). Skoro body jest domknięciem możemy wywołać je jako funkcję. W ten sposób możemy skorzystać z parametru it w ramach naszego znacznika, który będzie odpowiadał parametrowi wejściowemu domknięcia body.

Jak wszystkie znaczniki GSP i nasze będą należały do przestrzeni g chyba, że zadeklarujemy statyczną zmienną namespace w klasie biblioteki znaczników, np. (Listing 5-43):
 class PojecieTagLib {

static namespace = 'pj'

def wyswietl = { attrs, body ->
body(attrs.napis)
}
}
Warto zauważyć, że prostota tworzenia znaczników pozwala na ich dynamiczne przeładowywanie podczas tworzenia aplikacji. Nie potrzeba żadnych wyrafinowanych sztuczek. Po prostu zapis i Grails zadba o aktualność znacznika.

W testowaniu znaczników pomaga nam klasa grails.test.TagLibUnitTestCase. Rozszerzamy ją i w metodzie testującej wywołujemy metody na zmiennej tagLib, która odpowiada testowanej bibliotece znaczników (Grails rozpoznaje, o którą bibliotekę chodzi przez nazwę klasy testującej - nazwa biblioteki znaczników rozszerzona o przyrostek Tests). Metoda-znacznik - przyjmuje atrybuty w postaci klucz:wartość, a body przekazywane jest jako domknięcie, np. (Listing 5-44):
 class PojecieTagLibTests extends TagLibUnitTestCase {
void testRepeat() {
tagLib.wyswietl(napis: 'Tekst do wyświetlenia') {
'Treść znacznika: ${it}'
}
}
assertEquals 'Treść znacznika: Tekst do wyświetlenia', tagLib.out.toString()
}
Atrybut out to wyjście znacznika, więc sprawdzenie go z oczekiwanym przez nas wynikiem to sprawdzenie jego poprawnego działania.

Tyle w związku ze znacznikami GSP, co i tak stanowi niezłą lekturę samą w sobie (a miało być relacją, a jest raczej streszczeniem). Kolejny aspekt GSP to budowa stron z użyciem szablonów i układów.

Szablon GSP jest (wbrew temu co nazwa mogłaby sugerować) specjalnym plikiem GSP, który zawiera jedynie fragment strony. Jego celem jest wyniesienie pewnych elementów strony, do ponownego użycia w innych. Jest to po prostu umieszczenie treści w innym pliku, przez co nasza aplikacja zyskuje na elastyczności. Specjalność szablonu GSP ujawnia się jedynie po nazwie pliku, który musi rozpoczynać się od podkreślnika (_). Za pomocą znacznika g:render wyrysowujemy szablon, który wskazujemy przez atrybut template. Nazwa szablonu jako wartość atrybutu template nie może zawierać podkreślnika, np.
 <g:render template="/pojecie/listaPojec" />
odpowiada szablonowi w pliku grails-app/views/pojecie/_listaPojec.gsp.
Określenie modelu do wykorzystania w szablonie możliwe jest przez atrybut model, który przyjmuje mapę. W książce autorzy zaprezentowali szablon do wyświetlenia listy artykułów i wykorzystali go do wyświetlenia 5-ciu najlepiej sprzedających się albumów, piosenek i artystów, ale można ten sam szablon wykorzystać do wyświetlenia dowolnej ich listy. Niekwestionowany zysk.

Właśnie dzisiaj wyszedł Grails 1.1, co z Groovy 1.6 daje solidne podstawy do budowania naszych dynamicznych aplikacji webowych. Nie zapomnijcie wykonać aktualizacji.

Dodatkowo mam do wręczenia pojedynczą wejściówkę na GeeCONa w Krakowie w dniach 7-8 maja. Będę tam prezentował Apache OpenEJB i nowe funkcjonalności EJB 3.1 i już dumam jakby połączyć go z Grails, albo przynajmniej z Groovy ;-) Tak czy owak, muszę znaleźć sposób, aby wskazać tego szczęśliwca, który otrzyma wejściówkę jako czytelnik Notatnika (aby mógł chociażby zobaczyć mnie w akcji). Pomysły?

2 komentarze:

  1. proponuję wręczyć wejściówkę autorowi drugiego komentarza do tego wpisu ;)

    OdpowiedzUsuń
  2. Wejściówka, a właściwie kilka wejściówek, będzie losowana we wtorek na spotkaniu WJUGa - 31.03.

    OdpowiedzUsuń