W poprzednim poście przedstawiłem sposób na szybkie i efektywne tworzenie aplikacji webowych dzięki tandemowi GWT i MVP4G. Jednak arsenał wspomagający jest o wiele większy. Kolejną rzeczą, którą chętnie by widział developer są generyczne operacje na kolekcjach. Parę miesięcy temu miałem okazję skorzystać z LambdaJ przy okazji poprzedniego projektu, chciałem skorzystać z tej biblioteki dla celów obecnego projektu opartego właśnie na GWT i MVP4G (oraz MyBatis). Niestety, spotkało mnie przykre rozczarowanie - LambaJ korzysta z aop'a i refleksji, co skutecznie uniemożliwia współpracę z GWT - na co również wskazuje JRE Emulation Reference. Jakakolwiek próba użycia LambdaJ kończyła się wyjątkami. To zachęciło mnie to do zapoznania się z niejako zamiennikiem dla LambdaJ - z bibliotekami Google GUAVA.
Potrzebne narzędzia/biblioteki :
- z poprzedniego wpisu : Eclipse z pluginem GWT, MVP4G oraz GIN
- najlepiej skorzystać z projektu Eclipsa z poprzedniego wpisu
- biblioteki GUAVA
Zaczynamy zatem! Ze strony głównej projektu ściągamy interesujące nas JAR'y : guava-11.0.1.jar oraz guava-gwt-11.0.1.jar i umieszczamy je w podkatalogu lib. Teraz już posiadamy całą infrastrukturę, kolejnym krokiem jest modyfikacja deskryptora projektu - *.gwt.xml. Dla ułatwienia pokażę cały plik gdyż nie jest on i tak długi, podświetlone są 2 istotne linie kodu :
<?xml version="1.0" encoding="UTF-8"?> <module rename-to='tutek_mvp4g'> <!-- Inherit the core Web Toolkit stuff. --> <inherits name='com.google.gwt.user.User'/> <!-- Inherit the default GWT style sheet. You can change --> <!-- the theme of your GWT application by uncommenting --> <!-- any one of the following lines. --> <inherits name='com.google.gwt.user.theme.clean.Clean'/> <!-- <inherits name='com.google.gwt.user.theme.standard.Standard'/> --> <!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> --> <!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/> --> <!-- Other module inherits --> <inherits name='com.mvp4g.Mvp4gModule' /> <inherits name="com.google.common.collect.Collect" /> <inherits name="com.google.common.base.Base"/> <!-- Specify the app entry point class. --> <!-- <entry-point class='tutek.client.Tutek_MVP4G'/> --> <entry-point class='com.mvp4g.client.Mvp4gEntryPoint'/> <!-- Specify the paths for translatable code --> <source path='client'/> <source path='shared'/> </module>
Podświetlone linie importują potrzebne moduły biblioteki GUAVA. Kolejnym krokiem jest modyfikacja eventBusa. Dodamy jedno zdarzenie - wybieranie wierszy po przez wartość jednej z kolumn. Definicja interfejsu eventBus nie jest zbyt obszerna więc ponownie zamieszczę cały kod zaznaczając istotne linie :
package tutek.client;
import tutek.client.bean.Person;
import tutek.client.presenter.TutekPresenter;
import com.google.gwt.view.client.ListDataProvider;
import com.mvp4g.client.annotation.Event;
import com.mvp4g.client.annotation.Events;
import com.mvp4g.client.annotation.Start;
import com.mvp4g.client.event.EventBus;
@Events(startPresenter = TutekPresenter.class)
public interface TutekEventBus extends EventBus
{
@Start //obowiązkowe zdarzenie startowe
@Event(handlers = {TutekPresenter.class})
public void onStart();
//zdarzenie wybierania osób po imieniu
@Event(handlers = {TutekPresenter.class})
public void selectByName(String name, ListDataProvider<Person> data);
}
Zdarzenie "selectByName" jako parametr przyjmuje imię (name) wg. którego należy przefiltrować zawartość (data) tabelki. Jego obsługa jest zdefiniowana w klasie presentera - pojawi się nowa metoda o nazwie "onSelectByName" - nazwa zgodna z konwencją omówioną w poprzednim wpisie. Bezpośrednio w metodzie obsługującej zdarzenie modyfikujemy listę obiektów do wyświetlenia - przypomina to trochę funkcje statyczne. Klasa reprezentująca dane wyświetlane w tabeli - "Person" oraz klasa widoku ("TutekView") pozostają bez zmian w stosunku do poprzedniego wpisu. Zmienia się natomiast wnętrze klasy presentera :
@Override
public void bind()
{
GWT.log("bind");
//tworzymy kolumny
TextColumn<Person> name = new TextColumn<Person>()
{
@Override
public String getValue(Person object)
{
return object.name;
}
};
TextColumn<person> surName = new TextColumn<Person>()
{
@Override
public String getValue(Person object)
{
return object.surName;
}
};
//dodajemy kolumny
view.cellTable.addColumn(name);
view.cellTable.addColumn(surName);
//obsługa zdarzeń
view.btn_filter.addClickHandler(new ClickHandler()
{
@Override
public void onClick(ClickEvent event)
{
eventBus.selectByName("wacek", dataProvider);
}
});
}
Zmiany nastąpiły w metodzie bind(). Dokładniej zmianie uległa obsługa zdarzeń - teraz zamiast dokonywać obsługi zdarzenia w kodzie tej metody odpalamy zdarzenie dla eventBusa. Definicja tego zdarzenia zapisana w eventBusie mówi, że w prezenterze pojawi się nowa metoda, obsługująca zdarzenie będzie się nazywała "onSelectByName", zatem przyjrzujmy się jej :
public void onSelectByName(final String name, ListDataProvider<Person> data)
{
//Obiekt definiujący warunek
Predicate<Person> nameCond = new Predicate<Person>()
{
//nadpisana metoda szczegułowo definiujaca warunek filtrowania
@Override
public boolean apply(Person arg0)
{
return arg0.name.toLowerCase().equals(name);
}
};
data.setList( Lists.newArrayList( filter(data.getList(), nameCond) ) );
}
To co jest ważne podczas filtrowania listy : zdefiniowanie predykatu - czyli warunku (pierwsze podświetlenie) następnie przefiltrowanie. Czujni zauważyli na pewno, że filtrowanie za pomocą funkcji "filter" wygląda tak jak by owa metoda została zdefiniowana w prezenterze - jednak to nie tak. Dzięki statycznemu importowi :
import static com.google.common.collect.Collections2.filter;
Mozna używać funkcji "filter" z klasy "Collections2" tak jak by była zdefiniowana w naszej klasie. To wszystko - magia działa. Funkcja statyczna "filter" zwraca nową kolekcję, którą podstawiamy dla dataProvidera dostarczonego jako parametr - nie musieliśmy tego robić, jednak w moim odczuciu zwiększa to czytelność kodu. To wszystko.... Chyba nie - gdy wykorzystalem bibliotekę GUAVA w większym projekcie podczas kompilacji GWT (generowanie kodu js) dostalem nieoczekiwanie błędy, co najdziwniejsze, w bibliotece GUAVA.
[ERROR]
Errors in 'jar:file:/C:/.../lib/guava-gwt-10.0.1.jar!/com/google/common/collect/BstBalancePolicy.java'
[ERROR] Line 19: The import javax.annotation.Nullable cannot be resolved
[ERROR] Line 36: Nullable cannot be resolved to a type
[ERROR] Line 36: Nullable cannot be resolved to a type
[ERROR] Line 43: Nullable cannot be resolved to a type
[ERROR] Line 44: Nullable cannot be resolved to a type
[ERROR] Line 44: Nullable cannot be resolved to a type
Parę pytań do dr Google dało odpowiedź że brakuje biblioteki jsr305, uzupełniłem. Jednak to nadal nie dało rezultatu i błąd nadal się pojawiał. Dłuższe poszukiwania rozwiązania nie dały rezultatu aż w desperacji zacząłem próbować starszych wersji biblioteki GUAVA. Błąd pojawiał się również podczas używania wersji 10. Kolejna próba z wersją GUAVA 09 dała pozytywny rezulta - kompilacja GWT przebiegała bez błędów. Ciekawym jest fakt, iż błąd ujawniał się wewnątrz biblioteki, dodatkowo podczas zastosowania w większym projekcie. Zastosowanie zestawu GWT + MVP4G daje niezwykłą łatwość i porządek podczas tworzenia aplikacji. Dodanie do tego tandemu bibliotek GUAVA ułatwia manipulacje na danych (nie tylko!!) i dodatkowo zwiększa czytelność kod. Na koniec podaję link do gotowego projektu w Eclipse spakowanego rarem. Brakuje jedynie refleksji. Ale o tym już niebawem!
Brak komentarzy:
Prześlij komentarz