JSF - Tworzenie stron kilka pytań

0

Witam.

Mam kilka pytań na temat tworzenia stron w jsf:

  1. Parsowanie adresów/front controller.
    Czy istnieje jakiś gotowiec pozwalający na ładne przetwarzanie i generowanie adresów? Mam tu na myśli rozwiązanie podobne do znanego z Zend Framework. Gdzie adresy wyglądają np jak www.mysite.pl/controller/action/paramname1/paramvalue1/paramname2/paramvalue2

  2. Zmiana kolekcji podczas przetwarzania strony. Czy w momencie gdy zostanie wywołana jakaś akcja np delete, która usunie jeden z elementów kolekcji jest możliwe poprawne wyświetlenie listy elementów bez wykonania redirect-a?

<ui:composition
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:c="http://java.sun.com/jsp/jstl/core"
    xmlns:p="http://jspf.pozoga.eu/jstl">

    <!--
        modelManager - modelmanager for interface
    -->
    <c:set var="_editObjectAccessor" value="#{collectionAccessor.accessTo('modelManagerEditObject', modelManager.entityClass)}"/>


    <c:set var="fields" value="#{modelManager.listingFields}" />
    <h:form>
        <table class="listing">
            <!--    Fields Names    -->
            <tr>
            <c:forEach var="field" items="#{fields}">
                <th>#{msg[field]}</th>
            </c:forEach>
            </tr>
            <!--    Values      -->
            <c:forEach var="record" items="#{modelManager.list}">
                <tr>
                <c:forEach var="field" items="#{fields}">
                    <td>#{record[field]}</td>
                </c:forEach>
                <!-- Controll Buttons -->
                <td><h:commandButton value="#{msg.edit}" action="#{_editObjectAccessor.setIdLoad(record.id)}" /></td>
                <td><h:commandButton value="#{msg.delete}" action="#{record.deleteAction()}" /></td>
                </tr>
            </c:forEach>
        </table>
    </h:form>


    <!--#{_editObjectAccessor.formFields}-->


    <h:form rendered="#{_editObjectAccessor.value!=null}" >
        <p:formComposition>
            <h:inputHidden value="#{_editObjectAccessor.idLoad}" />
            <p:formInputEntity when="update" target="#{_editObjectAccessor}" property="createValue" valueClass="#{modelManager.entityClass}"/>
            <p:formSection>
                <h:commandButton value="#{msg.save}" action="#{_editObjectAccessor.value.updateAction()}" />
                <h:commandButton immediate="true" value="#{msg.cancel}" action="#{_editObjectAccessor.remove()}" />
            </p:formSection>
        </p:formComposition>
    </h:form>

    <c:set var="newrecord"  value="#{collectionAccessor.accessTo('modelManagerNew', modelManager.entityClass)}"/>
    
    <p:form title="#{msg.newRecord}" rendered="#{_editObjectAccessor.value==null}">
        <p:formInputEntity when="save" target="#{newrecord}" property="createValue" valueClass="#{modelManager.entityClass}" />
        <p:formSection>
            <h:commandButton value="#{msg[ 'add' ]}" action="#{newrecord.createValue.saveAction()}" />
        </p:formSection>
    </p:form>
</ui:composition>

0
Pozoga napisał(a)
  1. Parsowanie adresów/front controller.
    Czy istnieje jakiś gotowiec pozwalający na ładne przetwarzanie i generowanie adresów? Mam tu na myśli rozwiązanie podobne do znanego z Zend Framework. Gdzie adresy wyglądają np jak www.mysite.pl/controller/action/paramname1/paramvalue1/paramname2/paramvalue2

Przypuszczam, ze chodzi o adresy URL? Chcesz zrobic cos na styl RESTful web app? Mysle, ze Spring MVC i @RequestMapping byloby tym, czego szukasz.

Pozoga napisał(a)
  1. Zmiana kolekcji podczas przetwarzania strony. Czy w momencie gdy zostanie wywołana jakaś akcja np delete, która usunie jeden z elementów kolekcji jest możliwe poprawne wyświetlenie listy elementów bez wykonania redirect-a?

Troche niejasno zadane pytanie. Jak wywolasz akcje na komponencie (np. commandButton), to JSF przeladuje strone lub zaladuje nowa (w zaleznosci od navigation-rules) i wyrenderuje zawartosc odpowiednia dla aktualnego stanu MB.

BTW: mozesz blizej opisac, co to za strategia (design pattern) w kodzie, ktory wkleiles? Na pierwszy rzut oka wyglada dziwacznie, ale bycmoze to tylko ja glupi jestem.

0
  1. do tego istnieje PrettyFaces http://ocpsoft.com/prettyfaces/ a nie zaden Spring

Mozez posluzyc sie Ajaxem, np.

<h:dataTable id="dataTable" var="item" value="#{bean.items}">
    <h:column>#{item.data1}</h:column>
   <h:column>#{item.data2}</h:column>
   <h:column>
   <h:commandButton value="delete" action="#{bean.deleteAction()}">
         <f:attribute name="item" value="#{item}"/>
         <f:ajax render="dataTable"/>
    </h:commandButton>
   </h:column>
<h:dataTable>
0

Czy ktoś może mi powiedzieć co robię nie tak. Mam kod analogiczny z powyższym. Usuwa mi rekord jednak tabela nie jest odświeżona. Aby zobaczyć wynik muszę odświeżyć całą stronę.

<h:dataTable id="dataTable" var="entity" value="#{modelManager.list}">
        <c:forEach var="property" items="#{modelManager.listingFields}">
            <h:column >
                <f:facet name="header">#{property}</f:facet>
                #{entity[property]}
            </h:column>
        </c:forEach>
        <h:column>
            <h:commandButton value="delete" action="#{entity.deleteAction()}" onclick="return confirm('Are you sure you want to delete ?');">
                <f:attribute name="entity" value="#{entity}"/>
                <f:ajax render="dataTable"/>
            </h:commandButton>
        </h:column>
</h:dataTable>
0

sprobuj moze zamiast id komponentu uzyc @form

<f:ajax render="@form"/>

W tym wypadku zostanie zrobiony update (jesli potrzeba) wszystkich komponentow ktore sa w danym formularzu.

ps.
w podanym przykladzie przeze mnie podalem:

<h:commandButton value="delete" action="#{bean.deleteAction()}">

Do konca dobrze to jeszcze nie wyglada. Nazwy funkcji pisze sie bez klamerek, i lepiej by tu pasowalo actionListener zamiast action. Funkcji bym zdefiniowal w twoim bean niz w objekcie entity :

<h:commandButton value="delete" actionListener="#{bean.deleteAction}" />

w bean:

public void deleteAction(ActionEvent e){
	HtmlCommandButton button = (HtmlCommandButton) e.getSource();
	Entity en = (Entity) button.getAttributes().get("entity");
	entityList.remove(en); // List<Entity> entityList; 
	
}

nie uzywalbym c:forEach w dataTable. DataTable jest juz od tego zeby ci twoja liste objektow tabelarycznie przestawic i c:forEach jest tu zupelnie nie potrzebne. Dodatkowo mozesz miec problemy, ktore ciezko bedzie ci wykryc, mieszajac komponenty z normalnymi tag'ami. Sam osobiscie jeszcze takiego czegos nie widzialem i nie musialem do tej pory takiego rozwiazania uzywac. Zobacz sobie np. to: http://www.ninthavenue.com.au/blog/c:foreach-vs-ui:repeat-in-facelets

0

sprobuj moze zamiast id komponentu uzyc @form
<f:ajax render="@form"/>

Niestety nie pomogło. Rozwiązałem to w inny sposób. Opakowałem data table w panelGroup i ustawiłem render=":listing".
Mam co prawda jeszcze problem z form's ale jeszcze trochę pokombiuję a jaknic nie wymyślę zapytam na forum.

Wersja bez klamerek rzeczywiście trochę lepiej wygląda.
Tak myślę jakie są przewagi action listener?

nie uzywalbym c:forEach w dataTable. DataTable jest juz od tego zeby ci twoja liste objektow tabelarycznie przestawic i c:forEach jest tu zupelnie nie potrzebne. Dodatkowo mozesz miec problemy, ktore ciezko bedzie ci wykryc, mieszajac komponenty z normalnymi tag'ami. Sam osobiscie jeszcze takiego czegos nie widzialem i nie musialem do tej pory takiego rozwiazania uzywac. Zobacz sobie np. to: http://www.ninthavenue.com.au/blog/c:foreach-vs-ui:repeat-in-facelets

Używam c:foreach do wygenerowania listy kolumn. Datatable chyba nie ma innego mechanizmu pozwalającego na to?

0

action sluzy w sumie do nawigacji. Methoda zwaraca jakis string z powrotem, Wiec zeby usunac przez ajax jeden wiersz tabeli i zostac dalej w danym widoku trzeba zwrocic np. null co oczywiscie jest mozliwe ale moim zdaniem no ladnych rozwiazan nie nalezy. actionListener jest z kolei typu void i przypadku usuwania wiersza tabeli nie musi nic zwracac. Jako parametr przyjmuje ActionEvent obiekt i mam dostep do Komponentu, ktory dana akcje wykonal.

Z dynimcznym generowanie kolumn to sie troche zapedzilem, ale to przez to ze uzywam na codzien IceFaces a tam mam ice:columns. Richfaces ma to, PrimeFaces ma to, MyFaces Tomahawk ma to tez. A tu sie okazuje ze specifikacja JSF tego nie wspiera. Ja bym podpatrzyl jak te inne frameworki to zrobily. Z c:forEach bym uwazal, na pewno bym przetestowal czy bezblednie dziala. Tzn nie tylko pokazuje dynamicznie kolumny ale wspiera lifecycle od JSF. Na razie wyswietlasz tabelarycznie sam tekst, a co jak bys chcial jakis tekst wprowadzic, zmienic? Dokana sie walidacja w 3, bean zostanie zauktualizowana w 4 i actions wykonaja sie w 5 fazie?

0

Później pomyślę nad uporządkowaniem tego kodu. Najpierw spróbuję zrobić żeby to działało ale wszystkie twoje rady w tej kwestii oczywiście będą mi pomocne :)

IceFaces brzmi dość ciekawie. Opłaca się na to przerzucić? Jest dużo lepsze od jsf?

Zgadam się że stosowanie c:forEach może być ryzykowne. W połączeniu z dataTable nie miałem pomysłu na żaden sensowny test więc mogę stwierdzić tylko że dla tekstu działa poprawnie. Wcześniej pisałem tag generujący rormularz za pomocą c:forEach i mogę powiedzieć że działa bez zarzutu. Choć miałem kilka problemów podczas pisania.

    <c:forEach var="key" items="#{system.isEmpty(when)? _inputEntityAccessor.formFields : _inputEntityAccessor.getFormFields(when)}">
            <p:input id="#{key}" type="#{_inputEntityAccessor.formFieldType}" required="#{_inputEntityAccessor.formFieldAnnotation.required()}" target="#{value}" property="#{key}" />
            <h:messages for="#{key}"/>
    </c:forEach>

Pisałem że mam problem z formularzem. No właśnie nie mam pojęcia co z tym zrobić. Kod ma

  1. Wyświetlać listę elementów.
  2. Wyświetlać formularz edycji jeśli kliknięto edit, lub jeśli nie wyświetlać formularz dodawania nowego rekordu
    Ogólnie działa to dość dziwnie czasami reaguje, czasami nie. Dodam e wersja bez ajaxa działała poprawnie.
    Dodawanie, usuwanie i wyświetlanie działają poprawnie.
<ui:composition
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:c="http://java.sun.com/jsp/jstl/core"
    xmlns:p="http://jspf.pozoga.eu/jstl">

    <!--
        Input:
            modelManager - Object implements modelManager Interface (It is used to access to database)
        Rendered:
            The element rendered linsting of database and generates user interface to  modified it.
    -->

    <!-- Helpers Accessors -->
    <c:set var="_editObjectAccessor" value="#{collectionAccessor.accessTo('modelManagerEditObject', modelManager.entityClass)}"/>

    <!--
        BODY
    -->
    <!-- Listing -->
    <h:panelGroup id="listing">
        <h:dataTable id="modelListing" var="entity" value="#{modelManager.list}">
            <c:forEach var="property" items="#{modelManager.listingFields}">
                <h:column >
                    <f:facet name="header">#{property}</f:facet>
                    #{entity[property]}
                </h:column>
            </c:forEach>
            <h:column>
                <h:commandButton value="delete" action="#{entity.deleteAction()}" onclick="return confirm('Are you sure you want to delete ?');">
                    <f:attribute name="entity" value="#{entity}"/>
                    <f:ajax render=":listing"/>
                </h:commandButton>
            </h:column>
            <h:column>
                <h:commandButton value="edit" action="#{_editObjectAccessor.setValue(entity)}">
                    <f:attribute name="entity" value="#{entity}"/>
                    <f:ajax render=":modelManagerForm"/>
                </h:commandButton>
            </h:column>
        </h:dataTable>
    </h:panelGroup>

    <!-- Forms -->
    <h:panelGroup id="modelManagerForm">
        <p:form rendered="#{_editObjectAccessor.value.id!=null}" >
            <h:inputHidden value="#{_editObjectAccessor.idLoad}" />
            <p:formInputEntity when="update" target="#{_editObjectAccessor}" property="createValue" valueClass="#{modelManager.entityClass}"/>
            <p:formSection>
                <h:commandButton value="#{msg.save}" action="#{_editObjectAccessor.value.updateAction()}">
                    <f:attribute name="editobject" value="#{_editObjectAccessor.value}" />
                    <f:ajax execute="@form" render=":modelManagerForm :listing"/>
                </h:commandButton>
                <h:commandButton immediate="true" value="#{msg.cancel}" action="#{_editObjectAccessor.remove()}" >
                    <f:attribute name="editobject" value="#{_editObjectAccessor.value}" />
                    <f:ajax execute="@form" render=":modelManagerForm"/>
                </h:commandButton>
            </p:formSection>
        </p:form>

        <!-- New Record Form -->
        <c:set var="newrecord"  value="#{collectionAccessor.accessTo('modelManagerNew', modelManager.entityClass)}"/>

        <p:form title="#{msg.newRecord}" rendered="#{_editObjectAccessor.value==null}">
            <p:formInputEntity when="save" target="#{newrecord}" property="createValue" valueClass="#{modelManager.entityClass}" />
            <p:formSection>
                <!--<h:commandButton value="#{msg[ 'add' ]}" action="#{newrecord.createValue.saveAction()}" />-->
                <h:commandButton value="#{msg[ 'add' ]}" action="#{newrecord.createValue.saveAction()}">
                    <f:attribute name="newrecord" value="#{newrecord}"/>
                    <f:ajax execute="@form" render=":modelListing :modelManagerForm"/>
                </h:commandButton>
            </p:formSection>
        </p:form>
    </h:panelGroup>
</ui:composition>

0

Icefaces to AJAX framework ktory bazuje na JSF. Posiada pare gotowych komponentow i dzieki Direct-to-Dom koncepcji ulatwia pisanie dynamicznych (z duzym naciskiem na AJAX) aplikacji. Zobacz sobie showcase: http://component-showcase.icefaces.org/component-showcase/showcase.iface
W twoim przypadku by to oznaczalo ze nie musialbys za kazdym razem definiowac f:ajax . Wystarczylo by jak zmienisz jakies wartosci w modelu, Ice faces wykryje to i automatycznie wysle ci odpowiedni update.

Wracajac to twojego formularza,
do edycji uzywaz
<f:ajax render=":modelManagerForm"/>

co tak na prawde oznacza:

<f:ajax render=":modelManagerForm" execute="@this"/>

moze by to wystarczylo jak w tym modelManagerForm nie mial bys zadnych wartosci pobieranych z twojego beana. Przez execute="@this" wykona sie lifecycle (tzw. partial lifecycle) tylko tego konkretnego komponentu, nie zostanie np. w 4 fazie uaktualniony caly model a tylko zdefiniowana przez atrybut execute jego czesc. Bez ajaxa, czyli przez normylny POST jest wykonany lifecycle dla kazdego komponentu. Sprobuj nastepujacych mozliwosci, moze pomoga ci:

<f:ajax render=":modelManagerForm" execute="@form"/>
albo
<f:ajax render=":modelManagerForm" execute="@all"/>
albo
<f:ajax render=":modelManagerForm" execute="@this :modelManagerForm"/>

1 użytkowników online, w tym zalogowanych: 0, gości: 1