0
votes

I'm developing a webapp on Liferay 6.2 with JSF Primefaces 5.3. The webapp runs on Tomcat. I have Portlet 1 and Portlet 2 on the same page. I want to set the state of some back-end object that belongs to Portlet 1 after the user interacts with controls in Portlet 2.

I tried an event-based solution, which works just fine. On my view.xhtml I have something like this:

<p:selectOneMenu value="#{operationBox.selected}"
            valueChangeListener="#{broker.operationChanged}" onchange="submit()"
            style="width:50%">
            <f:selectItems value="#{operationBox.operations}" />
        </p:selectOneMenu>

The broker class (Portlet 2) handles the action, sets a custom event in the ActionResponse object and the listener (Portlet 1) receives it.

BUT, I don't want to submit the form, because that would cause a page refresh, which I don't need. I'm not going to render anything in Portlet 1. My broker class looks just like the examples provided in Liferay tutorials and demo apps:

    public void submit() {
    logger.debug("Submitting booking changes.");

    QName qName = new QName("http://liferay.com/events", "ipc.customerEdited");
    ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
    ActionResponse actionResponse = (ActionResponse) externalContext.getResponse();
    actionResponse.setEvent(qName, bookingsModelBean.getCustomer());
}

I don't know if it is ajax or the selectOneMenu tag (or both) that fail me here: if I do the following <f:ajax event="change" listener="#{broker.handle}"/> the response object my broker bean gets is ResourceResponse, which I cannot cast to ActionResponse in order to call setEvent(QName qname, String string).

Is there a way for selectOneMenu to trigger an action on value change? Are actions only tied to submits / page reloads?

I need a way to send data to the server-side of Portlet 1 from Portlet 2. What's my best option in your opinion?

Thanks a lot

EDIT with final solution

It turns out that this cannot be done via server-side event processing with the current specs. I eventually managed to do it with client-side javascript. The following method works only if the involved portlets are on the same page: Portlet 1 - fire event:

<p:selectOneMenu value="#{operationBox.selected}"
                onchange="return myFireEvent('operation',this.value);>

and

<script>
    function myFireEvent(caller, value) {
        var payload = ...; // process your parameters
        Liferay.fire('my-event-name', {
            payload: payload
        });
        return false;
    };
</script>

Portlet 2 - receive event: The workaround I used to send data to the server side is to call a custom js function via <p:remoteCommand> tag, that you must place inside a <h:form>:

<p:remoteCommand name="handleEvent" actionListener="#{icpReceiver.handleEventJS}"/>

This is invoked by the js receiver function provided by the Liferay API:

<script>
    Liferay.on('my-event-name', function(event) {
        handleEvent([{name:'payload', value:event.payload}]);
    });
</script>

And that's all.

1

1 Answers

1
votes

You're right - JSR-286-inter-portlet-communication is covering only action (and event) phases and is available neither in the render nor in the resource phase. The spec just came out a bit too early to envision this.

You can still do ajax-based IPC, but you'll have to do this on your own - typically on the frontend, e.g. through Javascript communication between the portlets on one page. It won't be JSR-286-IPC.

A related topic (but not too helpful for the concrete situation at hand) might be Liferay's SPA (single page applications), where you don't have full page reloads any more - rather the portal processes and transmits delta states for the portlets, while you still build classic portlets.