Saturday 21 June 2014

ADF - Navigate with double click in a table row

A functionality usefull in Form Builder is the doble click event in a table row.
For exemple to navigate into two fragments in the same taskflow or to navigate in a different taskflow.
Double

<af:resource type="javascript">
                  function clientEvent(evt) {
                      var cmp = evt.getSource();
                      AdfCustomEvent.queue(cmp, "BeanMethod", {}, false);
                      evt.cancel();
                  }
 </af:resource>

    private static void myCallFlowCase(String flowCase) {
        FacesContext context = FacesContext.getCurrentInstance();
        NavigationHandler nh = context.getApplication().getNavigationHandler();
        UIComponent c = JSFUtils.findPageRoot(context.getViewRoot());
        while(c.getParent() != null && !(c instanceof RichRegion)){
            c = c.getParent();
        }
        if(c != null){
            AdfFacesContext.getCurrentInstance().addPartialTarget(c);
        }
        nh.handleNavigation(context, "", flowCase);
    }

Thursday 17 April 2014

ADF - Convert inputText inputListOfValues to UpperCase

There are many articles about this topic.
Various techniques are proposed including: Javascript, converter, contentStyle etc..
I think that no solution is perfect.
For example:
Javascript: while typing the character changes.
Converter: the conversion happens only on the server side.
contentStyle: the conversion is only visual but the String submitted is not in uppercase.
For me, the best solution is to act in two places.
I consider excellent the use of the contentStyle to display uppercase letters:


In my case I use an inputText, but an inputListOfValues is equal to.
As I wrote before the contentStyle has an effect purely graphic but the value submitted is not converted.
We therefore need to combine the use of the contentStyle with the use of a Converter to convert the value also on server side.
So, we must implement a simple converter that implements the class: javax.faces.convert.Converter:


Very simple.
After that we must register this converter in faces-config.xml file:


Finaly in the converters list of our component we can choose the StringToUppercaseConverter:


In this way we not only see the uppercase letters when typing, but also the data will be submitted in uppercase.

But...Why this is not a perfect procedure?
In this way we remain uncovered on programmatic "setAttribute" calls.
For example if we call 
row.setAttribute("MyField","abc") 
for an attribute not in page, the converter (obviously not present) not will converts the value.

Attention:
I think that convert the data directly in the overridden setAttribute method  is not a good idea.
setAttribute should already be called with the right value.

Monday 25 November 2013

ADF - In a Button: invoke an Action (control-flow-case) from its ActionListener Programmatically

In the Property Inspector of UIComponents like af:commandButton, af:commandToobarButton, ecc.. there are two important property in the Button Action section: Action and ActionListener.
In Action we can reference a static outcome, for example a control-flow-case for navigate between two fragmgents in the same taskflow.
ActionListener is used to reference a method in a Bean.
If both the two properties are setted the execution will be at the same time, so we can't prevent the execution of the Action.
If we want use the ActionListener for a control so that if and only if this control is positive navigate to another fragment here the solution.
Description of the example:
We have a taskflow with two fragments: Fragment_A and Fragment_B.
Pressing a button (a RichCommandButton) in the Fragment_A we can navigate to Fragment_B only if a control returns "true".
Differently we can navigate from Fragment_B to Fragment_A without any control pressing another button.
To do this we set in the taskfow diagram the "goFragA" control-flow-case to navigate from Fragment_A to Fragment_B and the "goFragB" control-flow-case for back to the Fragment_A.


In the RichCommandButton in the Fragment_B set the action with "goFragA":



In the RichCommandButton in the Fragment_A set the ActionListener with the method:

  public void goToFragmentB(ActionEvent actionEvent) {
    if(<control>){
      RichCommandButton b = (RichCommandButton)actionEvent.getComponent();
      MethodExpression me = JSFUtils.getMethodExpression("goFragB",new Class[]{ActionEvent.class});
      b.setActionExpression(me);
    }else{
      FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR,"Control Failure",null);
      FacesContext.getCurrentInstance().addMessage(null,msg);
    }
  }

This solution is very convent for control the navigation.




Monday 18 November 2013

POST-QUERY in ADF

The POST-QUERY in ADF is very simple to implement and it is much documented both in the official Oracle Documentation and in various ADF Blogs and Forums.
In my blog I discuss often about the relation between Form Builder and ADF, so I think that a post about POST-QUERY is very important to write.
So, here the implementation:

Open the Model Project and implement the ViewObjectImpl class of your ViewObject.xml (in my example called MyViewObject.xml) as shown:



Now override the method "createRowFromResultSet":


    protected ViewRowImpl createRowFromResultSet(Object qc, ResultSet resultSet) {
      ViewRowImpl value = super.createRowFromResultSet(qc, resultSet);
      if(value != null){
        <<YOUR POST-QUERY CODE of the row "value">>
      }
      return value;
    }

That's all.
Easy and fast!

Monday 11 November 2013

WHEN-NEW-FORM-INSTANCE in ADF

A frequently asked question for ADF developers is: how can I execute a method on page load?
It would be the equivalent of the WHEN-NEW-FORM-INSTANCE of Form Builder.
On forums and blogs there are many solutions but I have adopted an alternative.
I needed a method that triggers on load of each fragment of a taskflow (with all the items already designed).
So, in ADF I wanted to implements two moments:

  1. The first called WHEN-NEW-TASKFLOW-INSTANCE fires when the first fragment of the taskflow loads: fires once.
  2. The second called WHEN-NEW-FRAGMENT-INSTANCE fires on load of each fragment of the taskflow: fires every time the fragment is invoked (therefore depends of the navigation).

An easy way to do this is using the rendered property of the outer UIComponent cotainer.
In each my fragments I use a panelStretchLayout like container (I think many of you do the same thing).
The rendered property is a method in my backingBean like:

<af:panelStretchLayout id="psl1" rendered="#{backingBeanScope.MyBean.myRendered}">
[...]

We can use the "get" method of "myRendered" property for execute a method.
This is not over.
We know that the method of rendered is executed multiple times during the loading of the page (we are not certain of having control).
To perform our methods only when we need we can do:

    public String getMyRendered(){
        final String TASK_FLOW_INSTANCE_FIRED = "TASK_FLOW_INSTANCE_FIRED";
        final String FRAGMENT_INSTANCE_FIRED = "FRAGMENT_INSTANCE_FIRED";
        AdfFacesContext afc = AdfFacesContext.getCurrentInstance();
        if(afc == null){
            return "true";
        }
        String tf = (String)afc.getPageFlowScope().get(TASK_FLOW_INSTANCE_FIRED);
        if(tf == null){
            <<CALL WHEN-NEW-TASKFLOW-INSTANCE METHOD>>
            afc.getPageFlowScope().put(TASK_FLOW_INSTANCE_FIRED, "1");
        }
        String frag = (String)afc.getViewScope().get(FRAGMENT_INSTANCE_FIRED);
        if(frag == null){
            <<CALL WHEN-NEW-FRAGMENT-INSTANCE METHOD>>
            afc.getViewScope().put(FRAGMENT_INSTANCE_FIRED, "1");
        }
        return "true";
    }

The method returns always "true" but uses the View and the PageFlow scopes for control the executions of  our triggers: when-new-taskflow-instance and when-new-fragment-instance.
As previously mentioned in these moments we have all of the items loaded and we can do operations on them.

Monday 4 November 2013

ADF - Attribute Binding dynamically created with validations

I needed to dynamically implement an attribute binding on PageDef of my fragment.
After that I could dynamically create also the item (a RichInputText) on the fragment with its validator.
There are 2 cases:
Case A) the items are created in a Table,
Case B) the items are created in a PanelFormLayout.

As has been created in the table (CASE A):


DCBindingContainer dcBindings = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
JUCtrlHierBinding treeData = (JUCtrlHierBinding)dcBindings.getControlBinding(MyView);
JUCtrlHierNodeBinding nodeBind = treeData.getRootNodeBinding();
/* EDIT - Causes errors in column sorting
* Correction below
* String[] names = nodeBind.getAttributeNames();
int nItem = names.length;
String[] newNames = new String[nItem + 1];
System.arraycopy(names, 0, newNames, 0, nItem);
newNames[nItem] = "MyNewField";
nodeBind.setAttributeNames(newNames);
*/
//EDIT - Correction:
ViewObjectImpl vo = (ViewObjectImpl )treeData.getViewObject();
AttributeDef[] adfs = nodeBind.getAttributeDefs();
int nItem = adfs.length;
AttributeDef[] newAdfs = new AttributeDef[nItem + 1];
System.arraycopy(adfs, 0, newAdfs, 0, nItem);
int newAttrIndex = vo.getAttributeIndexOf(""MyNewField");
newAdfs[nItem] = vo.getAttributeDef(newAttrIndex);
nodeBind.setAttributes(newAdfs);
treeData.setAttributes(newAdfs);

Then I create the RichInputText in a RichColumn in a RichTable with its validator:

ValidatorTag.BindingValidator vb = new ValidatorTag.BindingValidator(null,JSFUtils.getValueExpression("#{row.bindings.MyNewField.validator}"));
((UIXEditableValue)
newRichInputText).addValidator(vb);

It's works!

As has been created in form (CASE B):


DCBindingContainer dcBindings = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
DCIteratorBinding it = ADFUtils.findIterator("MyViewIterator");
JUCtrlAttrsBinding nb = JUCtrlAttrsBinding.createAttributeBinding((JUFormBinding)dcBindings,null, "MyView", null, "MyViewIterator", "MyNewField");
dcBindings.addControlBinding("MyNewField", nb);


Then I create the RichInputText in a PanelFormLayout with its validator:

ValidatorTag.BindingValidator vb = new ValidatorTag.BindingValidator(null,JSFUtils.getValueExpression("#{bindings.MyNewField.validator}"));
((UIXEditableValue)newRichInputText).addValidator(vb);



It's  don't works!


The problem is on the validator.
I see the new InputText in my page and the value is right, but when I submit the field I see this error:

"Unable to resolve a Validator instance using either validatorId '' or binding '#{bindings.MyNewField.validator}'."


My team has published the question on Oracle Forum without any replies:

https://forums.oracle.com/thread/2513671

How I solved it?


I have dealt the case B as the case A, creating the attribute binding in the same way.

The difference was in creating the item on the page.

Is no longer been inserted in panelFormLayout but inside an Iterator in this way:

UIXIterator iter = new UIXIterator();

iter.setVar("row");
iter.setRows(1);
iter.setValueExpression("value",JSFUtils.getValueExpression("#{bindings.MyView.collectionModel}"));
iter.setValueExpression("first",JSFUtils.getValueExpression("#{bindings.MyView.currentRowIndex}"));
myPanelForm.getChildren().add(iter);

Yes, this is a workaround but now.. It's works.