JSF 2.0: renderizar campos de formulário com dados iniciais usando feijões escondidos para solicitação (para atualizar os dados do servidor específicos para o componente atual)

StackOverflow https://stackoverflow.com/questions/3812685

Pergunta

Um caso bastante normal:

Temos um componente composto de 'portlet' que possui dois estados: expandido e desmoronado. Os portlets começam como expandidos, mas o usuário pode colapsá -los clicando neles. Esse estado deve ser salvo na sessão, para que, uma vez que o usuário atualize a página ou navegue para um novo, os portlets lembrem se são expandidos/desmoronados.

Este é o núcleo do problema: Como você realmente implementaria esse estado em um componente, que pode ser inserido na página várias vezes?

Alguns antecedentes / idéias:

A implementação de economia de estado para um portlet seria facilmente alcançada usando um feijão escopo de sessão. Também o número fixo de portlets pode ser suportado pela criação de um número fixo de feijões escondidos por sessão ou declarando propriedades diferentes em um feijão. No entanto, não quero nem mencionar por que essas abordagens são ruins.

Minha idéia inicial para isso foi criar um mapa (ou alguma estrutura de objeto) em uma única sessão para manter os estados para todos os portlets. No entanto, não posso me referir diretamente a esses dados do JSF (para que o JSF também os atualizasse). Eu pensei em escrever um getter/setter específico para buscar/atualizar o valor certo no mapa, mas isso não é possível, pois não há dados de identificação durante a execução de getters e setters.

No entanto, provavelmente está claro que eu preciso salvar os estados em um feijão de sessão. Eu posso fazer o estado com bastante facilidade, postando um formulário com f:ajax e executar um método especial usando listener e os dados enviados. Para suportar várias instâncias de componente, eu poderia usar (várias instâncias de) um feijão escondido para lidar com cada expansão/colapso. No entanto, para realmente publicar os dados que identificam o portlet e seu estado, preciso primeiro inseri-los nos campos de formulário em tempo de renderização.

Então, como realmente fornecer a cada portlet dados corretos sobre o tempo de renderização (na verdade, basta estaduar boolean/enum nesse caso, mas considere um caso em que mais dados devem ser tratados)?

Parece que:

  • h:inputHidden e h:inputText Não suporta definir o valor inicial diferente de não ser o valor-atributo (que apontaria para #{portletBean.portletState}).
  • Não posso carregar automaticamente o feijão da solicitação com os valores iniciais corretos em seu tempo de criação, pois não há nenhuma informação de identificação disponível.

Mais uma vez, parece que estou perdendo alguma coisa ... ponteiros?

Suponho que pelo menos uma das respostas seria usar UIComponent Em vez de componentes compostos, mas eu gostaria de manter isso como componente composto, pois contém muitos elementos HTML brutos.

Atualizar:

  • Existe um pergunta bastante semelhante, com sugestão para usar o feijão escopo de visualização. Eu não acho que isso seja viável aqui, no entanto.
Foi útil?

Solução 3

Eu usei um Hack previamente resolvido para chamar um método de feijão de JS:

Criou uma forma vazia e invisível, com h:commandButton e f:ajax dentro dele chamando listener.

Criou um botão separado para iniciar o evento de expansão/colapso. A partir desse botão, inicie o método JavaScript, que anima a expansão/colapso e, ao mesmo tempo, clica no botão oculto dentro do formulário. Todos os dados necessários são enviados usando o listener atributo. O ouvinte tem como alvo um feijão de sessão, que verifica/atualiza um mapa dos estados, identificado pelos IDs do cliente do componente.

Isso é bem simples, me pergunto por que eu não percebi que o Eariler ...

No entanto, ainda está longe de ser perfeito e não responde como o feijão escondido por solicitações pode ser iniciado com valores padrão para usá-los como formulário de armazenamento/transporte.

Outras dicas

Eu tenho alguns hacks possíveis para isso. Vou postá -los aqui, mas não acho que esse seja realmente o caminho a seguir:

Use JavaScript para definir campos de formulário com os dados certos diretamente após a renderização:

window.addEvent('domready', function() {
    var portletState = '#{portletBean.getPortletState(cc.attrs.clientId)}';
    // find the field and set the data...
});

Parece Veery Hacky. Eu não quero seguir esse caminho.

Use o mapa de atributos do componente para manter os dados:

<cc:interface>
    <cc:attribute name="portletState" default="#{portletBean.getPortletState(cc.attrs.clientId)}" />
</cc:interface>

Acesse isso no código:

public void stateChangedCallback(String clientId) {
    UIComponent component = FacesContext.getCurrentInstance().getViewRoot().findComponent(clientId);
    String state = (String) component.getAttributes().get("portletState");
}

Também não parece certo. Além disso, não tenho certeza se funciona (você pode realmente usar o EL no atributo padrão).

Use o mapa de atributo do componente Para manter os dados fornecendo -os ao chamar o componente:

<portlet:portlet id="specificPortlet">
    <f:attribute name="portletState" value="#{portletBean.getPortletState(component.clientId)}" />
</portlet:portlet>

Novamente, muito desajeitado e duplica seu código.

Você pode fazer isso usando taglibs, você pode passar parâmetros em tags:

Adicione o seguinte ao seu taglib:

<tag>
    <tag-name>date</tag-name>
    <source>components/date.xhtml</source>
</tag>

Então aqui está o conteúdo de componentes/date.xhtml

<ui:composition name="date_template">
    <h:panelGrid id="#{id}Panel" columns="1" border="0" cellpadding="0" cellspacing="0">
        <rich:calendar immediate="true" id="#{id}" popup="true" datePattern="dd.MM.yyyy"
                        enableManualInput="true" showApplyButton="true" cellWidth="12px" cellHeight="11px" style="width:100px"
                        value="#{dateForm.date}" required="#{required}" readonly="#{readonly}" validator="#{dateForm.validateDate}">
            <a4j:support event="onchanged" reRender="#{id}Panel,#{reRenderDate}"/>
            <ui:insert />
        </rich:calendar>

        <rich:message for="#{id}" errorClass="error" />
    </h:panelGrid>
</ui:composition>

E então você usa isso como:

<adse:date id="dateTransfert" required="true"
           dateForm="#{dossierBean.dateTransfert}"
           readonly="false" reRenderDate="montantAncien,montantNouveau,montantEmolument">
     <a4j:support event="oninputblur" reRender="dateTransfertPanel,montantAncien,montantNouveau,montantEmolument"/>
</adse:date>

Nesse caso, o DateForm é passado, este é um objeto e, no taglib, usamos propriedades desse objeto (DateForm.date). Observe também ou003Cui:insert /> que pode ser usado para incluir outros elementos no XHTML.

Portanto, você pode passar no contexto de cada portlet. Você pode até fazer esse padrão com o PortletBean como uma superclasse que define o método getportletState ().

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top