Lipiec
30
2009

Lista encji z podglądem i edycją – widok

Słowa kluczowe: , , , | Kategorie: Seam Framework
No Gravatar

Poprzednio omówiłem budowę komponentu służącego do zarządzania listą encji oraz ich podglądem i edycją. Tym razem zajmę się plikiem XHTML, czyli widokiem.

Zacznę od przygotowania standardowej strony, na której znajdą się kolejne elementy:


<!DOCTYPE composition
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:s="http://jboss.com/products/seam/taglib"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:rich="http://richfaces.org/rich"
    xmlns:a4j="http://richfaces.org/a4j"
    template="/layout/template.xhtml">
  <ui:define name="body">
  <!-- TUTAJ ZAWARTOŚĆ -->
  </ui:define>
</ui:composition>

Lista encji

Głównym elementem strony będzie lista użytkowników. Będzie to tabela zawierająca login i role użytkownika (linie 12–26) oraz odnośniki edycji i podglądu (30–46). Pod tabelą znajdzie się przycisk utworzenia nowego użytkownika (57–63).


<h:form styleClass="results">
  <rich:dataTable
      id="usersList" var="_user"
      rowKeyVar="_index" rows="10"
      columnClasses="center" reRender="scroller"
      value="#{users.list}">

    <f:facet name="header">
      Konta użytkowników
    </f:facet>

    <rich:column
        sortBy="#{_user.login}"
        filterBy="#{_user.login}"
        filterEvent="onkeyup">
      <f:facet name="header">Login</f:facet>
      <h:outputText value="#{_user.login}"/>
    </rich:column>

    <rich:column>
      <f:facet name="header">Role</f:facet>
      <rich:dataList
          var="_role" value="#{_user.roles}">
        <h:outputText value="#{_role.name}"/>
      </rich:dataList>
    </rich:column>

    <rich:column styleClass="action">
      <f:facet name="header"></f:facet>
      <a4j:commandLink
          ajaxSingle="true" id="edit" value="Edycja"
          oncomplete="#{rich:component('editPanel')}.show()"
          reRender="editPanel">
        <f:setPropertyActionListener
            value="#{_user}"
            target="#{users.selectedInstance}" />
      </a4j:commandLink>
      #{' '}
      <a4j:commandLink
          ajaxSingle="true" id="view" value="Szczegóły"
          oncomplete="#{rich:component('viewPanel')}.show()"
          reRender="viewPanel">
        <f:setPropertyActionListener
            value="#{_user}"
            target="#{users.selectedInstance}" />
      </a4j:commandLink>
    </rich:column>

    <f:facet name="footer">
      <rich:datascroller
          id="scroller" for="usersList" />
    </f:facet>

  </rich:dataTable>

  <div class="actionButtons">
    <a4j:commandButton
        ajaxSingle="true" id="create"
        value="Nowy użytkownik"
        action="#{users.createInstance}"
        oncomplete="#{rich:component('editPanel')}.show()"
        reRender="editPanel">
    </a4j:commandButton>
  </div>
</h:form>

Podczas otwierania podglądu lub edycji użytkownika, należy ustawić odpowiednią encję jako wybraną. W tym celu użyty został znacznik f:setPropertyActionListener, który przenosi użytkownika z danego rekordu (_user) do wybranej instancji (selectedInstance).

Po wywołaniu odnośnika dochodzi do wyświetlenia odpowiedniego panelu (np. oncomplete="#{rich:component('viewPanel')}.show()") oraz jego odświeżenie (np. reRender="viewPanel"). Oba panele zostaną zdefiniowane w dalszej części wpisu.

W tabeli użyte zostały przydatne elementy, jak sortowanie (linijka 13) i filtrowanie (14–15) po polu login oraz stronicowanie (znacznik rich:datascroller w stopce tabeli). Na szczególną uwagę zasługuje fakt, iż nie potrzebują one żadnych dodatkowych zabiegów ze strony programisty.

Panel podglądu

Podgląd danych użytkownika realizowany jest poprzez panel modalny, czyli blokujący jakiekolwiek działania na stronie przed jego zamknięciem. Na panelu znajdują się informacje dotyczące użytkownika (linie 10–21) oraz przycisk zamykający (23–28):


<rich:modalPanel
    id="viewPanel" moveable="false"
    autosized="true" width="450">
  <f:facet name="header">
    Konto użytkownika
    #{users.selectedInstance.login}
  </f:facet>
  <f:facet name="controls"/>
  <h:form>
    <a4j:outputPanel ajaxRendered="true">
      <s:decorate
          template="/layout/display.xhtml">
        <ui:define name="label">
          Login
        </ui:define>
        <h:outputText
            value="#{users.selectedInstance.login}"/>
      </s:decorate>

      <div style="clear:both"/>
    </a4j:outputPanel>

    <a4j:commandButton
        value="Zamknij"
        oncomplete="
          if (#{facesContext.maximumSeverity == null})
          #{rich:component('viewPanel')}.hide();">
    </a4j:commandButton>
  </h:form>
</rich:modalPanel>

Panel edycji

Edycja użytkownika odbywa się analogicznie do podglądu. Na panelu znajdują się komponenty służące do edycji poszczególnych pól (linie 12–27) oraz przyciski potwierdzenia (29–34) i anulowania (35–39) zmian:


<rich:modalPanel
    id="editPanel" moveable="false"
    autosized="true" width="450">
  <f:facet name="header">
    #{users.newInstance ?
    'Tworzenie nowego konta użytkownika.' :
    'Edycja konta użytkownika '.
    concat(users.selectedInstance.login)}
  </f:facet>
  <f:facet name="controls" />
  <h:form>
    <a4j:outputPanel ajaxRendered="true">
      <s:decorate
          id="loginField"
          template="/layout/edit.xhtml"
          rendered="#{users.newInstance}">
        <ui:define name="label">Login</ui:define>
        <h:inputText
            id="login" required="true"
            value="#{users.selectedInstance.login}"/>
      </s:decorate>

      <div style="clear:both">
        <span class="required">*</span>
        pola wymagane
      </div>
    </a4j:outputPanel>

    <a4j:commandButton
        value="Zapisz" action="#{users.save}"
        reRender="usersList" id="save"
        oncomplete="
          if (#{facesContext.maximumSeverity == null})
          #{rich:component('editPanel')}.hide();"/>
    <s:button
        value="Anuluj"
        onclick="
          if (#{facesContext.maximumSeverity == null})
          #{rich:component('editPanel')}.hide();"/>
  </h:form>
</rich:modalPanel>

Ogólnie zawartość paneli nie odbiega zbytnio od zawartości wygenerowanych przez seam-gen czy JBoss Tools stron podglądu i edycji encji. Jednak należy zwrócić szczególną uwagę na zamknięcie panelu modalnego po zakończeniu operacji (np. #{rich:component('editPanel')}.hide();) oraz odświeżenie tabeli z danymi po dokonaniu zmian (reRender="usersList").

Jednak zamykanie panelu odbywa się w dość intrygujący sposób:

oncomplete="

  if (#{facesContext.maximumSeverity == null})
    #{rich:component('editPanel')}.hide();"

Warunek zakłada że właściwość maximumSeverity komponentu facesMessages (maksymalna ważkość komunikatu) będzie pusta. Oznacza to, że wywołanie akcji (np. users.save) nie spowoduje ustawienia jakichkolwiek komunikatów (informacji, ostrzeżeń czy też błędów). Taka blokada jest konieczna w sytuacji gdy wewnątrz akcji sprawdzana jest poprawność wpisanych do formularza danych, kiedy to może pojawić się błąd.

Poza przyciskiem zapisania zmian (29–34) linijka if (#{facesContext.maximumSeverity == null}) jest niepotrzebna. Jednak postanowiłem ją dodać – na wypadek późniejszych zmian, wprowadzających konieczność zastosowania tego warunku.

Podsumowanie

Omówiony przykład jasno pokazuje, że można obsłużyć wszystkie operacje na encjach za pomocą jednego komponentu stanowego oraz jednego widoku. Łatwo wyobrazić sobie rozszerzenie funkcjonalności o usuwanie użytkowników, przypisanie roli za pomocą jednego przycisku, czy inne podobne operacje.

Przy najbliższej okazji pokażę, jak połączyć dwie takie konwersacje za pomocą wstrzykiwania zależności, aby druga zawierała dane szczegółowe dla elementu wybranego w pierwszej (ang. master-detail).

Napisz Komentarz

*