dvt:treemap showing node detail in popup

This post describes how to implement an dvt:treemap which shows a af:popup when the user clicks on a detail node in the map.
The documentation of the dvt:treemap component tell us that the dvt:treemapnode supports the af:showPopupBehaviortag and reacts on the ‘click’ and ‘mouseHover’ events.
This is part of the solution and allows us to begin implementing the use case. We add an af:showPopupBehavior to the nodes we want to show detail information for.

After creating a default Fusion Web Application which uses the HR DB schema, we begin with creating the data model for the model project. For this small sample the departments and employees tables will be sufficient.


The views are named according to their usage to make it easier to understand the model. This is all we need for the model.

Let’s start with the UI which only consist of a single page. The page has a header part and a center part. In the center area we build the treemap by dragging the Departments from the data controls onto the page and dropping it as treemap. After that, in the dialog we specify the first level of the map to be the departmentId (which shows the department name as the label) and the for the second level we choose the employeeId (which shows the last name of the employee as label) from the employees. The whole process is shown in the gallery below.


The resulting treemap is very basic in it’s features, e.g. there is no legend as you see later.
In the next step we create an af:popup to show the nodes detail information. This process is outlined in the next gallery. We drag the popup component onto the page below the af:treemap component

One thing to take note of are the properties of the popup. First we set the content delivery to ‘lazyUncached’, which makes sure that the data is loaded every time the popup is opened. Otherwise we’ll see only the data from the first time the popup has been opened. Second change is to set the launcherVar to ‘source’. This is the variable name we later use to access the node data. Third change is to set the event context to ‘launcher’. This means that events delivered by the popup and its descendents are delivered in the context of the launch source.

The treemap for example, when an event is delivered ‘in context’ then the data for the node clicked is made ‘current’ before the event listener is called, so if getRowData() is called on the collectionModel in the event listener it will return the data of the node that triggered the event. This is exactly what we need.

Finally we add a popupFetchListener to the popup which we use to get the data from the current node to a variable in the bindings. In the sample this variable ‘nodeInfo’ is defined in the variable iterator of the page and an attribute binding ‘nodeInfo1’ is added. More info on this can be found here.


The code below shows the popupFetchListener:

package de.hahn.blog.treemappopup.view.beans;

import javax.el.ELContext;
import javax.el.ExpressionFactory;

import javax.faces.application.Application;
import javax.faces.context.FacesContext;

import oracle.adf.model.BindingContext;
import oracle.adf.share.logging.ADFLogger;
import oracle.adf.view.rich.event.PopupFetchEvent;

import oracle.binding.AttributeBinding;
import oracle.binding.BindingContainer;


/**
 * Treemap handler bean
 * @author Timo Hahn
 */
public class TreemapBean {
    private static ADFLogger logger = ADFLogger.createADFLogger(TreemapBean.class);

    public TreemapBean() {
    }

    /**
     * listen to popup fetch.
     * @param popupFetchEvent event triggerd the fetch
     */
    public void fetchListener(PopupFetchEvent popupFetchEvent) {
        // retrieve node information 
        String lastName = (String) getValueFromExpression("#{source.currentRowData.lastName}");
        Integer id = (Integer) getValueFromExpression("#{source.currentRowData.EmployeeId}");
        //build info string
        String res = lastName + " id: " + id;
        logger.info("Information: " + res);
        // get the binding container
        BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();

        // get an ADF attributevalue from the ADF page definitions
        AttributeBinding attr = (AttributeBinding) bindings.getControlBinding("nodeInfo1");
        //set the value to it
        attr.setInputValue(res);
    }

    // get a value as object from an expression
    private Object getValueFromExpression(String name) {
        FacesContext facesCtx = FacesContext.getCurrentInstance();
        Application app = facesCtx.getApplication();
        ExpressionFactory elFactory = app.getExpressionFactory();
        ELContext elContext = facesCtx.getELContext();
        Object obj = elFactory.createValueExpression(elContext, name, Object.class).getValue(elContext);
        return obj;
    }
}

Finally we have to design the popup to show the node info from the attribute binding ‘nodeInfo1’. The popup uses a dialog with an af:outputText like

Show the node info in the popup

Show the node info in the popup


and set an af:showPopupBehavior to the node showing the employees

Running the finished application brings up the treemap, not pretty but enough to see this use case working. If we click on an employee node we see the popup with the last name of the employee and the employee id, the primary key of the selected row in the employees iterator.

You can download the sample application which was build using JDeveloper 12.1.3 and the HR DB schema from GitHub.

Advertisements

JDeveloper: Showing a Popup when Selecting an af:selectOneRadio

A question on the new OTN JDeveloper and ADF forum (or space if you like the new name better) inspired this post.

Use Case
The use case is to show a popup each time the user clicks on one of the radiobuttons of a button group. This popup should show an inputText component to let the user enter some text for the selection. After the selection the text is shown on the page under the button group.
To make it more interesting, it was not ask for this in the question, the popup should show which radio button was clicked.
UPDATE
An additional question on the OTN forum asks how to display the label of the selected radiobutton and not it’s value. I updated the workspace to show how to implement this.

Implementation UPDATE
The use case sounds easy first: add a showPopupBehavior to the selectItem to show the popup and show the selected radioValue as the title of the dialog inside the popup. Problem with this solution is that a selectItem can’t handle client listeners, which a showPopupBehavior is under the hood. If you try this you get an error

Caused by: javax.servlet.jsp.JspException: ADF_FACES-60023:Component: RichSelectItem[UIXFacesBeanImpl, id=si10] does not support client listeners.

Putting the showPopupBehavior onto the af:selectOneRadio isn’t a solution either as this would would allow to show the popup, but would prevent the selection of the radio button. This is because the showPopupBehavior is a client behavior tag which prevents the event to
go further.

The solution to the problem is to use a valueChangeListener on the af:selectOneRadio and show the popup from the bean code. Before showing the popup we need to process the value change event to get the selected value in the dialog of the popup.

The UI part looks like

        <af:panelGroupLayout id="pgl2">
          <af:selectOneRadio label="Activation" id="sor1" value="#{bindings.SelectedActivation1.inputValue}"
                             inlineStyle="border-width:thin; border-color:Blue; border-style:solid;"
                             valueChangeListener="#{RadiobuttonPopupBean.activationChangedListener}" autoSubmit="true">
            <af:selectItem label="Active" value="active" id="si1"/>
            <af:selectItem label="Inactive" value="not active" id="si2"/>
          </af:selectOneRadio>
          <af:outputText value="Last reason: #{bindings.PopupText1.inputValue}" id="ot1" partialTriggers="d1"/>
        </af:panelGroupLayout>
        <af:popup id="p1" contentDelivery="lazyUncached" binding="#{RadiobuttonPopupBean.radioPopup}">
          <af:dialog id="d1" title="Selected: #{bindings.SelectedActivation1.inputValue}">
            <af:inputText label="Reason" id="it1" value="#{bindings.PopupText1.inputValue}"/>
          </af:dialog>
        </af:popup>

The listener code in the bean, which is created in request scope as it contains a reference to a ui component, look like

import javax.faces.context.FacesContext;
import javax.faces.event.ValueChangeEvent;

import oracle.adf.view.rich.component.rich.RichPopup;

import org.apache.myfaces.trinidad.util.ComponentReference;

public class RadiobuttonPopupBean {
    private ComponentReference radioPopup;

    public void setRadioPopup(RichPopup radioPopup) {
        this.radioPopup = ComponentReference.newUIComponentReference(radioPopup);
    }

    public RichPopup getRadioPopup() {
        if (radioPopup != null)
            return (RichPopup)radioPopup.getComponent();

        return null;
    }

    public void activationChangedListener(ValueChangeEvent valueChangeEvent) {
        // process updates to get the selected value inside the popup
        FacesContext contxt = FacesContext.getCurrentInstance();
        valueChangeEvent.getComponent().processUpdates(contxt);
        //show the popup
        RichPopup.PopupHints hint = new  RichPopup.PopupHints();
        getRadioPopup().show(hint);
    }
}

The application running look like


You see that the popup shows the value of the selected radio button as the title and the entered text in the popup is shown as ‘last reason’ below the radio button group. This ‘magic’ is done by simply adding a partial trigger to the outputText component which is listening to the dialog inside the popup.
As the sample don’t store the value of the selection of the radio group or the entered text in the db, I use the variable iterator to store these values. More about this technique can be found in my blog

UPDATED IMPLEMENTATION
As the use case changed a bit I changes the solution in a way to show how to implement this. First step is that I added another attribute to the variables iterator to store the selected label. The attribute is name ‘SelectedActivation1’ and is of type String. This attribute is needed as it’s not easyly possible to get the selected label of the radiobutton outside the selection listener. The changed valueChangeListener looks like the code below:

    public void activationChangedListener(ValueChangeEvent valueChangeEvent) {
        // process updates to get the selected value inside the popup
        FacesContext contxt = FacesContext.getCurrentInstance();
        valueChangeEvent.getComponent().processUpdates(contxt);
        //get the selected lable from the radio button
        // for this we need to iterate over the children of hte af:selectOneRadio
        // and find the child which has the same value as the new value
        RichSelectOneRadio rsoc =
            (RichSelectOneRadio)valueChangeEvent.getSource();
        List childList = rsoc.getChildren();
        String newVal = (String)valueChangeEvent.getNewValue();
        for (int i = 0; i < childList.size(); i++) {
            if (childList.get(i) instanceof RichSelectItem) {
                RichSelectItem csi = (RichSelectItem)childList.get(i);
                if (((String)csi.getValue()).equals(newVal)) {
                    // get the binding container
                    BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();

                    // get an ADF attributevalue from the ADF page definitions
                    AttributeBinding attr = (AttributeBinding)bindings.getControlBinding("SelectedLabel1");
                    // and store the label there
                    attr.setInputValue(csi.getLabel());
                }
            }
        }
        //show the popup
        RichPopup.PopupHints hint = new RichPopup.PopupHints();
        getRadioPopup().show(hint);
    }

The images below showing the changes application

You can download the workspace from the ADF EMG Sample Project BlogTestRadiobutton.zip. The sample uses the HR DB schema (even as this is not really needed for the sample).

af:popup mit Übernahme der Änderungen in einer Erfassungsmaske

Problem:

In einer Erfassungs- oder Änderungsmaske soll über ein Popup-Dialog Daten eingegeben werdne, die anschließend in der aufrufenden Seite (hier eine Erfassungs- oder Änderungsmaske) angezeigt werden sollen.

Lösungsversuche:

Da der Dialog über einen Event verlassen wird, liegt es natürlich nahe, die Daten aus dem PopUp in einer Bean, die den Event verarbeitet, in die entsprechenden Datenwerte der Erfassungsmaske einzutragen. Leider tut sich an der Oberfläsche dannaber nicht, sprich, der Anwender sieht immer noch die alten Daten.

Alle Versuche über den BindingContainer (refresh oder auch direktes Änderen der Daten in der Attributliste des BindingContainer) helfen hier auch nicht.

In seinem Blog hat Shay Shmeltzer (http://blogs.oracle.com/shay/2008/05/13/#a683) den Lösungsansatz skizziert. Leider hat er es aber so ausgedrückt, als ob diese Lösung nur mit Tabellen funktioniet. Im Forum fand ich dann den Hinweis von Frank Nimphius (http://forums.oracle.com/forums/thread.jspa?messageID=2280508&#2280508), dass das auch mit anderen Komponenten geht.

Lösung:

Es ist aslo nur notwendig, das Panel (oder sonstige Gruppierungskomponente) in die Bean zu binden, die die PopupDaten verarbeiten soll und dann diese Komponente als PartialTarget einzutragen.

// gebundene Komponente, die die Attribute enthält
private RichPanelGroupLayout mainAttribute;
...

RequestContext.getCurrentInstance().addPartialTarget(mainAttribute);

Das aktuallisiert dann das Binding auf der Seite und zeigt die neuen Werte an.