30 Sept 2015

Calculating VO Transient Attributes

In this post I am going to consider a use-case when we need to calculate value of a VO transient attribute according to some complicated and resource-consuming business logic.

Let's say there is a method in a ViewRowIml class:
public String getValueForSomeTransAttr()
{   
  return getBusinessLogicFactory().
           getSuperComplexBusinessLogic.evaluateForKeyValue(getPrimaryKey());
}

We could use this method either in attribute's getter method:
  public String getSomeTransAttr()
  {   
    return getValueForSomeTransAttr();
  }

or we could use it in Groovy expression for the attribute:

Both options are bad, because the method is going to be invoked every time whenever the attribute value is accessed, and that could happen a number of times within the request. Of course we'd like to avoid extra executions of our super complicated business logic. Basically, the value of the transient attribute depends on primary key only, which means that we want to calculate it only once for each row (assuming that primary key value never changes). In order to meet this requirement we have to set "false" in Refresh Expression Value:


The same thing can be done using Edit Expression Editor:


Having done that, the method will be invoked only when a VO row is created from VO result set and when a new row is created by a user in UI.

That's it!




28 Sept 2015

Show an Image in a Popup Window

There is a very common use-case when we have a link with an image on a page and we need to show a full-size version of the image in a popup window by clicking on the link. In this post I am going to show a few implementations of this use-case in ADF application.

Inline Popup
The first and the most obvious solution is to use af:popup and af:showPopupBehavior tags. Something like this:

<af:link text="Inline Popup" id="l1" icon="/images/turtle.jpg"
         iconPosition="top" styleClass="ImageLink">
  <af:showPopupBehavior popupId="p1"/>
</af:link>

<af:popup animate="false" contentDelivery="lazyUncached"
          childCreation="deferred" id="p1">
  <af:panelWindow resize="on" stretchChildren="first" id="pw1" title="Turtle">
    <af:image id="dc_i1" source="/images/turtle.jpg"/>
  </af:panelWindow>
</af:popup>

In this case an inline popup is going to be shown whenever the link is clicked:


So, it is pretty easy to implement and it works quite fast and nice.
What if we don't want to use an inline popup and we prefer to show the image in a separate browser window?

Separate browser tab
Let's assume the requirement is to show the image in a separate browser tab.
Actually, it is very easy to implement. We're going to use destination and targetFrame attributes of af:link component:

<af:link text="Destination" id="l2" icon="/images/turtle.jpg"
         iconPosition="top" styleClass="ImageLink"
         destination="/images/turtle.jpg" targetFrame="_blank"/>

So, whenever the link is clicked a new tab should be opened with a full-size image inside it. Actually, the "_blank" targetFrame forces the browser to open a new tab.


Separate browser window
If we want to force a browser to show the image inside a new window, we're going to use either of the following techniques:

ADF dialog: action
This is a pure ADF technique and it is based on ability to show a page in a new browser window while navigating to it. There are two pages in our page flow:

The ImageView contains an image:
  <af:form id="f1">
    <af:image source="/images/turtle.jpg" id="i1"/>
  </af:form>

And the MainView contains a link:
<af:link text="Dialog action" id="l3" icon="/images/turtle.jpg"
         iconPosition="top"
         action="dialog:showImage"
         useWindow="true" windowHeight="400" windowWidth="400"/>

Pay attention to the useWindow attribute and to the action name which starts with "dialog:" prefix. These two things force the browser to show ImageView page in a new window. The size of this new window is defined with windowHeight and windowWidth attributes.
This approach works fine, but I wouldn't say that this solution is the best one in terms of performance. Whenever the framework is processing such "dialog:" navigation it seams that a spaceship is starting  on Cape Canaveral. It may take a second or two to open this new tab.

Java Script Approach
This lightweight solution uses a java script to open a new browser window:

function showImage(event)
{
  var link = event.getSource();
  popupWindow = window.open(link.getProperty("imageSrc"),
                            'Turtle', 'height=400,width=400');
}

There is a corresponding clientListener defined for the link:
<af:link text="Java Script" id="l4" icon="/images/turtle.jpg"
         iconPosition="top" styleClass="ImageLink">
  <af:clientListener method="showImage" type="action"/>
  <af:clientAttribute value="#{ImageBean.endpointURL}/images/turtle.jpg"
                      name="imageSrc"/>
</af:link>

In order to pass correct image URI to the Java Script function we are using a managed bean method getEndpointURL:

public String getEndpointURL()
{
  FacesContext facesContext = FacesContext.getCurrentInstance();
  HttpServletRequest request =
    (HttpServletRequest) facesContext.getExternalContext().getRequest();
  String url = request.getRequestURL().toString();
  StringBuffer newUrlBuffer = new StringBuffer();
  newUrlBuffer.append(url.substring(0, url.lastIndexOf("faces/")));
  return newUrlBuffer.toString();
}

Basically this method returns URL to the application endpoint.

The sample application for this post can be downloaded here. It requires JDeveloper 12.1.3.

That's it!