Jun 11

As previously stated we are looking to integrate ExtJS with as many of the apex wizard driven components and templates as possible. This post focuses on the tabular form and how we integrated it into the Ext grid whilst retaining the APEX form processing functionality e.g. “ApplyMRU” and “ApplyMRD”.

APEX ExtJS - APEX Form Component Selection

In the previous posts we have highlighted that we have created a region and report template that takes a standard SQL report and turns it into and Ext grid. We are reusing the same templates on the tabular form. What this highlights is code reusability and centralization, we don’t need to create another set of templates for the tabular form. That said it does come with some restrictions as we can’t support a Popup LOV or APEX Date Picker (we’ll explain why shortly). We will also be reusing the button template that previously posted about to add the buttons to the grid toolbar and the delete button will reuse the Modal window popup for confirm deletion prompt and simply post the page using “doSubmit”. It’s funny how nicely all the pieces start fitting together, design is key, remember that!

Since we are simply using a template to enable the Ext grid functionality whatever is defined under the report definition will be displayed in the Ext grid. So if our columns are select lists, then select lists will be displayed, same for text fields, and textareas. This also means that they will be generated with the right name and id’s which are parameters for APEX wwv_flow.accept package call e.g. “f01″. This is how we can still use the APEX MRU,MRD processing functionality.

TheProblems we Encountered:

  1. First Problem: Popup LOV’s and APEX Date Picker cannot be supported (it broke our report template as we generate a JSON object in javascript) as previously stated, this is because the APEX engine generates some additional javascript into the report row entries which is outside of our control so columns cannot be defined with these two types. We will however provide an Ext equivalent (which is better) simply by defining the item as a text field but using the CSS class to determine what Ext widget to override it with, i.e. ext-date, ext-popuplov.
  2. Next Problem: our region template broke, this was because the APEX engine automatically appends some javascript code to the #BODY# subsititution tag for tabular forms to control row highlighting on selection. e.g.

            <script type="text/javascript">
            <!--
                var rowStyle      = new Array(5);
                var rowActive     = new Array(5);
                var rowStyleHover = new Array(5);
    
                rowStyle[1]='';
                rowStyleHover[1]='';
                rowActive[1]='N';
                rowStyle[2]='';
                rowStyleHover[2]='';
                rowActive[2]='N';
                rowStyle[3]='';
                rowStyleHover[3]='';
                rowActive[3]='N';
                rowStyle[4]='';
                rowStyleHover[4]='';
                rowActive[4]='N';
                rowStyle[5]='';
                rowStyleHover[5]='';
                rowActive[5]='N';
    
                function checkAll(masterCheckbox) {
                    if (masterCheckbox.checked) {
                        for (var i = 0; i<document.wwv_flow.f01.length; i++) {
                            if (document.wwv_flow.f01[i].checked==false) {
                              document.wwv_flow.f01[i].checked=true;
                              highlight_row(document.wwv_flow.f01[i],i);
                            }
                        }
                    } else {
                        rowsNotChecked=0;
                        for (var i = 0; i<document.wwv_flow.f01.length; i++) {
                           if (document.wwv_flow.f01[i].checked!=true) {
                               rowsNotChecked=rowsNotChecked+1;
                           }
                        }
                        if (rowsNotChecked==0) {
                            for (var i = 0; i<document.wwv_flow.f01.length; i++) {
                                if (document.wwv_flow.f01[i].checked==true) {
                                  document.wwv_flow.f01[i].checked=false;
                                  highlight_row(document.wwv_flow.f01[i],i);
                                }
                            }
                        }
                    }
                }
    
                function highlight_row(checkBoxElemement,currentRowNum) {
                    if(checkBoxElemement.checked==true) {
                        for( var i = 0; i < checkBoxElemement.parentNode.parentNode.childNodes.length; i++ ) {
                            if (checkBoxElemement.parentNode.parentNode.childNodes[i].tagName=='TD') {
                                if(rowActive=='Y') {
                                    rowStyle[currentRowNum] = rowStyleHover[currentRowNum];
                                } else {
                                    rowStyle[currentRowNum] = checkBoxElemement.parentNode.parentNode.childNodes[i].style.backgroundColor;
                                }
                                checkBoxElemement.parentNode.parentNode.childNodes[i].style.backgroundColor = '';
                            }
                        }
                        rowStyleHover[currentRowNum] =  '';
                    } else {
                          for( var i = 0; i < checkBoxElemement.parentNode.parentNode.childNodes.length; i++ ) {
                              if (checkBoxElemement.parentNode.parentNode.childNodes[i].tagName=='TD') {
                                  checkBoxElemement.parentNode.parentNode.childNodes[i].style.backgroundColor =  rowStyle[currentRowNum];
                                  rowStyleHover[currentRowNum] =  rowStyle[currentRowNum];
                                  document.wwv_flow.x02.checked=false;
                              }
                          }
                    }
                }
            // -->
            </script>
    

    The solution was to put in some extra start and end script tags in the report template and region template (remember we need two templates to work together to achieve the Ext grid). Originally our region template had an open script tag at the top and a close script tag after the #BODY# substitution string.We simply put a close script tag at the end of the report template and a new script start tag immediately after the #BODY# tag in the region template.

  3. Next Problem: Add Rows via page submission and the application process “Add Rows” would not work as it was not output by the report template. The apex engine auto generated this code so this was a big problem. Our solution (trust me there’s always one with anything, it can just take a little time to figure out) was to create a javascript function to add the new row to the grid. Its achieved by copying the last row in the Ext store and modifying the row HTML data before adding the extra row back to the store (note the actual row HTML has been generated by APEX not Ext). The HTML modification required incrementing input id’s and nulling values. I’d recommend that you familiarize yourself with regular expressions as it makes this task achievable in just a few lines of code.
    lRowArray[i] = lRowArray[i].replace(/(id=\"f\d+_).*?\"/g, '$1' + PadDigits(lastIndex,0,4) + '"');
    

    As for working out the right ID to increment the HTML input elements to i.e. “f1_0011″ we simply counted the records in the Ext store.

    That’s the great thing about Ext is that your report data is held in an accessible JSON object, and in this example our report data is not just data it also happens to be HTML, i.e. input fields and select lists so this means we don’t need to query the server for adding another row, which is more “end user” friendly.

    This gave us the benefit of being able to add more than one row at a time before committing the changes, as multiple “Add Row” clicks on a normal tabular form saves the new record each time.

  4. Next Problem: When copying the Ext store row we needed to make sure that the “fcs” hidden item, which is used for a row checksum to compare if the data had changed, contained a dummy value, as when we initially erased the value via a regex global replace the rows weren’t saved e.g.
    <input type="hidden" value="" name="fcs"/>

    was a problem. Solution: we simply changed it to

    <input type="hidden" value="12345" name="fcs"/>

    and the rows were then saved.

  5. Next Problem: we had to disable Ext grid filtering for dates and numbers. This was due to us defining the data type of the column in the grid column model and the Ext renderer replacing our HTML with “NaN” which is basically a return error identifier.

As we were missing the date picker field type we simply added an onload event to the Store which converted text items into Ext date pickers based on the items having a class setting of “ext-form-date-picker”. The class setting was set in the “Element Attributes” in the “Tabular Form Element” section. We also allowed all textareas to be manually resized e.g.

pStore.on({load: function() { extDateFields('grid'+pRegionID);extResizableByID('grid'+pRegionID); } });

// convert inputs with class "ext-form-date-picker" to date fields
function extDateFields(pID){
  var els=Ext.get(pID).select("[class=ext-form-date-picker]");
  els.each(function(el){
    if (el.dom.className.indexOf("aext-form-date") == -1) {
       el.dom.className += " "+"aext-form-date";
       var df = new Ext.form.DateField({"applyTo": el.dom.id, "format":'d-M-Y',"altFormats":'j|j/n|j/n/y|j/n/Y|j-M|j-M-y|j-M-Y'});
       df.render();
    }
  })
}
function extResizableByID(pID){
  var els=Ext.get(pID).select('textarea',true);
  els.each(function(e){
    if (e.dom.className.indexOf("aext-resizeable") == -1) {
       e.dom.className += " "+"aext-resizeable";
       var resizeMe = new Ext.Resizable(e, {
          wrap:true,
          pinned:true,
          width:e.getWidth(),
          height:e.getHeight(),
          minWidth:e.getWidth(),
          minHeight: e.getHeight()
       });
    }
  })
}

The end result is an APEX tabular form displayed in an Ext grid which supports inserts/updates/deletes, here’s an example from our development environment….

APEX ExtJS - APEX Tabular Form in an Ext Grid

Leave a Reply

preload preload preload