Thursday, April 8, 2021

Two ways to fetch the current records (Interactive Grid #10)

 




Last but not least tip no. 10!


#10 Two ways to fetch the current records

This will be the last blog in the series for now, but no less interesting than the others :-)

In this entry, we want to show how you can fetch the records of an interactive grid in two different ways. 
The first method is using a JavaScript function, as the second method we employ a PL/SQL function. Both are easy to implement. You can determine your approach depending on what you want to do with the data. Personally, I prefer PL/SQL and avoid JavaScript code whenever it´s possible but every developer thinks differently here :-)

Let's have a closer look at how we can do this.


1) Fetch records via JavaScript 

We start with a new application or page where we will add an interactive grid. In our example we use the table "EMP" as source. Then we give the Interactive Grid a Static ID, for example "my_ig". In our case we only want to fetch the values of the "EMPNO" column and save them in a page item. This is a very basic example, but it suffices to show how it works. Next we need a page item that we call "P1_EMPNO_JS". When that is done, we add a Dynamic Action and set the following:
  • Event: 'Page change (Interactive Grid)'
  • Selection type: 'Region' 
  • Region: 'The Interactive Grid you add to the page'
The Dynamic Action is fired every time the selection of the Interactive Grid changes. We can also use a button or some other DA event. The final thing we need is a 'True' action that executes JavaScript Code. Here we enter the following JavaScript function:


var l_empno;
// fetch the model for the interactive grid
var model = apex.region("my_ig").widget().interactiveGrid("getViews""grid").model;

// loop through the records
model.forEach(function(igrow) {
   if(l_empno == null){
       // first row without seperator ":"
       l_empno = igrow[model.getFieldKey("EMPNO")];
   }
   else {
       // add seperator ":" to each additional row
       l_empno = l_empno + ':' + igrow[model.getFieldKey("EMPNO")];
   }
}
);

// Set Page Item
apex.item"P1_EMPNO_JS" ).setValue (l_empno);


What we are doing here is getting the model of the Interactive Grid, and looping through each row and writing the value of "EMPNO" to a variable. At the end we store the result of the variable in a page item.

That´s it ;-)


2) Fetch records via PL/SQL 

In our second example we use the same Interactive Grid as before. We need a second Page Item, which we call "P1_EMPNO_SQL", and another Dynamic Action, which, as in the previous example, is also fired on a "Page change (Interactive Grid)" event. For the DA we need a 'True' action that executes PL/SQL code and enter the following:


declare
    l_region_id number;
    l_context   apex_exec.t_context
    l_empno_ids number
    l_empno     varchar2(1000 CHAR);
begin
    -- Get the region id for the IG
    select region_id
      into l_region_id
      from apex_application_page_regions
     where application_id = :APP_ID
       and page_id        = :APP_PAGE_ID
       and static_id      = 'my_ig';

    -- Get the query context for the IG
    l_context := apex_region.open_query_context (
                        p_page_id => :APP_PAGE_ID,
                        p_region_id => l_region_id );

    -- Get the column positions for column(s)
    l_empno_ids := apex_exec.get_column_position( l_context, 'EMPNO' );

    -- Loop through the query of the context
    while apex_exec.next_row( l_context ) loop 
        if l_empno is null then
            -- first row without seperator ":"
            l_empno := apex_exec.get_number( l_context, l_empno_ids );
        else    
            -- add seperator ":" to each additional row
            l_empno := l_empno || ':' || apex_exec.get_number( l_context, l_empno_ids );
        end if;    
    end loop;
    
    -- Set Page Item
    :P1_EMPNO_SQL := l_empno;

    -- Close query context
    apex_exec.close( l_context );
exception
      when others then        
        apex_exec.close( l_context );
      raise;
end;      


What we're doing here is the same process as before in our JavaScript function. We get the query context of the Interactive Grid, loop through each row and write the value "EMPNO" to a variable. Some APEX APIs are used to achieve this, which will not be described in further detail here, but we are going to have a closer look at them in future blogs, so keep an eye out.


The examples here are very basic but show what can be achieved with a bit of customization. You can do a lot this way, for example fetching the records of the IG in order to insert or update data in this or another table. Using the JavaScript approach, it is possible to make your page more interactive, for example to show or hide regions/items/buttons based on the selected data. 


Feel free to let us know if these tips and tricks helped you. 
If you have questions or suggestions, please leave us a comment ;-)


Here is the demo app for reference.


References:
  • https://docs.oracle.com/en/database/oracle/application-express/20.2/aexjs/grid.html
  • https://docs.oracle.com/en/database/oracle/application-express/20.2/aeapi/APEX_REGION-OPEN_QUERY_CONTEXT-Function.html#GUID-BDB9F4B7-D1A7-4C9A-B4C7-45A57AD76427
  • https://docs.oracle.com/en/database/oracle/application-express/20.2/aeapi/APEX_EXEC.html#GUID-3CF1D2DD-AEA4-4982-9857-548567AB7169

Labels: , ,

Wednesday, March 17, 2021

Customize your Toolbar (Interactive Grid #9)

  







Next to last tip no.9!


#9 Customize your Toolbar

As the previous blog entries mentioned, the interactive grid brings many out-of-the-box features on the one hand, but also many adjustments that can be made on the other. This means it is possible for the developer to customize the toolbar the way the customer wants. This article shows some examples of how you can customize the interactive grid Toolbar without much effort.

Before we can start, however, we should understand how the toolbar is structured.




As you can see in the figure, the toolbar consists of seven different menu groups (arrays) that can all be customized individually. In our example we will first hide all menu groups and then only the "action1"  menu group will be customized and displayed.


Let's start with a new application or page where we will add an interactive grid. In our example we use the table "EMP" as source. The toolbar of the interactive grid can be customized via JavaScript. For this we have to add some JavaScript Initialization Code under the attributes of the IG. First we will hide the unneeded menu groups and locate the menu group "actions1" that we want to customize.


function(config) {
    var $ = apex.jQuery,
    toolbarData = $.apex.interactiveGrid.copyDefaultToolbar(),  // Make a copy of the default toolbar
    toolbarGroup = toolbarData.toolbarRemove("search");         // Remove the search menu group
    toolbarGroup = toolbarData.toolbarRemove("reports");        // Remove the reports menu group
    toolbarGroup = toolbarData.toolbarRemove("views");          // Remove the views menu group
    toolbarGroup = toolbarData.toolbarRemove("actions2");       // Remove the actions2 menu group
    toolbarGroup = toolbarData.toolbarRemove("actions3");       // Remove the actions3 menu group
    toolbarGroup = toolbarData.toolbarRemove("actions4");       // Remove the actions4 menu group
    toolbarGroup = toolbarData.toolbarFind("actions1");         // Locate the actions menu group  
    
    // Add new control elements here

    // Assign new toolbar data back to toolbarData configuration property
    config.toolbarData = toolbarData;

    // Return the config
    return config;
}


After we have done that and started the application, we see that we have created an IG with a blank toolbar. The next step is to add some elements to the toolbar.

The most common element is probably the button. Let's add some button to the toolbar first. 
The structure is always the same. We declare a type, label, icon and an action to be executed.


    // Add new buttons
    // Note the toolbar is action driven, so we just need to define the correct action name with the button.
    toolbarGroup.controls.push({
        type: "BUTTON",
        label: "Add Row(s)",
        action: "selection-add-row",
        icon: "icon-ig-add-row",
        iconBeforeLabel: true,
        hot: true
    });    
    toolbarGroup.controls.push({
        type: "BUTTON",
        label: "Delete Row(s)",
        action: "selection-delete",
        icon: "icon-ig-delete",
        iconBeforeLabel: true,
        hot: true
    });
    toolbarGroup.controls.push({
        type: "BUTTON",
        label: "Save",
        action: "save",
        icon: "icon-ig-save",
        iconBeforeLabel: true,
        hot: true
    });


There are many default widgets for the interactive grid that we can use. For a complete reference, see the list below.




In our case we use the "Add Row", "Delete Row" and "Save Changes" action as an example, but we can also define and execute our own custom actions or trigger Dynamic Actions or an AJAX process. The next code snippet shows you how you can directly execute a JS command or trigger a Dynamic Action.  Note that you have to create the custom Dynamic Action, too.


    toolbarGroup.controls.push({
        type: "BUTTON",
        label: "My Button",
        icon: "fa fa-american-sign-language-interpreting",
        iconBeforeLabel: true,
        hot: true,
        action: "my_action"
    });
    toolbarGroup.controls.push({
        type: "BUTTON",
        label: "My DA Button",
        icon: "fa fa-play",
        iconBeforeLabel: true,
        hot: true,
        action: "my_da"
    });  
    
    config.initActions = function(actions){
        actions.add({
            name: "my_action",
            actionfunction() {
            alert('Hello World');
            }
        });
        
        actions.add({
            name: "my_da",
            actionfunction() {
            $.event.trigger('my_da');
            }
        });
    }


Now our toolbar should look like this.




Our next step in customizing the toolbar will be to add a select-list. To do this, we follow the same procedure as before and add another control element which is of type "SELECT". 


    // Add new select-list    
    toolbarGroup.controls.push({
        type: "SELECT",
        title: "Select",
        id: "my_select",
        action: "my_select_action"
    });    


Again, we need to also create a custom action to add values to the Select List and an event to be triggered.


    actions.add({
        name: "my_select_action",
        choices: [{ label: "All Jobs", value: "" },
                  { label: "President", value: "PRESIDE" }, 
                  { label: "Manager", value: "MANAGER" },
                  { label: "Analyst", value: "ANALYST" },
                  { label: "Clerk", value: "CLERK" },
                  { label: "Salesman", value: "SALESMAN" }                  
                 ],
        actionfunction(event, focusElement) {
        var e = document.getElementById("my_ig_ig_toolbar_my_select"),
            value = e.options[e.selectedIndex].value;
            apex.item"P1_JOB" ).setValue(value);
        }
    });


Here we create a list of the jobs and save the value in an item (P1_JOB). Now we can work with this value and, for example, filter our interactive grid. 
For this we create a hidden item which we also call "P1_JOB" and also a new "Change" Dynamic Action for that item. After that, we add a "True" action to refresh the grid. Finally adjust the WHERE clause of the grid and pick P1_JOB to "Page Items to Submit"




Now our toolbar should look like this and we can filter our IG by selecting a value from the Select List.




Finally we are going tol add a menu to the toolbar.

For this, we need a new control element of type "MENU". That is the easy part because we already know how to do it. However, defining the menu items is a little bit harder. Each entry has its own type, action, label and other options that we can define. Note: each additional entry needs a separator first.
In our example, we'll use the "Highlight" and "Download" pre-defined action, plus two other custom actions that will trigger an alert.


    // Add a Menu to the toolbar
    toolbarGroup.controls.push({type: "MENU",
        id: "my-menu",
        label: "My Menu",
        icon: "fa fa-navicon",
        iconBeforeLabel: true,
        hot: false,
        action: "custom-menu",
        // Add Menu Items (each additional entry needs a separator)
        menu: {
            items: [{
                type: "action",
                action: "show-highlight-dialog",
                label: "Highlight"
            }, {
                type: "separator"
            }, {
                type: "action",
                action: "show-download-dialog",
                label: "Download"
            }, {
                type: "separator"
            }, {
                type: "action",
                actionfunction(){ alert('Action 1');},
                label: "Action 1",
                icon: "fa fa-number-1-o"
            }, {
                type: "separator"
            }, {
                type: "action",                
                label: "Action 2",
                actionfunction(){ alert('Action 2');},
                icon: "fa fa-number-2-o"
            }]
        }
    });


So thats it. Your toolbar should now look like this.



The finished JavaScript Code should be the following:


function(config) {
    var $ = apex.jQuery,
    toolbarData = $.apex.interactiveGrid.copyDefaultToolbar(),  // Make a copy of the default toolbar
    toolbarGroup = toolbarData.toolbarRemove("search");         // Remove the search menu group
    toolbarGroup = toolbarData.toolbarRemove("reports");        // Remove the reports menu group
    toolbarGroup = toolbarData.toolbarRemove("views");          // Remove the views menu group
    toolbarGroup = toolbarData.toolbarRemove("actions2");       // Remove the actions2 menu group
    toolbarGroup = toolbarData.toolbarRemove("actions3");       // Remove the actions3 menu group
    toolbarGroup = toolbarData.toolbarRemove("actions4");       // Remove the actions4 menu group
    toolbarGroup = toolbarData.toolbarFind("actions1");         // Locate the actions menu group  
    
    // Add new buttons
    // Note the toolbar is action driven, so we just need to define the correct action name with the button.
    toolbarGroup.controls.push({
        type: "BUTTON",
        label: "Add Row(s)",
        action: "selection-add-row",
        icon: "icon-ig-add-row",
        iconBeforeLabel: true,
        hot: true
    });    
    toolbarGroup.controls.push({
        type: "BUTTON",
        label: "Delete Row(s)",
        action: "selection-delete",
        icon: "icon-ig-delete",
        iconBeforeLabel: true,
        hot: true
    });
    toolbarGroup.controls.push({
        type: "BUTTON",
        label: "Save",
        action: "save",
        icon: "icon-ig-save",
        iconBeforeLabel: true,
        hot: true
    });
    toolbarGroup.controls.push({
        type: "BUTTON",
        label: "My Button",
        icon: "fa fa-american-sign-language-interpreting",
        iconBeforeLabel: true,
        hot: true,
        action: "my_action"
    });
    toolbarGroup.controls.push({
        type: "BUTTON",
        label: "My DA Button",
        icon: "fa fa-play",
        iconBeforeLabel: true,
        hot: true,
        action: "my_da"
    });  

    // Add new select-list
    toolbarGroup.controls.push({
        type: "SELECT",
        title: "Select",
        id: "my_select",
        action: "my_select_action"
    }); 

    // Add a Menu to the toolbar
    toolbarGroup.controls.push({type: "MENU",
        id: "my-menu",
        label: "My Menu",
        icon: "fa fa-navicon",
        iconBeforeLabel: true,
        hot: false,
        action: "custom-menu",
        // Add Menu Items (each additional entry needs a separator)
        menu: {
            items: [{
                type: "action",
                action: "show-highlight-dialog",
                label: "Highlight"
            }, {
                type: "separator"
            }, {
                type: "action",
                action: "show-download-dialog",
                label: "Download"
            }, {
                type: "separator"
            }, {
                type: "action",
                actionfunction(){ alert('Action 1');},
                label: "Action 1",
                icon: "fa fa-number-1-o"
            }, {
                type: "separator"
            }, {
                type: "action",                
                label: "Action 2",
                actionfunction(){ alert('Action 2');},
                icon: "fa fa-number-2-o"
            }]
        }
    });

    config.initActions = function(actions){
        actions.add({
            name: "my_action",
            actionfunction() {
            alert('Hello World');
            }
        });
        actions.add({
            name: "my_da",
            actionfunction() {
            $.event.trigger('my_da');
            }
        });
        actions.add({
            name: "my_select_action",
            choices: [{ label: "All Jobs", value: "" },
                      { label: "President", value: "PRESIDE" }, 
                      { label: "Manager", value: "MANAGER" },
                      { label: "Analyst", value: "ANALYST" },
                      { label: "Clerk", value: "CLERK" },
                      { label: "Salesman", value: "SALESMAN" }                  
                     ],
            actionfunction(event, focusElement) {
            var e = document.getElementById("my_ig_ig_toolbar_my_select"),
                value = e.options[e.selectedIndex].value;
                apex.item"P13_JOB" ).setValue(value);
            }
        });
        }; 

    // Assign new toolbar data back to toolbarData configuration property
    config.toolbarData = toolbarData;

    // Return the options
    return config;
}


The following elements can also be added in the same way, but we will not go into that further here: 
  • Static Text
  • Textfield
  • Radio button
  • Toggle switch

These were very basic examples. Try anything you want. Many things are possible and surly will be fun for you :-)


We hope these tips and tricks will help you. 
If you have questions or suggestions, please leave us a comment ;-)


Here is the demo app for reference.


References:
https://docs.oracle.com/en/database/oracle/application-express/20.2/aexjs/grid.html

Labels: , ,

Thursday, March 11, 2021

Customize the Column Heading Menu (Interactive Grid #8)

 





Here we go with tip no.8!


#8 Customize the Column Heading Menu

The Interactive Grid is very popular in development, but not all the features that the IG brings are always necessary and can overwhelm the user. Therefore it is sometimes welcome to reduce some functions of the IG. In this post we will show you a few examples how to customize the "Column Heading Menu".

First, we will disable all header activation for a specific column. So when you click on the header no menu open anymore. The implementation is quite simple. A JavaScript function has to be added to each column where no menu is to be displayed. To do this, click on the specific column in the APEX Builder and enter the following JavaScript initialization code:


function(options) { 
    options.defaultGridColumnOptions = { 
       noHeaderActivate: true 
     } 
     return options; 
   }


If this function is to be used for several columns, it is recommended to create a global function and call it for each column ;-)


In the APEX Builder, you can deactivate or activate options without having to write additional code. At the moment there are three options -> Hide, Control Break/Aggregate and Sort. However, if you only want to deactivate aggregate and display Control Break, for example, additional development is required. To do this, we do the same as in the first case and add a JavaScript code to the column again, which is as the following:


function(options) {
    options.features = options.features || {};
    options.features.aggregate = false;
    return options;
}


If all columns are to be affected, we can run the function in a loop for each column. In this case, however, the JavaScript function must be added to the Interactive Grid, not to the column. Click Attributes on the IG and enter the following JS code in the JavaScript initialization code:


function(options) { 
    options.columns.forEach(col => { 
       col.features = col.features || {};
       col.features.aggregate = false;       
     }) 
     return options; 
}


The next and final example shows how to hide all (or just the required) options for all columns in one JavaScript function. First we give the Interactive Grid a Static-Id, for example "my_ig". Then we add the following "Execute when Page Load" JavaScript Function:


$("#my_ig").on("gridactivatecolumnheader"function(e){
    setTimeout(function() {
        $("#my_ig_ig_column_header_menu").find("[data-option='freeze']").remove();
        $("#my_ig_ig_column_header_menu").find("[data-option='hide']").remove();
        $("#my_ig_ig_column_header_menu").find("[data-option='aggregate']").remove();
        $("#my_ig_ig_column_header_menu").find("[data-option='break']").remove();
    },1);
});


Nothing more to do :-)


We hope the tips and tricks will help you. 
If you have questions or suggestions, please leave us a comment ;-)


And here is the demo app.


References:
https://docs.oracle.com/en/database/oracle/application-express/20.2/aexjs/grid.html#columns

Labels: , ,

Friday, December 18, 2020

Column "Read-only" Function (Interactive Grid #7)





Let's have a look at cheat number seven :-) 


#7 Column "Read-only" Function

This tip mostly comes Out-of-the-Box from Oracle APEX. It is so simple that we don't think about it, because as a developer we often think too complicated.

Ok, what do we want? The target is to lock cells in the interactive grid in case that another cell in the same row has a predefined value. To realize this, APEX has the Read-only function, which we can use perfectly here. 

But we also have another attribute available for the interactive grid -> Execute
The options for the Execute attribute are "For Each Row" and "Once".

The following is written in the documentation:
If you refer to Columns in your read only condition, you must specify For Each Row in order for the condition to be evaluated for each row of the Interactive Grid. Otherwise, set to Once to evaluate the condition only one time for the region.

By default the attribute is "Once", although the documentation says that you have to specify "For Each Row" in the interactive grid.

Enough theory...let's start with a small demo app.

For this example we use the sample dataset "EMP / DEPT", in which we have the tables "EMP" and "DEPT". Then we create an application with an interactive grid or create a new page in an existing application. As source we use the table "EMP".

Now we have an interactive grid where we can add "Read-only" columns. To do this, we go to the page designer and click for example the column "Sal". Next go to the Read-Only attribute and set for this column the Type to "Item != Value", Item to "Deptno", Value to "30" and Execute to "For each Row".




When the application is started now, we can already see the result that the cells can only be edited in Department 30. However, if the department is changed now, our read-only condition does not change. To do this we need a dynamic action that saves and re-initializes the values of the grid.

For that, we create a new dynamic action that is triggered by an change event. The Selection Type must be "Column(s)" and the column "DEPTNO". Then we create a "True" action that Execute JavaScript Code and enter the following in the Code-Editor:


apex.message.confirm"Do you really want to change the Department?"function( okPressed ) { 
if( okPressed ) {
   apex.region("my_ig").call("getActions").invoke("save");
}        
});


First line is only a message box that asked you if you really want to change the value. 
If you clicked "OK", the Interactive Grid save the modified cells by calling the "Save" event (line 3).

Please note that "my_ig" is the static id from the Interactive Grid. 
So don´t forget to set the static id :-)


So, that´s it...and here is the demo app.


We hope the tips and tricks will help you. 
If you have questions or suggestions, please leave us a comment

Labels: , ,

Thursday, November 26, 2020

Column groups heading and styling (Interactive Grid #6)

 




It´s time to continue our tips and tricks series about the Interactive Grid, that have often helped us in the development process.

Let's start with tip six :-) 


#6 Column groups heading and styling

This tip is actually very simple and comes mostly out of the box. In the past we added column groups by using CSS and JavaScript. But APEX also offers a direct low code solution that we would like to share with you. With a little bit of CSS styling on top we can design the column headers and groups as we would like. Let's start with a small demo app.

For this example we use the sample dataset "EMP / DEPT", in which we have the tables "EMP" and "DEPT" and also a view "EMP_DEPT_V" which combines both tables. Then we create an application with an interactive grid or create a new page in an existing application. As source we use the view "EMP_DEPT_V".

Now we have an interactive grid where we can add column groups. To do this, we go to the page designer, right-click under the column group regions (rendering tree) and select "Create Column Group" from the context menu. Set the Heading attribute for this new group to „Employee“. Then create another column group and set the heading attribute to „Department“.




Next, each column must be linked to a group. To establish this assignment, click on any column and in the Layout section set the "Group" property.




Done. Now we've added column groups to the interactive grid without writing any code.




But next we want to style the column groups and column headings and for that we need a little bit CSS and JavaScript code.

First we give the interactive grid a static id, for example "my_ig". Then we add some CSS code in the CSS Inline Code-Editor to style the column groups. In our example we are changing the background color of the column groups, but feel free to style anything you need.


#my_ig  .a-GV-headerGroup[data-idx="0"] { 
  background-color#cdcdcd;
}

#my_ig  .a-GV-headerGroup[data-idx="1"] { 
  background-color#31869b;
}


To find the right CSS object, right- click on the column group you want to style and click on inspect in the context menu to open the browser development tool.




Indeed, we want to give the column headings a background color too. But there is no declarative CSS attribute for the column header cell! So we need advanced column JavaScript Initialization Code like this.


function(config) {
    config.defaultGridColumnOptions = {
        headingCssClasses: "EmployeeHeader"
    }
    return config;
}


Enter this code to all columns assigned to the "Employees" group. Then repeat this step for all columns assigned to "Department" and enter the following in the JavaScript Code Editor.


function(config) {
    config.defaultGridColumnOptions = {
        headingCssClasses: "DepartmentHeader"
    }
    return config;
}


After that we can add a CSS rule for the column headings in the inline editor on the page. For example, enter the following to apply the colors of the column groups to the headers as well.


#my_ig .EmployeeHeader { 
  background-color#cdcdcd;
}
  
#my_ig .DepartmentHeader { 
  background-color#31869b;
}


That´s it. With a little bit extra Code of CSS and JavaScript, we could easily add colomn groups to an interactive grid and format them as we like.




Optional hint:

The better solution would be to condense the column javascript code into a single function so that you can link these functions to the columns you need. But in our tiny example, this quick and easy method is enough to show you the way you can hande it.


And here is the demo app.


We hope the tips and tricks will help you. 
If you have questions or suggestions, please leave us a comment

Labels: , , , ,