Showing posts with label Dynamic. Show all posts
Showing posts with label Dynamic. Show all posts

31 Oct 2017

Implementing Dynamic Dialog Handler with Functional programming

In my previous post I mentioned a common use case when we need to programmatically check if the current transaction is dirty and notify a user about that before doing something. Like "You have unsaved changes that will be lost, do you want to continue?".
Suppose that we need to notify the user about dirty transaction in many places across the application, when navigating from one view to another, when clicking Search button, when invoking a business service method, etc. So, in every single scenario we need to do different things after the user confirms that they want to proceed. It means that our dialog listener should know somehow what it was all about and what to do next.

The solution could be to add a custom attribute to the af:dialog component pointing to a function which is going to be invoked when the user clicks "Yes" on the dialog:

<af:popup id="pDirtyTransaction" contentDelivery="lazyUncached">
  <af:dialog title="Warning" type="yesNo" closeIconVisible="false"
             id="dDirtyTransaction"
    dialogListener="#{theBean.dirtyTransactionDialogListener}">
     <af:outputText value="You have unsaved changes, do you want to continue?"
                    id="ot1"/>

     <f:attribute name="dialogHandler" value=""/>                   

  </af:dialog>
</af:popup>


In that case the dialog listener may look like this:

public void dirtyTransactionDialogListener(DialogEvent dialogEvent) {       
  Map attrs = dialogEvent.getComponent().getAttributes();
  Consumer<Boolean> dialogHandler = (Consumer) attrs.get("dialogHandler");
  if (dialogHandler != null) {
      dialogHandler.accept(dialogEvent.getOutcome() == DialogEvent.Outcome.yes);
      attrs.put("dialogHandler",null);
  }                   
}

We expect here that dialogHandler attribute points to an object implementing Consumer functional interface.

There is a method in our utils showing the popup with the dialog:

public static void showDirtyTransactionPopup(Consumer dialogHandler) {
  if (dialogHandler != null) {
      JSFUtil.findComponent("dDirtyTransaction").getAttributes().
              put("dialogHandler",dialogHandler);
  }

  RichPopup popup =
      (RichPopup) JSFUtil.findComponent("pDirtyTransaction");
  popup.show(new RichPopup.PopupHints());
}


Let's use this approach in a simple scenario. There are two view activities in our task flow View1 and View2. The user clicks a button to navigate from one view to another. While navigating we need to check if the current transaction is dirty and if it is ask the user if they want to proceed. We can leverage the power of Java 8 Lambda expressions and implement the button action listener  like this:

public void buttonActionListener(ActionEvent actionEvent) {

  if (Utils.isTransactionDirty()) {       

       Utils.showDirtyTransactionPopup((yesOutcome) -> {          

           //the code below will be invoked by the dialog listener
           //when the user clicks a button on the dialog                                                                     
           if ((Boolean) yesOutcome) {
               //the user has agreed to proceed,
               //so let's rollback the current transaction
               Utils.getCurrentRootDataControl().rollbackTransaction();            

               //and queue an action event for this button again
               new ActionEvent(actionEvent.getComponent()).queue();
           } });

   } else
       //just navigate to View2
       Utils.handleNavigation("goView2");
}

Basing on this technique we could implement a declarative component serving as a dialog with a dynamic content and a dynamic handler.

That's it!






29 Jan 2016

Dynamic Iterator Binding Definition

In one of my previous posts I showed how we can use multiple iterator binding definitions to represent data on a page provided by different row sets of a single VO instance. A blog reader asked me a reasonable question about this technique. Suppose that at design time we don't know what row sets and what view objects are going to be shown on a page. So, we're not able to define all necessary iterator bindings in the page def file at design time. Is it possible to dynamically create those iterator binding definitions at run time? Yes, it is!

This job can be done using the following managed bean method:

private DCIteratorBinding createIterator(String iteratorName, String voName,
                                         String dataControlName, String rsiName)
{
  DefinitionFactory defFactory =
    JUMetaObjectManager.getJUMom().getControlDefFactory();
 
  //Create and init an iterator binding definition
  JUIteratorDef iterDef = (JUIteratorDef)
    defFactory.createControlDef(DCDefBase.PNAME_Iterator);

  HashMap initValues = new HashMap();
  initValues.put(JUTags.ID, iteratorName);
  initValues.put(JUTags.DataControl, dataControlName);
  initValues.put(JUTags.PNAME_VOName, voName);
  initValues.put(JUTags.PNAME_RSIName, rsiName);

  iterDef.init(initValues);

  //Create an iterator binding instance
  DCIteratorBinding iter =
    iterDef.createIterBinding(BindingContext.getCurrent(), getBindings());

  //Add the instance to the current binding container
  getBindings().addIteratorBinding(iteratorName, iter);

  return iter;
}

At the very beginning the method gets an instance of the control definition factory. This factory is used to produce an iterator binding definition object. This definition is going to be initialized with a map containing iterator name, data control name, view object name and row set name. So, basically, all those attributes that we set up for an iterator binding definition in a page def file at design time. Finally, the method creates an iterator binding instance and adds it to the current binding container.

This managed bean method can be consumed while creating dynamic value bindings such as an attribute value binding, a button value binding, a lov binding, a tree binding etc. Examples of these techniques are described and implemented in a sample application here.

That's it!

25 Feb 2014

Dynamic LOV binding

In this post I am going to show how we can create list-of-values binding dynamically and add it to the binding container at run time. In order to create a LOV binding I've got a couple of methods in my managed bean:
public JUCtrlListBinding getJobsLOV() {
    
    //Find listOfValues binding in the current binding container
    //May be it is already there
    DCControlBinding jobsLov = getBindings().findCtrlBinding("JobId");
    
    //Let's create listOfValues binding as we didn't find it 
    if (jobsLov == null) {
        jobsLov = createJobsLOV(); 
    }
    
    return (JUCtrlListBinding) jobsLov;
 }

private JUCtrlListBinding createJobsLOV() {
  //Create an instance of listOfValues binding definition
  //Actually lovDef is going to be an instance of FacesCtrlLOVDef
  DefinitionFactory defFactory = 
      JUMetaObjectManager.getJUMom().getControlDefFactory();
  JUCtrlValueDef lovDef = 
      (JUCtrlValueDef) defFactory.createControlDef(JUTags.PNAME_listOfValues); 

  //Initialize listOfValues binding definition
  HashMap initValues = new HashMap();
  initValues.put(DCControlBindingDef.PNAME_IterBinding, "VEmployeesIterator");
  initValues.put(ListBindingDef.PNAME_ListServerBindingName, "LOV_JobId");  
  initValues.put(ListBindingDef.PNAME_AttrNames, new String[] {"JobId"});
  initValues.put(JUTags.ID, "JobId");
  lovDef.init(initValues);
    
  //Create an instance of listOfValues binding
  JUCtrlListBinding lov = (JUCtrlListBinding) 
         lovDef.createControlBinding(getBindings());
  
  //Add the instance to the current binding container
  getBindings().addControlBinding(lovDef.getName(), lov);
  return lov;
}


And a corresponding LOV component looks like this:
    <af:inputComboboxListOfValues id="jobIdId"
       popupTitle="Search and Select: #{LovBean.jobsLOV.hints.label}"
       value="#{LovBean.jobsLOV.inputValue}"
       label="#{LovBean.jobsLOV.hints.label}"
       model="#{LovBean.jobsLOV.listOfValuesModel}"
       required="#{LovBean.jobsLOV.hints.mandatory}"
       columns="#{LovBean.jobsLOV.hints.displayWidth}"
       shortDesc="#{LovBean.jobsLOV.hints.tooltip}"/>
 
The sample application for this post requires JDeveloper R2.

That's it!

24 Feb 2013

Dynamic Tree Binding Definition

ADF table component usually requires a tree binding definition in the PageDef file pointing to some iterator binding.  For example a table rendering rows of some VEmp view object would require the following structure in the PageDef file:

  <executables>
    <iterator Binds="VEmp" RangeSize="25" DataControl="AppModuleDataControl"        id="VEmpIterator"/>
  </executables>
  <bindings>
    <tree IterBinding="VEmpIterator" id="VEmp">
      <nodeDefinition DefName="com.cs.blog.dyntreebindingdemo.model.VEmp"
                      Name="VEmp0">
        <AttrNames>
          <Item Value="EmployeeId"/>
          <Item Value="FirstName"/>
          <Item Value="LastName"/>
          <Item Value="Email"/>
          <Item Value="PhoneNumber"/>
          <Item Value="HireDate"/>
          <Item Value="JobId"/>
          <Item Value="Salary"/>
          <Item Value="CommissionPct"/>
          <Item Value="ManagerId"/>
          <Item Value="DepartmentId"/>
          <Item Value="CreatedBy"/>
          <Item Value="CreatedDate"/>
          <Item Value="ModifiedBy"/>
          <Item Value="ModifiedDate"/>
          <Item Value="ActionComment"/>
          <Item Value="CreatedBy1"/>
        </AttrNames>
      </nodeDefinition>
    </tree>
  </bindings>

In this post I'm going to show how we can create a tree binding definition and a corresponding iterator binding dynamically at runtime.

Let's consider a managed bean method returning a tree binding definition:

private static final String VO_NAME = "VEmp";
private static final String ITERATOR_NAME = "VEmpIterator";
private static final String TREE_NAME = "VEmp";   
private static final String DATACONTROL_NAME = "AppModuleDataControl";   

public JUCtrlHierBinding getTree() {
    DCBindingContainer dcb = getBindings();

    //May be the VEmp tree binding is already created
    JUCtrlHierBinding chb = (JUCtrlHierBinding)dcb.findCtrlBinding(TREE_NAME);

    if (chb == null) { // if not

        //Looking for the VEmpIterator iterator
        JUIteratorBinding iter =
            (JUIteratorBinding)getIterator(ITERATOR_NAME, VO_NAME);

        //Create and init a tree binding definition
        JUCtrlHierDef hierDef = new FacesCtrlHierDef();
        HashMap initValues = new HashMap();
        initValues.put(JUCtrlHierDef.PNAME_IterBinding, iter.getName());
        JUCtrlHierTypeBinding typeBinding = new JUCtrlHierTypeBinding();
        initValues.put(JUCtrlHierDef.PNAME_TypeBindings,
                       new JUCtrlHierTypeBinding[] { typeBinding });
        hierDef.init(initValues);

        //Create a tree binding instance
        chb = (JUCtrlHierBinding)hierDef.createControlBinding(dcb);

        //Add the instance to the current binding container
        dcb.addControlBinding(TREE_NAME, chb);
    }
    return chb;
}

And getIterator method looks for the corresponding iterator binding and creates it if it doesn't exist yet:

public DCIteratorBinding getIterator(String iteratorName, String voName)
{
  DCBindingContainer dcb = getBindings();

  //It could be created earlier 
  DCIteratorBinding jub = dcb.findIteratorBinding(iteratorName);

  if (jub==null) {// if not
    //Create and init an iterator binding definition
    JUIteratorDef jid = new JUIteratorDef(iteratorName, null, voName, null, 25);
    HashMap initValues = new HashMap();
    initValues.put(JUTags.DataControl , DATACONTROL_NAME);
    jid.init(initValues);

    //Create an iterator binding instance
    jub = jid.createIterBinding(BindingContext.getCurrent(), dcb);        

    //Add the instance to the current binding container
    dcb.addIteratorBinding(iteratorName, jub);
  }
  return jub;
}
The readonly table using our dynamic tree binding should look like this:

<af:table rows="#{DynTableBean.tree.rangeSize}"
          fetchSize="#{DynTableBean.tree.rangeSize}"
          emptyText="#{DynTableBean.tree.viewable ? 'No data to display.' : 'Access Denied.'}"
          var="row" rowBandingInterval="0"
          value="#{DynTableBean.tree.collectionModel}"
          selectedRowKeys="#{DynTableBean.tree.collectionModel.selectedRow}"
          selectionListener="#{DynTableBean.tree.collectionModel.makeCurrent}"
          rowSelection="single" id="dc_t1">
  <af:forEach items="#{DynTableBean.tree.attributeDefs}" var="def">
    <af:column headerText="#{DynTableBean.tree.labels[def.name]}" sortable="true"
               sortProperty="#{def.name}" id="dc_c1">
      <af:outputText value="#{row[def.name]}" id="dc_ot1"/>
    </af:column>
  </af:forEach>
</af:table>

The technique shown in this post can be easily used to build a declarative component responsible for browsing data of any view object passed to the component as an attribute.

That's it!

22 Sept 2012

Dynamic iterator binding and method action call

In my previous post I mentioned that we can use dynamic binding approach in an effort to get the VO instance name evaluated dynamically at run-time. So, in the PageDef file we can have the following definition:


<iterator Binds="#{someViewObjectName}" RangeSize="25"   
               DataControl="AgileDataModelServiceDataControl"  
               id="VAgileIterator"/>



And EL #{someViewObjectName} is going to be evaluated as a name of some view object instance in the AgileDataModelService application module. And let's assume that any VO instance, which name is evaluated by the EL expression, inherits some parent view object VAgile. And its implementation class VAgileImpl contains some public method returning a String:

    public String getSomeString() {
        return ...;
    }


We're going to publish this method in VO's client interface and make it invokable from the UI via  binding layer. So, we're going to define a methodAction in the PageDef file in order to invoke the method for the particular VO instance, which name is going to be evaluated by the #{someViewObjectName} EL expression:

<methodAction id="getSomeString" RequiresUpdateModel="true"
              Action="invokeMethod" MethodName="getSomeString"
              IsViewObjectMethod="true"
              DataControl="AgileMetaDataModelServiceDataControl"
              InstanceName="???"
              ReturnName="data.AgileMetaDataModelServiceDataControl.methodResults.getSomeString_AgileMetaDataModelServiceDataControl_VAgile_getSomeString_result"/>

But a methodAction definition has an InstanceName attribute, supposed to contain a name of the particular VO instance. Since we're using the dynamic iterator binding approach, we don't know the instance name, it's going to be evaluated at run time by the EL expression. Unfortunately we can't use EL expressions in the Instance attribute. The workaround is to use an iterator bindings VAgileIterator to get the instance of the VO. So we can do the following:

<methodAction id="getSomeString" RequiresUpdateModel="true"
              Action="invokeMethod" MethodName="getSomeString"
              IsViewObjectMethod="false"
              DataControl="AgileMetaDataModelServiceDataControl"
              InstanceName="bindings.VAgileIterator.viewObject"
              ReturnName="data.AgileMetaDataModelServiceDataControl.methodResults.getSomeString_AgileMetaDataModelServiceDataControl_VAgile_getSomeString_result"/>

Have a note, that we used bindings.VAgileIterator.viewObject in the InstanceName attribute and the IsViewObjectMethod attribute is false. Actually, that's it. It works.

29 Jul 2012

Resource bundle for dynamic view object

We can use ADF BC API to create dynamic view objects at run-time and present the data in UI with dynamic:table, dynamic:form, "forEach" or with any other approach. The question is: how to setup correct UI hints for the VO's attributes?
The example below creates a dynamic VO VEmployees:
     String queryStmt = "select Employee_ID,  Last_Name from Employees";
     vo = createViewObjectFromQueryStmt("VEmployees", queryStmt);

By default labels of the query fields are equal to the field names. Of course, we can set the labels directly:
     AttributeDefImpl at = (AttributeDefImpl) vo.getAttributeDef(0);
     at.setProperty(AttributeDefImpl.ATTRIBUTE_LABEL, "Unique ID");

     at = (AttributeDefImpl) vo.getAttributeDef(1);
     at.setProperty(AttributeDefImpl.ATTRIBUTE_LABEL, "Last Name");
 
We can do even more. Let's take care of the locale:
     AttributeDefImpl at = (AttributeDefImpl) vo.getAttributeDef(0);
     at.setProperty(AttributeDefImpl.ATTRIBUTE_LABEL, "Unique ID");
     at.setProperty(AttributeDefImpl.ATTRIBUTE_LABEL+"_ukr_UA", "Iдентiфiкатор");

     at = (AttributeDefImpl) vo.getAttributeDef(1);
     at.setProperty(AttributeDefImpl.ATTRIBUTE_LABEL, "Last Name");
     at.setProperty(AttributeDefImpl.ATTRIBUTE_LABEL+"_ukr_UA", "Прiзвище");     


But what if we want to store UI hints in a resource bundle file (or files for different locales)? Instead of setting some value for the label directly, we have to set resource ID and set particular resource bundle for the VO's ViewDef. In the following example we call getResourceBundleDef() method to get the resource bundle of the current Application Module. This is a common practice to have one bundle per project.
     AttributeDefImpl at = (AttributeDefImpl) vo.getAttributeDef(0);
     at.setProperty(AttributeDefImpl.ATTRIBUTE_LABEL +"_ResId", 
                    "VEmployees.Id_LABEL");

     at = (AttributeDefImpl) vo.getAttributeDef(1);
     at.setProperty(AttributeDefImpl.ATTRIBUTE_LABEL +"_ResId", 
                    "VEmployees.LastName_LABEL");

     ViewDefImpl viewDef = (ViewDefImpl) ((ViewObjectImpl) vo).getDef();
     viewDef.setResourceBundleDef(getResourceBundleDef());

The resource bundle properties file has the following fragment:
VEmployees.Id_LABEL=Unique ID
VEmployees.LastName_LABEL=Last Name

In order to use a separate properties file or, probably, we want to implement our custom resource bundle, retrieving resources from let's say a database, we have to create a resource bundle definition ourselves:
     AttributeDefImpl at = (AttributeDefImpl) vo.getAttributeDef(0);
     at.setProperty(AttributeDefImpl.ATTRIBUTE_LABEL +"_ResId", 
                    "VEmployees.Id_LABEL");

     at = (AttributeDefImpl) vo.getAttributeDef(1);
     at.setProperty(AttributeDefImpl.ATTRIBUTE_LABEL +"_ResId", 
                    "VEmployees.LastName_LABEL");

     ViewDefImpl viewDef = (ViewDefImpl) ((ViewObjectImpl) vo).getDef();
     //Create custom properties bundle definition
     PropertiesBundleDef rb = new PropertiesBundleDef(viewDef);
     rb.setPropertiesFile("com.cs.blog.dynamicbundle.model.VEmployeesBundle");
     
     viewDef.setResourceBundleDef(rb);

 
That's it!

15 Jun 2012

Oracle OpenWorld 2012. Deep Dive into Oracle ADF.

I am happy to invite everybody to attend my session at Oracle OpenWorld 2012.

Session ID: CON5623
Session Title: Deep Dive into Oracle ADF: Advanced Techniques

You are welcome to have a journey from the surface of the declarative approach into the deep of the real-life hands-on programming. The session is completely technical and concentrates on advanced techniques of creating and manipulating ADF objects programmatically on-the-fly.

Looking forward to seeing you in San Francisco!

11 Dec 2011

Dynamic ADF Train. Showing train stops programmatically.

In one of my previous posts I showed how to create train stops programmatically. And I got a comment with a question on the post - "Is it possible to show different pages on each of the dynamic train stop?". The answer is - Yes, off course!
In this post I'm going to give an example of showing train stops programmatically. So, I need to show or hide some stops dynamically at runtime. Everybody knows that TrainStop has Ignore attribute

 
And if we could dynamically change it's value or put there some EL expression that could be evaluated during the taskflow's life cycle, It would be the best approach of showing/hiding train stops at runtime. But Ignore attribute is evaluated only once, at the task flow initialization phase and cannot be modified further.  But! As it was shown in this post we can programmatically add (or remove) train stops to the train model of the task flow. So, we can do it!

Let's say I've got the following train TaskFlow:


PassenerView activity, LuggageView and MealView  by default have <ignore>true</ignore> attribute and at the very beginning, after task flow initialization, are hidden. The train model doesn't have stops for these activities. On the first stop of the task flow I'm going to decide which activity should be included in the train and which one is hidden. On the task flow initialization I call the following managed bean method:

    private static String START_VIEW = "StartView";
    //Fill map with all activities of the TaskFlow
    //except StartView
    private void InitIgnoredStops() {
        for (Iterator it= getTaskFlowDefinition().getActivities().values().iterator(); it.hasNext();) {
          Activity act = (Activity) it.next();
          if (!START_VIEW.equals(act.getIdAttribute())) {
                ignoredStops.put(act.getId(), "false");
            }

        }
        
    }

The ignoredStops map is shown on the StartView page using techniques explained in this post. So, I have the following page:



The following method is called on Submit button's action:

public String buttonPress() {

    TrainModel trainModel = TrainUtils.findCurrentTrainModel();

    //Loop over the map
    for (Object actid : ignoredStops.keySet().toArray()) {
        //If activity is not chosen 
        if (!Boolean.parseBoolean(ignoredStops.get(actid).toString())) {
            // if activity is included in the train then remove it
            if (trainModel.getTrainStops().get(actid) != null)
                 trainModel.getTrainStops().remove(actid);
        } else {
            //If activity is chosen and it is not included in the train 
            // then we need to include it
            if (trainModel.getTrainStops().get(actid) == null) {
                MetadataService metadataService =
                    MetadataService.getInstance();
                
                //Get activity and its train stop definition
                Activity activity =
                    metadataService.getActivity((ActivityId)actid);                
                TrainStopContainer stopContainer =
                        (TrainStopContainer)activity.getMetadataObject();
                TrainStop ts = stopContainer.getTrainStop();
                
                
                //Create new train stop model and add it to the train
                trainModel.getTrainStops().put((ActivityId)actid,
                                                   new TrainStopModel(ts,
                                                                      (ActivityId)actid));
                }
            }


        }
    
}

If needed activities are chosen we can press Submit  button and get the following picture:




That's it!