views:

442

answers:

1

My goal is to dynamically load the contents of a component in JSF 2.0. The use case is this: user clicks some button, which opens a modal panel with some heavy-contents via AJAX. Due to its' heaviness, I want to delay loading it until/if user actually needs it. If the user closes this panel, it is not actually removed from DOM, just faded out. If the user clicks the initialization button again, the previously loaded panel is shown.

I know that I can prevent rendering of contents using rendered="#{cc.attrs.visibilityState == 'hidden'}" and that I can re-render the component via JSF AJAX call. However, how can I adjust the attributes of a composite component on-the-fly so that the second time around the component would actually render?

1) I know that I could do:

<h:outputLink>Foo
    <f:ajax event="click" render="theComponentIWantToUpdate" listener="#{someBean.someMethod()}" />
</h:outputLink>

And then programmatically adjust theComponentIWantToUpdate attributes (to change value of #{cc.attrs.visibilityState}) so that it would actually render with full contents. But how to actually do that?

2) Also, the problem is that I don't want to update (re-render) theComponentIWantToUpdate each time the button is pressed, only the first time (see the business case). How can I set an evaluation for <f:ajax /> call for this? It has the disabled attribute, but it only orders whether or not to actually render the AJAX-handler (not evaluated each time the link is pressed).

3) Furthermore, I probably want to do some custom javascript first when the link is clicked and only execute AJAX request via javascript using jsf.ajax.request(). However, that function doesn't support providing listener attribute so I don't how to execute a backing bean method with raw javascript jsf.ajax.request() call? There is actually a similar question without suitable answers (see JSF 2.0 AJAX: jsf.ajax.request to call method not only rerender an area of page).

A: 

A partial solution:

Here is my link that sends an AJAX-request (inside a composite component):

<h:form>
    <h:outputLink styleClass="modlet-icon">
        <f:ajax event="click" render=":#{cc.clientId}:modalWindow:root" listener="#{modalWindowBean.enableContentRendering(cc.clientId, 'modalWindow')}" />
    </h:outputLink>
</h:form>

The listener calls this method:

public class ModalWindowBean {
    ...

    public void enableContentRendering(String clientId, String windowId) {
        UIComponent component = FacesContext.getCurrentInstance().getViewRoot().findComponent(clientId + ":" + windowId);
        component.getAttributes().put("contentRenderingEnabled", true);
    }
}

Here is my target to be rendered:

<modalWindow:modalWindow id="modalWindow">
    <quickMenu:quickMenuOverlay id="quickMenuOverlay" />
</modalWindow:modalWindow>

ModalWindow simply wraps the target into a nice looking window panel. Within ModalWindow:

<composite:implementation>
    <h:outputScript library="component/modalWindow" name="modalWindow.js" target="head" />
    <h:outputStylesheet library="component/modalWindow" name="ModalWindow.css" />

    <h:panelGroup id="root" layout="block" styleClass="modalWindow hide">
        <ui:fragment rendered="#{cc.attrs.contentRenderingEnabled}">
            ...all the wrapping elements with <composite:insertChildren /> within it
            <script type="text/javascript">
                // Fade it in
                var win = ModalWindow.getInstance('#{cc.clientId}');  // this gets the instance, available everywhere
                win.position(#{cc.attrs.left}, #{cc.attrs.top});
                win.resize(#{cc.attrs.width}, #{cc.attrs.height});
                win.fadeIn();
            </script>
        </ui:fragment>
    </h:panelGroup>

    <ui:fragment rendered="#{!cc.attrs.contentRenderingEnabled}">
        <script type="text/javascript">
            // Initialize modalWindow when it is rendered for the first time
            var win = new ModalWindow('#{cc.clientId}');  // will be publicly available through ModalWindow static methods
        </script>
    </ui:fragment>

</composite:implementation>

The problem? AJAX-request is sent each time user clicks the button (so the window is reloaded and faded in each time). I need to be able to control when/if the AJAX-request is actually sent.

All this stuff makes me miss Apache Wicket, although I'm not sure how I would do this with it anyway :)

Tuukka Mustonen