GunnarB
GunnarB

Reputation: 33

JSF / primefaces eventing in diagram elements

i need to access the data object of an org.primefaces.model.diagram.Element in a managedBean on click of this very element. I am using primefaces 5.2. I tried multiple ways to establish this, but none did work so far. Here is one approach:

xhtml:

the idea is, to pass the element through a commandLink action attribute (which works fine for e.g. data tables):

 <fl:composition template="/WEB-INF/template.xhtml" xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:fl="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:p="http://primefaces.org/ui">
   <fl:define name="content">
      <h:form id="form">
         <p:diagram style="height:600px" value="#{flowDiagramBean.model}" var="el">
            <f:facet name="element">
               <p:commandLink actionListener="#{flowDiagramBean.onElementClicked}" update="@form" value="#{el.actionElementLabelText}">
                  <f:attribute name="element" value="#{el}"/>
               </p:commandLink>
            </f:facet>
         </p:diagram>
      </h:form>
   </fl:define>
</fl:composition>

The inital build up of the prototypes diagram:

@PostConstruct
   public void init() {
      setModel(new DefaultDiagramModel());
      getModel().setMaxConnections(-1);

      getModel().getDefaultConnectionOverlays().add(new ArrowOverlay(20, 20, 1, 1));
      FlowChartConnector connector = new FlowChartConnector();
      connector.setPaintStyle("{strokeStyle:'#C7B097',lineWidth:3}");
      getModel().setDefaultConnector(connector);

      ScreenFlowItemObj screenFlowItemObj = new ScreenFlowItemObj();
      screenFlowItemObj.setActionElementLabelText("test");

      Element start = new Element(screenFlowItemObj, "20em", "6em");
      start.addEndPoint(new BlankEndPoint(EndPointAnchor.BOTTOM));
      start.addEndPoint(new BlankEndPoint(EndPointAnchor.LEFT));

      Element trouble = new Element(screenFlowItemObj, "20em", "18em");
      trouble.addEndPoint(new BlankEndPoint(EndPointAnchor.TOP));
      trouble.addEndPoint(new BlankEndPoint(EndPointAnchor.BOTTOM));
      trouble.addEndPoint(new BlankEndPoint(EndPointAnchor.RIGHT));

      model.addElement(start);
      model.addElement(trouble);

   }

As you can see, the type ScreenFlowItemObj is the passed data object of each element.

The actionListener looks like this:

public void onElementClicked(ActionEvent event) {
      ScreenFlowItemObj screenFlowItemObj = (ScreenFlowItemObj) event.getComponent().getAttributes().get("element");
   }

Unfortunately on click of the commandLink the attribute "element" does not get put to the attribute map, so in the listener screenFlowItemObj resolves to null. Am i doing something wrong? Might there be another way to make this happen?

I am using primefaces 5.2 on a 1.7 tomee with JSF 2.2.

Thank you,

Gunnar

Upvotes: 2

Views: 4537

Answers (2)

GunnarB
GunnarB

Reputation: 33

Thanks to Kukeltje approach, i was able to implement the functional reqiurement successfully:

xhtml:

<fl:composition template="/WEB-INF/template.xhtml"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:fl="http://java.sun.com/jsf/facelets"
    xmlns:p="http://primefaces.org/ui">
    <fl:define name="content">
        <h:form id="screenFlowForm">
            <p:diagram value="#{flowDiagramBean.model}" var="el"
                style="height:600px">
                <f:facet name="element">
                    <h:outputText value="#{el.actionElementLabelText}" />
                </f:facet>
            </p:diagram>
            <p:remoteCommand name="elementClicked"
                actionListener="#{flowDiagramBean.onElementClicked}"/>
            <script type="text/javascript">
                        $(document).on('click',
                                '.ui-diagram > .ui-diagram-element',
                                function(info) {
                                    elementClicked([ {
                                        name : 'elementId',
                                        value : info.target.id
                                    } ]);
                                });
            </script>
        </h:form>
    </fl:define>
</fl:composition>

ActionListener:

public void onElementClicked() {
        logger.info(FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("elementId"));
    }

Works for me. I do not understand, why i have to pass the parameter in an one-element array, but ok - it is working.

Thank you for leading me to the right direction.

Gunnar

Upvotes: 1

Kukeltje
Kukeltje

Reputation: 12337

There is no click event on 'elements' in the jsPlumb framework that PrimeFaces uses, so an 'ajax' select event is not possible, not directly anyway.

What you could do is:

  1. Give each element an explicit ID in your model (e.g. start.setId('start'))
  2. in your page after the diagram add some code that adds event handlers to the diagram elements (these are identifiable by their class (`ui-diagram-element)
  3. in the event handler call a PrimeFaces remote command that calls an actionListener with the id of the clicked element added to it

adding the eventhandler:

$(document).on('click', '.ui-diagram > .ui-diagram-element', function(info){
  console.log(info.target.id); // change this to call the remote command
});

How the info.target.id can be passed to a remote command can be read in this stackoverflow Q/A

I checked step 1 and 2 and used 3 before. It all works, I just did not combine them yet, but that should be not to difficult

Upvotes: 3

Related Questions