Usuwanie komunikatów systemowych

W jsf istnieje prosty sposób implementacji komunikatów w systemie. Czasami się zdarza, że pewna metoda tworzy w swoim ciele komunikat dla użytkownika a w innym miejscu wywołanie tej metody nie powinno wyświetlać żadnej informacji. Utworzenie takiej samej metody, tylko że bez komunikatu jest złamaniem zasady DRY , a znowu dodanie warunków, może doprowadzić do nadmiernej komplikacji.

Prostym rozwiązaniem jest zastosowanie poniższego kodu:

FacesContext context = FacesContext.getCurrentInstance();
   Iterator<String> clients = context.getClientIdsWithMessages();
   while (clients.hasNext()) {
     String clientId = clients.next();
     Iterator<FacesMessage> messages = context.getMessages(clientId);

     while (messages.hasNext()) {
        messages.next();
        messages.remove();
     }
   }

Gdzie są pobierane message a następnie podczas iteracji można je usunąć.

Kolorowanie składni dzięki tej stronie

Inny sposób pobierania wiersza w jsf – 2

W poprzedniej notce podałem jeden sposób pobierania wierszy w jsf a dziś pora na drugi.

Do tabeli dodajemy nowy przycisk (można zastąpić ten, który już był).

<h:commandButton
   actionListener="#{myBean.doOtherAction}"
   action="done">
      <f:facet name="userId">
         <f:param name="name" value="#{user.id}"></f:param>
      </f:facet>
</h:commandButton>

Tutaj ciekawą rzeczą jest to, że definiujemy zamiast metody w akcji metodę w actionListener oraz facet “userId” do którego przypisujemy id użytkownika, który się znajduje w danym wierszu.

Metoda doOtherAction w beanie wygląda tak:
public void doOtherAction(ActionEvent actionEvent) {
   String userId = ((String) ((UIParameter) actionEvent.getComponent().getFacet("userId")).getValue());
//do sth
}

W taki sposób można pobrać wartość dla pola w obiekcie, które jest renderowane w tabeli.
Który z tych dwóch sposobów jest lepszy, trudno mi określić. Ja korzystam z nich naprzemiennie i jeśli potrzebuje całego obiektu, to wtedy korzystam z dataModelu a jeśli potrzebuje jedno pole wtedy z facetów.

pobieranie-wiersza-czesc-1

Napisane w jsf. 1 komentarz »

Pobieranie wiersza część 1

We wszystkich aplikacjach CRUD jedną z głównych czynności jest pobieranie konkretnego wiersza z listy i coś z nim robienie. Zwłaszcza, gdy ma się pewne złe nawyki z autorskiego web-frameworku (wiele rzeczy było w nim fajnych i w tym czasie gdy system powstawał to było duże ułatwienie zamiast czystego jsp i servletów), to na początku trudno się przestawić do sposobów pobierania wiersza w jsf.
Na szczęście jsf udostępnia prosty sposób pobierania wiersza z danej kolumny. Jak wiadomo znacznik tworzy tabelę na stronie iterując po wszystkich elementach listy, chociaż lepszy jest znacznik z rozszerzenia jsf – tomahawka. Tomahawk obsługuje więcej typów, między innymi Set.

<h:dataTable value='#{myBean.users}' var='user'>
 <h:column>
  <f:facet name="header">
    <h:outputText value="Name" />
   </f:facet>
  <h:outputText value="#{user.name}"/>
  </h:column>
  <h:column>
  <f:facet name="header">
    <h:outputText value="Surname" />
   </f:facet>
   <h:outputText value="#{user.surname}"/>
 </h:column>
  <h:column>
   <f:facet name="header">
    <h:outputText value="Action" />
  </f:facet>
   <h:commandButton value="Do sth" action="myBean.doSomething"/>
 </h:column>
<h:dataTable>

Jak na razie sprawa jest prosta. Pobieramy dane i iterujemy po nich tworząc tabelę z imieniem i nazwiskiem oraz jakimś przyciskiem. value=’#{myBean.users}’ odwołuje się do metody getUsers w beanie i przypisuje pojedynczy element listy do zmiennej user.

Nasza metoda może wyglądać tak:
private DataModel userModel;
//getters and setters
public DataModel getUsers() {
   UserService userService = new UserImpl();
   List<User> users= userService .getAllUsers();
   userModel = new ListDataModel(users);
   return userModel ;
}

A metoda doSomething tak:
public String doSomething(){
   User user = (User)userModel.getRow();
    //some action
   return "done";
}

Metoda pobierająca użytkowników wygląda całkiem zwyczajnie do tej linijki
userModel = new ListDataModel(users)
Co się tutaj dzieje i czym jest userModel a właściwie DataModel. DataModel jest abstrakcją, która na podstawie różnych źródeł danych, wspiera ich przetwarzanie w wierszach. Tutaj tworzymy nowy DataModel a właściwie ListDataModel z listy użytkowników, i zwracamy.
Dopiero cała magia dokonuje się w metodzie doSomething(), a dokładniej w linijce
User user = (User)userModel.getRow();
Metoda getRow() w automagiczny sposób zwraca nam wybrany obiekt, poprzez kliknięcie przycisku w wybranym wierszu a my tylko rzutujemy go na naszego Usera, i już można wykonać jakieś czynności na naszym użytkowniku.

inny-sposob-pobierania-wiersza-w-2

Porządki w faces-config

Pierwszy raz jak zobaczyłem w jaki sposób jest zorganizowana nawigacja w jsf bardzo mi się to spodobało. Moja radość z tego powodu trwała do momentu gdy pojawiło się trochę więcej stron, które korzystały w większości z tych samych reguł nawigacyjnych. Rozwiązując test na javablackbelt pojawiło mi się pytanie związane z tym problemem. Nie znałem odpowiedzi ale zaznaczyłem taką odpowiedź, którą bym chciał aby była prawdziwa. I tak się stało.


<navigation-rule>
  <from-view-id>*</from-view-id>>
  <navigation-case>
    <from-outcome>something</from-outcome>>
    <to-view-id>/page.jsp</to-view-id>>
  </navigation-case>
</navigation-rule>

Poprsostu genialne rozwiązanie. Gwiazdka zastępuje nam dowolną stronę czyli mamy globalną nawigację dla wszystkich stron, a pozostało tylko obsłużenie specyficznych zachowań strony.

A może ktoś zna sposób jak jeszscze można zrefaktoryzowac faces-config?

Załączanie stron w jsf

Chyba każdy z programistów zna zasadę, a conajmniej o niej słyszał, DRY (Don’t Repeat Yourself). Bardzo ogólnie polega ona na unikaniu powtórzeń kodu. W dobrze zaprojektowanych i utrzymywanych aplikacjach javowych nie jest trudno jej przestrzegać. Im program staje się bardziej skomplikowany, więcej osób go pisze tym bardziej przestrzeganie tej zasady staje się trudniejsze, ale to nie o tym jest ta bajka.

Trochę trudniejsza sprawa jest z tworzeniem stron jsp (a właściwie jsf, o czym będe pisał). O ile jeszcze jakieś statyczne elementy jak stopka czy nagłówek moża łatwo wyodrębnić, to może pojawić się problem z listami, tabelkami, formatkami, które są w wielu miejsach takie same bądź rożnią się nieznacznie od siebie. Chyba najlepszym rozwiązaniem jest includowanie części podstron do docelowej strony.

JSF w standardzie udostępnia nam kontrolkę . Próbowałem z niej korzystać i wszystko było dobrze do momentu aż CSS został dodany do strony. Załączana strona nie widziała arkuszu stylów. Kilka sposobów, które znalazłem niestety nie zapewniły mi powodzenia. Rozwiązaniem na którym się wzorowałem można znaleźć tutaj.
Tworzymy cześć naszej strony w odzielnym pliku i zapisujemy jako jsp. Taki plik może mieć w sobie załączone kolejne fragmenty stron. A następnie dołączamy taką stronę poprzez

<h:panelGroup>
 <jsp:include page="includedPage.jsp" />
</h:panelGroup>

To rozwiązanie sprawiło, że CSS był widoczny dla komponentów na dołączonej stronie oraz dodatkowo w kodzie było mniej śmieci niż subview generuje.
Ważna sprawą jest dołaczenie dyrektyw jsf do podstron(jeśli korzystają one z backing beanów), bo w przeciwnym razie można dostać wyjątek o niemożliwości sparsowania strony. Oraz uważać aby id komponentów się nie powtarzały.

Wymagany checkbox w jsf (required checkbox in jsf)

Kontrolki w jsf posiadają bardzo ciekawy atrybut a mianowicie “required”. Ustawienie jego na true zapewnia, że submit nie zostanie zatwierdzony dopóki, wymagane pole nie będzie uzupełnione. I samemu nie trzeba tego oprogramowywać.

Jednak dla wymaganych checkboxów pojawia się problem. Atrybut required sprawdza czy dany komponent nie jest nullem a nie zaznaczony checkbox ma wartość false. Która równocześnie jest wartością domyślną dla komponentów. Ale i z tym problemem można się uporać, pisząc własny walidator dla checkboxów w backing beanie.

Na stronie jsp dla checkboxa dodajemy wskazanie na metodę walidującą w beanie i ustawiamy atrybut required na true.
<h:selectBooleanCheckbox id="accept"
   validator="#{RegistryBean.validateCheckbox}"
   value="#{RegistryBean.accept}" required="true">
</h:selectBooleanCheckbox>

Natomiast metoda sprawdzająca czy checkbox jest wymagany wygląda następująco:
public void validateCheckbox(FacesContext context, UIComponent component, Object value) {
  if (value instanceof Boolean && ((Boolean) value).equals(Boolean.FALSE)) {
    FacesMessage message = new FacesMessage("You must choose checkbox");
    throw new ValidatorException(message);
  }
}

I tutaj walidator sprawdza czy nasz checkbox jest nie zaznaczony. Jeśli użytkownik nie zaznaczył checkboxa zostaje rzucony wyjątek ValidatorException, który pokazuje użytkownikowi, że dane pole jest wymagane za pomocą przyjaznego komunikatu.

Follow

Otrzymuj każdy nowy wpis na swoją skrzynkę e-mail.