1 2 Previous Next 17 Replies Latest reply on Nov 2, 2018 7:28 PM by partlycloudy

    IG download - Dynamic filename

    partlycloudy

      APEX 18.2

       

      Actions > Download allows us to download report data in, say, CSV format. The filename can be made dynamic using &P1_ITEM. substitution and setting the value using a on-load computation.

       

      But this is set when the page is first rendered. How can I get a dynamic filename for every download with, for instance, to_char(sysdate,'SSSSS') in the filename so this sequence component is a) guaranteed to be unique b) monotonically increasing c) resets every day?

       

      https://docs.oracle.com/database/apex-18.2/AEXJS/interactiveGrid.html#events-section

       

      Is there a beforedownload event or something like that so we can set a page item value in session state before the download starts?

       

      The initActions option looks promising but when I added the example showed in the documentation to the IG Attributes > Advanced > Javascript initialization code, the a) Help dialog is indeed suppressed and b) the shortcut is added to the filter dialog but the new action is not added to the Actions menu. Strange.

       

      If I define a global variable g and grab a reference to it in the initialization code, when I do g.lookup("my-action").action(), the function (alert in this case), is executed but the UI doesn't show the new menu action. I even tried adding actions.update("my-action") after adding the action but that didn't help either.

       

      John Snyders-Oracle What am I missing?

       

       

      function( options ) {
          options.initActions = function( actions ) {
      
      
              // Hide all elements associated with the show help dialog action
              actions.hide( "show-help-dialog" );
      
      
              // Add a keyboard shortcut to the show filter dialog action
              actions.lookup( "show-filter-dialog" ).shortcut = "Ctrl+Alt+F";
              actions.update( "show-filter-dialog" );
      
      
              // Add new actions, either singularly passing in an actions object as shown here, or in
              // multiple by passing an array of action objects
              actions.add( {
                  name: "my-action",
                  label: "Hello",
                  action: function( event, focusElement ) {
                      alert( "Hello World!" );
                  }
              } );
              actions.update("my-action");
              console.log(actions);
              g=actions;
          };
          return options;
      }
      

       

      Any ideas? Thanks

        • 1. Re: IG download - Dynamic filename
          Alli Pierre Yotti

          I will answers now the first Question. The another question will come later.

           

          - create a Item : P43_ITEM

          - Add that Item as filename: &P43_ITEM.

          - create a Button   and put than Button in a Region with position "inline Dialog". The Inline Dialog Position help us to not display the Button to the User.

          - create a DA on click of the above Button

          - action : execute pl/sql code

               pl/sql code : :P43_ITEM := 'filename'||to_char(sysdate,'SSSSS');

             Item to return : P43_ITEM

          Bildschirmfoto 2018-10-31 um 22.15.41.png

           

          - Call that Code on Page Load to hide the default download action menu Item

          $("#emp_ig_toolbar_actions_button_menu").on("menubeforeopen", function(event, ui) {

              apex.region("emp").widget().interactiveGrid("getActions").hide("show-download-dialog");

          });

           

          - Create a DA on Page load to add a new Menu Item to open the Download Dialog

          $(function() {

          $("#emp_ig_toolbar_actions_button_menu").menu("option").items.push({

                      type: "action",

                      id: "irHello",

                      hide: false,

                      label: 'download',

                      icon: 'fa fa-download',

                      action: function() {

                        apex.region("emp").widget().interactiveGrid("getActions").show("show-download-dialog");//show the default download action menue item again

                        apex.event.trigger('#myLink', 'click'); //submit the above DA

                          setTimeout(function(){  apex.region("emp").widget().interactiveGrid("getActions").invoke("show-download-dialog"); }, 1000);

                      

                      }

                  });

            });

           

          Demo

          https://apex.oracle.com/pls/apex/f?p=23342:43

          • 2. Re: IG download - Dynamic filename
            Alli Pierre Yotti

            - Now to the  add action function

            actions.add({

                name: "my-action",

                label: "Hello",

                action: function(event, focusElement) {

                    alert("Hello World!");

                }

            });

             

            "Add an actions.action object or an array of actions.action objects to this actions context. The action name must be unique within the context and the shortcut if any must be unique within the context and valid. Debug warnings are logged if any of these conditions are not met. "

            https://docs.oracle.com/database/apex-18.2/AEXJS/actions.html#add

             

            You can also use that to add a action

             

            apex.actions.add({

                name: "my-action",

                label: "Hellol",

                action: function(event, focusElement) {...}

            });

             

             

            But the next and interest step is to say the IG where you want to use that action. That means you need a Menu Item to use that action. For example the action that you add above, i want to use it it the action menu for the IG

            $("#emp_ig_toolbar_actions_button_menu").menu("option").items.push({

                type: "action",

                action: 'my-action'

            });

             

            - Result

            Bildschirmfoto 2018-10-31 um 22.39.04.png

             

            Demo

            https://apex.oracle.com/pls/apex/f?p=23342:43

            • 3. Re: IG download - Dynamic filename
              John Snyders-Oracle

              Hi,

              As Alli has correctly pointed out simply adding an action does not surface it in the UI anywhere. That is an extra step. However, I do not recommend using code such as
                  $("#emp_ig_toolbar_actions_button_menu").menu("option").items.push...

              because it makes use of internal knowledge of how element ids are generated and if the toolbar widget were ever refreshed your change to the menu would be lost.

               

              With IG it is best to use the $.apex.interactiveGrid.copyDefaultToolbar() method during Advanced JavaScript Initialization code. There are a number of examples of this in the Sample Interactive Grids app and the IG Cookbook app. But I think they all just modify the IG toolbar rather than the actions menu. But the actions menu is defined as part of the toolbar structure so you can modify that as well.

               

              I assume the global g variable is just for testing. You wouldn't want to do that normally. So

              g.lookup("my-action").action()

              is a shortcut for

              apex.region("emp").call("getActions").lookup("my-action").action()

              or even better

              apex.region("emp").call("getActions").invoke("my-action")

               

              As for setting the download filename after the page has rendered I have not tried to do that. It looks like Alli's technique of using a DA PL/SQL code to set the item on the server before invoking the download is reasonable.

               

              Regards,
              -John

              • 4. Re: IG download - Dynamic filename
                partlycloudy

                A long time ago, someone told me to not just copy code but understand it first ;-) and there are several things I don't agree/understand about the proposed solution. (Side note - Using the forum's Advanced Editor and using code/syntax highlighting automaticaly adds line numbers to code, making it easier to discuss, please consider doing this, thanks)

                 

                I ended up implementing it differently, based 100% on what I read in the documentation

                 

                Question/comments, in no particular order

                1. Using a hidden page item, the native Set Value DA and triggering it using apex.event.trigger, waiting for 1 second and then calling the built-in show-download-dialog looks convoluted to me
                2. I am not clear on why the custom action does not automatically get added to the UI, the documentation for the actions interface states that the show() method shows UI elements associated with the action by setting the hide property to false. This is a convenience method to show without having to call actions#lookup and actions#update. Why does this not work for Interactive Grid?!
                3. The documentation for initActions has an example to hide a built-in  Actions menu item using actions.hide( "show-help-dialog" ); Why does this not work with actions.hide( "show-download-dialog" );?!!
                4. I agree with John that using $("#emp_ig_toolbar_actions_button_menu").menu("option").items.push... is not a good idea because it is not documented and subject to change. John Snyders-Oracle what is the recommended way to access the IG menu widget? You said the actions menu is defined as part of the toolbar structure so you can modify that as well, could you please share an example?

                 

                https://apex.oracle.com/pls/apex/f?p=1855:3

                 

                Here is what I ended up doing

                 

                1. Function & Global Variable

                 

                function f_set_filename(p_callback){
                    apex.server.process(
                        "set_filename",
                        {},
                        {
                            success: function( data )  {         
                                p_callback();
                            },
                        }
                    );   
                }
                

                 

                2. Hidden page item P3_FILENAME and AJAX callback process (set_filename) as follows

                 

                :P3_FILENAME := 'filename_'||to_char(sysdate,'SSSSS');
                htp.p('{}');
                

                 

                3. IG Attributes > JS Initialization code as follows and Download filename as &P3_FILENAME.

                 

                function( config ) {  
                    config.initActions = function( actions ) {          
                        actions.hide( "show-help-dialog" );  // Hide all elements associated with the show help dialog action  
                        actions.hide("show-download-dialog");  // This does not work, why?!
                        
                        // Add a keyboard shortcut to the show filter dialog action  
                        actions.lookup( "show-filter-dialog" ).shortcut = "Ctrl+Alt+F";  
                        actions.update( "show-filter-dialog" );                    
                        
                        actions.add({
                            name: "my-action",
                            action: function(event, focusElement) {
                                f_set_filename(function() {
                                    actions.invoke("show-download-dialog");
                                });
                            }
                        });
                    };      
                     
                    var $ = apex.jQuery,
                       toolbarData = $.apex.interactiveGrid.copyDefaultToolbar(), // Make a copy of the default toolbar
                       actionsMenuGroup = toolbarData.toolbarFind( "actions1" );  // Locate the actions menu group
                
                
                   // Array position denotes displayed position in the toolbar, so let's add the new download button directly
                   // after the actions menu group in the array, such that it displays directly after the actions menu in the
                   // toolbar.
                   // Note: The toolbar is action-driven, so integrates easily with the Interactive Grid. To show the dialog, we
                   // just define the appropriate action for showing the download dialog (show-download-dialog)
                            
                   actionsMenuGroup.controls.push( {
                       type: "BUTTON",
                       action: "my-action",
                       label: "Download",
                       icon: "fa fa-download",
                       iconBeforeLabel: true
                   });
                
                
                    // Assign new toolbar data back to toolbarData configuration property
                    config.toolbarData = toolbarData;
                    return config;  
                }  
                

                 

                John - Your help with the couple of unanswered questions above would be appreciated.

                 

                Thanks

                • 5. Re: IG download - Dynamic filename
                  Alli Pierre Yotti

                  partlycloudy wrote:

                  A long time ago, someone told me to not just copy code but understand it first ;-) and there are several things I don't agree/understand about the proposed solution. (Side note - Using the forum's Advanced Editor and using code/syntax highlighting automaticaly adds line numbers to code, making it easier to discuss, please consider doing this, thanks)

                   

                  I ended up implementing it differently, based 100% on what I read in the documentation

                   

                  Question/comments, in no particular order

                  1. Using a hidden page item, the native Set Value DA and triggering it using apex.event.trigger, waiting for 1 second and then calling the built-in show-download-dialog looks convoluted to me
                  2. I am not clear on why the custom action does not automatically get added to the UI, the documentation for the actions interface states that the show() method shows UI elements associated with the action by setting the hide property to false. This is a convenience method to show without having to call actions#lookup and actions#update. Why does this not work for Interactive Grid?!
                  3. The documentation for initActions has an example to hide a built-in  Actions menu item using actions.hide( "show-help-dialog" ); Why does this not work with actions.hide( "show-download-dialog" );?!!
                  4. I agree with John that using $("#emp_ig_toolbar_actions_button_menu").menu("option").items.push... is not a good idea because it is not documented and subject to change. John Snyders-Oracle what is the recommended way to access the IG menu widget? You said the actions menu is defined as part of the toolbar structure so you can modify that as well, could you please share an example?

                  thanks for the suggestion. I will use it definitely

                   

                  - Using callback is a great Idea, you can use  a load indicator to show the user that a Process is running in Background and he has to wait. It is better for the usability.

                   

                  function f_set_filename(p_callback) {
                      apex.server.process(
                          "set_filename", {}, {
                              loadingIndicator: '#static_id',
                              loadingIndicatorPosition: "centered",
                              success: function(data) {
                                  p_callback();
                              },
                          }
                      );
                  }
                  

                   

                   

                  Using a hidden page item, the native Set Value DA and triggering it using apex.event.trigger, waiting for 1 second and then calling the built-in show-download-dialog looks convoluted to me

                  - apex.event.trigger trigger a PL/SQL Process that is why it take a few seconde. But it is okay.

                   

                  I agree with John that using $("#emp_ig_toolbar_actions_button_menu").menu("option").items.push... is not a good idea because it is not documented and subject to change. John Snyders-Oracle what is the recommended way to access the IG menu widget? You said the actions menu is defined as part of the toolbar structure so you can modify that as well, could you please share an example?

                   

                  menu("option").items.push is definitely the way to add action item in the Menu. But not it the Toolbar.

                   

                  - In the Toolbar you can use that way

                   

                  function(options) {
                      var $ = apex.jQuery,
                          toolbarData = $.apex.interactiveGrid.copyDefaultToolbar(), // Make a copy of the default toolbar
                          actionsMenuGroup = toolbarData.toolbarFind("actions1"); // Locate the actions menu group
                  
                  
                      // Array position denotes displayed position in the toolbar, so let's add the new download button directly
                      // after the actions menu group in the array, such that it displays directly after the actions menu in the
                      // toolbar.
                      // Note: The toolbar is action-driven, so integrates easily with the Interactive Grid. To show the dialog, we
                      // just define the appropriate action for showing the download dialog (show-download-dialog).
                      actionsMenuGroup.controls.push({
                          type: "BUTTON",
                          action: "show-download-dialog",
                          iconBeforeLabel: true
                      });
                  
                  
                      // Assign new toolbar data back to toolbarData configuration property
                      options.toolbarData = toolbarData;
                  
                  
                      // Return the options
                      return options;
                  }
                  

                   

                   

                  I am not clear on why the custom action does not automatically get added to the UI, the documentation for the actions interface states that the show() method shows UI elements associated with the action by setting the hide property to false. This is a convenience method to show without having to call actions#lookup and actions#update. Why does this not work for Interactive Grid?!

                   

                  I am not sure if i can explain it the best way. But i will try. Action is like a verb, a property to do something. To use the action, you need something that we use it. That thing is calling a action item

                  • 6. Re: IG download - Dynamic filename
                    partlycloudy

                    Thanks but my questions remain unanswered.

                     

                    In Question #2, either the documentation is incorrect or this is a bug in the actions.hide/show() method. #3 is even more puzzling, the .hide() method updates the menu UI for one action but not another!

                     

                    In #4 - I understand how to add a button to the toolbar, my question was how to add a new menu item to the IG Actions menu using the documented copyDefaultToolbar method. John hinted that this is possible but I don't understand how.

                     

                    Thanks

                    • 7. Re: IG download - Dynamic filename
                      Alli Pierre Yotti

                      partlycloudy wrote:

                      Thanks but my questions remain unanswered.

                       

                      In Question #2, either the documentation is incorrect or this is a bug in the actions.hide/show() method. #3 is even more puzzling, the .hide() method updates the menu UI for one action but not another!

                       

                      In #4 - I understand how to add a button to the toolbar, my question was how to add a new menu item to the IG Actions menu using the documented copyDefaultToolbar method. John hinted that this is possible but I don't understand how.

                       

                      Sure you can do it with the copyDefaultToolbar like this

                       

                      function(config) {
                        let $ = apex.jQuery,
                          toolbarData = $.apex.interactiveGrid.copyDefaultToolbar(), 
                          toolbarGroup = toolbarData.toolbarFind("actions1"); 
                        console.log(toolbarData)
                        console.log(toolbarGroup.controls)
                        toolbarGroup.controls[0].menu.items.push({
                          type: "subMenu",
                          id: "static",
                          label: "Hello world",
                          icon: "icon-ig-save-as",
                          iconBeforeLabel: true,
                          iconOnly: false,
                          hot: true,
                          disabled: false,
                          menu: {
                            items: [{
                                type: "action",
                                action: function() {
                                  alert('action 1');
                                },
                                label: "Customer world",
                                icon: "fa fa-home",
                                disabled: false
                              }, {
                                type: "separator"
                              }, {
                                type: "action",
                                action: function() {
                                  alert('action 2');
                                },
                                label: "action 2",
                                icon: "fa fa-apex",
                                disabled: true
                              },
                              {
                                type: "separator"
                              },
                              {
                                type: "action",
                                action: function() {
                                  alert('action 1');
                                },
                                label: "salut world",
                                icon: "fa fa-home",
                                disabled: false
                              }
                            ]
                          }
                        });
                      
                        toolbarGroup.controls[0].menu.items.push({
                          type: "action",
                          id: "static",
                          label: "My Menu",
                          icon: "icon-ig-save-as",
                          iconBeforeLabel: true,
                          iconOnly: false,
                          hot: true,
                          disabled: false,
                          action: function() {
                            alert(1)
                          }
                        });
                        config.toolbarData = toolbarData;
                        return config;
                      }
                      

                       

                      Demo

                      https://apex.oracle.com/pls/apex/f?p=23342:11

                      • 8. Re: IG download - Dynamic filename
                        partlycloudy

                        Ah I see, I was missing the toolbarGroup.controls[0].menu.items part  since the structure of the toolbarData object is not documented in depth, just briefly.

                         

                        Inspecting the toolbarData object shows that the controls array contains the menu object used to render the IG Actions menu, got it, thanks!

                         

                        My questions #2 and #3 remain un-addresed, hopefully John can chime in.

                         

                        Thanks

                         

                        • 9. Re: IG download - Dynamic filename
                          Alli Pierre Yotti
                          My questions #2 and #3 remain un-addresed, hopefully John can chime in.

                           

                          Sure that will works. The problem is that not all action are initialized when the IG load.

                          That's why we have to wait until the IG is ready to hide the action

                           

                          config.initActions = function(actions) {
                              $(function() {
                                  actions.hide("show-download-dialog");
                              });
                          };
                          
                          • 10. Re: IG download - Dynamic filename
                            partlycloudy

                            Hmmm this is interesting.

                             

                            Line 4 and Line 6 are doing the same thing but Line 6 is wrapped in a jQuery pattern which seems like a short-hand for document.ready() which is odd.

                             

                            Besides, why is this technique NOT needed for Line 3?!

                             

                            function( config ) {    
                                config.initActions = function( actions ) {            
                                    actions.hide( "show-help-dialog" );  // Hide all elements associated with the show help dialog action    
                                    actions.hide("show-download-dialog");  // This does not work, why?!  
                                    $(function(){ // But this does! Huh?!
                                            actions.hide("show-download-dialog");
                                        }
                                     );
                            

                             

                             

                            The problem is that not all action are initialized when the IG load. That's why we have to wait until the IG is ready to hide the action

                             

                            Ok that explains it, makes sense now. Thanks for your patience.

                             

                            My take-away from this thread is that John Snyders-Oracle and team have done an amazing job documenting all these advanced Javascript APIs but more work needs to be done to point out nuances like these!

                             

                            Thanks again

                            • 11. Re: IG download - Dynamic filename
                              Alli Pierre Yotti

                              partlycloudly wrote:

                               

                              Hmmm this is interesting.

                               

                              Line 4 and Line 6 are doing the same thing but Line 6 is wrapped in a jQuery pattern which seems like a short-hand for document.ready() which is odd.

                               

                              Besides, why is this technique NOT needed for Line 3?!

                               

                              as i said the said, the "show-download-dialog" is not in the UI/view when the Page is loading. That is because we have to wait until the IG is ready, than we can hide it.

                               

                              I suggest you to put all the action in the anonym function. So you are sure there will be hide without issues

                               

                                config.initActions = function(actions) {
                                  $(function() {
                                      actions.hide( "show-help-dialog" );       
                                      actions.hide("show-download-dialog"); 
                                  });
                              };
                              
                              • 12. Re: IG download - Dynamic filename
                                partlycloudy
                                as i said the said,

                                 

                                Minor point of note - When I typed my prior response, I didn't see your latest, updated, response! Editing the same post over and over instead of adding new replies is not ideal, especially when multiple users (like us) are posting to the thread rapidly! I try to avoid it  but don't always succeed. Maybe the forum software should disallow it, similar to a instant messaging platform!

                                 

                                Thanks

                                • 13. Re: IG download - Dynamic filename
                                  partlycloudy

                                  https://apex.oracle.com/pls/apex/f?p=1855:3

                                   

                                  I realized that my solution doesn't quite work when I add the $(function() {  actions.hide("show-download-dialog");  });  Clicking on the Download button in the toolbar does not invoke the show-download-dialog action.

                                   

                                  In other words, hiding the Download action from the Actions menu also makes it unavailable to actions invoked by toolbar buttons!

                                   

                                  Any ideas?

                                  • 14. Re: IG download - Dynamic filename
                                    Alli Pierre Yotti

                                    partlycloudy wrote:

                                     

                                    https://apex.oracle.com/pls/apex/f?p=1855:3

                                     

                                    I realized that my solution doesn't quite work when I add the $(function() {  actions.hide("show-download-dialog");  });  Clicking on the Download button in the toolbar does not invoke the show-download-dialog action.

                                     

                                    In other words, hiding the Download action from the Actions menu also makes it unavailable to actions invoked by toolbar buttons!

                                     

                                    Any ideas?

                                     

                                    Yeah right. It does not work because the "show-download-dialog" action does not exist in the UI. You hid it. That is why you should add it in the UI before invoke "show-download-dialog"

                                     

                                     

                                    That will works for you

                                     

                                    function(config) {
                                        config.initActions = function(actions) {
                                            actions.hide("show-help-dialog"); // Hide all elements associated with the show help dialog action    
                                            actions.hide("show-download-dialog"); // This does not work, why?!  
                                    
                                            // Add a keyboard shortcut to the show filter dialog action    
                                            actions.lookup("show-filter-dialog").shortcut = "Ctrl+Alt+F";
                                            actions.update("show-filter-dialog");
                                    
                                    
                                            actions.add({
                                                name: "my-action",
                                                action: function(event, focusElement) {
                                                    f_set_filename(function() {
                                                         actions.show("show-download-dialog");
                                                        actions.invoke("show-download-dialog");
                                                    });
                                                }
                                            });
                                        };
                                    
                                    
                                        var $ = apex.jQuery,
                                            toolbarData = $.apex.interactiveGrid.copyDefaultToolbar(), // Make a copy of the default toolbar  
                                            actionsMenuGroup = toolbarData.toolbarFind("actions1"); // Locate the actions menu group  
                                    
                                    
                                    
                                    
                                        // Array position denotes displayed position in the toolbar, so let's add the new download button directly  
                                        // after the actions menu group in the array, such that it displays directly after the actions menu in the  
                                        // toolbar.  
                                        // Note: The toolbar is action-driven, so integrates easily with the Interactive Grid. To show the dialog, we  
                                        // just define the appropriate action for showing the download dialog (show-download-dialog)  
                                    
                                    
                                        actionsMenuGroup.controls.push({
                                            type: "BUTTON",
                                            action: "my-action",
                                            label: "Download",
                                            icon: "fa fa-download",
                                            iconBeforeLabel: true
                                        });
                                    
                                    
                                    
                                    
                                        // Assign new toolbar data back to toolbarData configuration property  
                                        config.toolbarData = toolbarData;
                                        return config;
                                    }
                                    
                                    1 2 Previous Next