1 2 Previous Next 15 Replies Latest reply on Aug 11, 2018 9:19 PM by Laurent Matheo

    [View Component] Choosing actions in the property pane

    Philipp Claus

      Hey,

       

      I am currently working on a custom view component. I would like to add the possibility to add actions - the way you can do it with action buttons in the property pane.

       

      So far I added the following lines to the design.service.js file:

       

      function getRxInspector() {
      return {
           inputs: {
                rxData: {
                     act: {
                          label: 'Action',
                          type: 'rx-action-list',
                          group: 'actions',
                          index: 4
                           }
      

       

      Also I defined "act" in the config.js file:

       

      {
         name: 'act',
         isConfig: true
      }
      

       

      After deploying, the typical selection view shows up in the property pane of the component. Clicking on it does not have an effect, though.

      Also, this error occurs.

       

      I tried different things to make it work, without success.

       

      Did someone face this problem before or has a clue how to solve it? Laurent Matheo, maybe?

       

      Thanks in advance!


      Phil

        • 1. Re: [View Component] Choosing actions in the property pane
          Laurent Matheo

          Hi Philipp Claus, sorry for the delay, it has been a hectic month

           

          Looking at the code it seems you are trying to mix view component and actions.

          View Components and Actions are two different things, you cannot mix-up a view component with an action code, it is not designed to work this way.

          The same way you have a structure for view component, you have one for action.

           

          I made a couple of examples (source code) if you want to have custom javascript actions and made some videos about it, did you give it a shot?

          Some resources / tutorials for developers (view component, rest api, javascript action etc...)

           

          Source code:

          GitHub - lmame/Sample-Library-Public: Sample Library Public

          Video playlist:

          Innovation Suite - Road to Customization - YouTube

           

          Specific videos on Actions:

          Episode 11 - [Action] Custom Action and notifications - YouTube

          Episode 12 - [Action] - Creating a synchronous JS Action + Custom Java Command, with result - YouTube

          Episode 18 - [Action] - Creating a custom Action from Scratch - YouTube

          • 2. Re: [View Component] Choosing actions in the property pane
            Philipp Claus

            Hey! I do not try to build a custom action. I tried to build in the functionality to add/remove actions to a view component. The 'rx-action-list' type creates the typical selection option, but it does not react to click events, like I already mentioned. I was not able to figure out how this functionality is implemented in the background..

            • 3. Re: [View Component] Choosing actions in the property pane
              Laurent Matheo

              Hummm I see. I don't think we thought about this use case. Technically you "could" use what we call "inspectors" for your objects but the danger of this is that it is not designed to be "public" so even if you manage to pull this off we might change it in the future thinking nobody uses it (apart from us) and break your code.

               

              Another way maybe would be to do "your" implementation and use the javascript rxAction method to actually call an action from javascript code.

              Creating custom actions - Documentation for Innovation Suite - BMC Documentation

               

              I would be curious to know the use case for this, can you share it? Would you use it for example for your own custom buttons / links (or any kind of object) when you click on it? But in your use case, would the action use one of your custom field(s), for example input text or textarea (even if you could actually rather use your view component output variables, it would be better).

              Another solution might be to find a way to trigger a button "click" for example. If you have a button and you want to execute some actions you would have "your" button tied to a "hidden" OOTB button and this button would have the actions.

               

              I'm pinging Robert Curlee, it might be worth creating an idea for this, the challenge is on what object to trigger the actions? If the view component is "huge" (let's say a dashboard with 3 or 4 internal buttons), on which button to "attach" the trigger (?).

              We might have to give a "name" or ID to the action(s) / OOTB button(s) and this reference would be passed to your view component to be triggered actually by your code. So rather than using "rxAction" to call one specific action we would have to create (?) a "rxActions" or something with the reference.

              1 of 1 people found this helpful
              • 4. Re: [View Component] Choosing actions in the property pane
                Philipp Claus

                In my specific case I want to use the functionality for a custom button, like you said. I thought it was nice to give non-developers the opportunity to use the custom view component naturally like they would do with any other component.

                It was easy to integrate the "Hidden" property in the property pane, but this one is way more complex.

                Triggering a hidden button with the click on the custom button could be an option. It is a detour, but a relatively easy one for the user.

                But how to tell the view component to click a button? Btw, also it would be nice to know how to check an entry of a record grid with code

                • 5. Re: [View Component] Choosing actions in the property pane
                  Laurent Matheo

                  Yes to trigger a button I don't think we have something for that yet...

                  I guess technically you could cheat in your view component looking for a specific button by its class or name using Xpath or looking inside the DOM... I did that for a pet project actually but this would be "quite" a workaround.

                   

                  As for accessing the grid properties I wonder if it's not possible, at a point. We are using "ui-grid" angularJs component (http://ui-grid.info/ ).

                  You could try to pass the grid object itself as an input parameter, you would have to define an input parameter in your config.js file as something like:

                                      name: 'gridComponent',
                                      label: 'Select your Grid',
                                      enableExpressionEvaluation: true,
                                      editor: 'rx-expression-field'
                  

                   

                  You should be then able to access some methods, I guess something like:

                  refresh, setFilter, clearSearch, getSelectedRows, getFirstSelectedRow, getSelectedRowCount etc... and especially maybe the getUiGridApi() which should return the ui-grid api object, so I guess there you can do pretty much whatever you want using ui-grid own apis.

                  • 6. Re: [View Component] Choosing actions in the property pane
                    Laurent Matheo

                    Ok I found a way to make it work, I'll refine it a bit and post it here.

                    • 7. Re: [View Component] Choosing actions in the property pane
                      Laurent Matheo

                      Ok so here is a simple example. The goal is to create a view component that allows you to select a grid row.

                      It is very basic, you can select the first or second row.

                      The goal is to show how to get the "gridApi" object from ui-grid so you can use their apis from there. I will update the project I posted on gitHub with the code since you were not the first one to ask for this kind of things, but maybe this week end

                       

                      In the meantime, the View Component expects one input parameter, which is the grid object itself:

                      01.png

                       

                      So here is the input parameter you need to put into your ".config.js" file (the isRequired is up to you, same for name and label):

                                      propertiesByName: [
                                          {
                                              name: 'gridObject',
                                              label: 'Grid Object',
                                              isConfig: true,
                                              isRequired: true,
                                              enableExpressionEvaluation: true,
                                              editor: 'rx-expression-field'
                                          }
                                      ]
                      

                       

                       

                      Then here is how it looks at runtime, you have two divs, click on either one of them to select a row in the grid. I didn't put checks if there were data in the grid or not, that's not the point

                      HTML code for the view component template, clicking on one div calls the scope "selectRow" method:

                          Click me to select the first row in the grid
                          Click me to select the second row in the grid

                       

                      02.png

                       

                      Here is the directive code, at least the interesting part:

                                      link: function ($scope) {
                                          var _config = $scope.rxConfiguration.propertiesByName,
                                              gridApi;
                      
                      
                                          // Used to get the ui-grid core gridApi object
                                          function init() {
                                              if (_.isObject(_config.gridObject) && _.isFunction(_config.gridObject.getUiGridApi) && !gridApi) {
                                                  // getUiGridApiAsync() returns a promise.
                                                  _config.gridObject.getUiGridApiAsync()
                                                      .then(function (uiGridApi) {
                                                          gridApi = uiGridApi;
                                                      });
                                              }
                                          }
                      
                      
                                          // Selecting a specific visible row in the given grid.
                                          // Using ui-grid native apis to select a row in the grid.
                                          // selectRowByVisibleIndex():: http://ui-grid.info/docs/#!/api/ui.grid.selection.api:PublicApi
                                          $scope.selectRow = function (index) {
                                              if (gridApi) {
                                                  gridApi.selection.selectRowByVisibleIndex(index);
                                              }
                                          };
                      
                      
                                          // The grid object is loading async in the view.
                                          $scope.$watch('rxConfiguration.propertiesByName.gridObject', init);
                                      }
                      

                       

                      Some points:

                      • The grid loads async in the view, so if you try to get the object right off the bat calling init() you will have an "undefined". Hence the $watch.
                      • To get the gridApi object, you need to call our method "gridApiAsync" which returns a promise.
                      • Once you have the gridApi object from ui-grid well you can use it however you want here I just force a selection using their methods ".selection.selectRowByVisibleIndex()".

                       

                      Note:

                      This is given "as is". You can do "a lot" of things with ui-grid, for example attaching to events, creating your own rowProcessors etc... so be careful not to step on BMC own things

                      • 9. Re: [View Component] Choosing actions in the property pane
                        Philipp Claus

                        Very nice! Thanks for the effort! Is it also possible to select the row once, as soon as the view is initially loaded?  Instead of the click event? I tried things like

                         

                        $scope.$on('$viewContentLoaded', function() {
                          
                        //call it here

                        });

                         

                        but it didn't work.

                         

                        Thanks in advance

                        • 10. Re: [View Component] Choosing actions in the property pane
                          Laurent Matheo

                          The problem is that the components are loaded asynchronously so even if the main view is loaded your grid might not.

                          I guess you can achieve this doing like I do, a $watch on the grid itself rather than on the view.

                           

                          So I guess in my example it would be in the init() when you get the gridApi object, you can try to select the first row directly there.

                          • 11. Re: [View Component] Choosing actions in the property pane
                            Philipp Claus

                            Hey Laurent,

                             

                            I tried to include the selection into the init() like this:

                             

                             

                            if (_.isObject(_config.gridObject) && _.isFunction(_config.gridObject.getUiGridApi) && !gridApi) {
                            // getUiGridApiAsync() returns a promise.
                                 _config.gridObject.getUiGridApiAsync()
                                 .then(function (uiGridApi) {
                                      gridApi = uiGridApi;
                                      gridApi.selection.selectRowByVisibleIndex(2);
                            });
                              }
                            
                            
                            

                             

                            It did not work, though. Nothing happened

                            Was this what you had in mind or did I misunderstood you?

                             

                            Also I am still trying to find a way to trigger a button or to include a working "Actions" section into the "Properties" pane of a custom view component.

                            • 12. Re: [View Component] Choosing actions in the property pane
                              Laurent Matheo

                              It was something like this indeed. Maybe the data was not yet loaded even if the grid was actually there (?).

                               

                              Maybe you can check this solution, adding an "on.rowsRendered" event on the gridApi BUT I am not sure if we do this so you would override ours (I am at home and don't have the source code opened).

                              The interesting part here is the "gridApi.grid.renderContainers.body.visibleRowCache.length" which I guess tells where there is data. So we could do a $timeout on this I guess as a hacky solution

                              Ability to set selection on initialization · Issue #2038 · angular-ui/ui-grid · GitHub

                               

                              var first = true;
                              gridApi.core.on.rowsRendered($scope, function() {
                                if (gridApi.grid.renderContainers.body.visibleRowCache.length === 0) { return; }
                                selectRows(first)
                                first = false;
                              });

                               

                              javascript - How to get notified when grid changed in AngularJS ui-grid? - Stack Overflow

                              ui-grid 'renderComplete' event after rendering rows in DOM - Stack Overflow

                               

                               

                              As for the buttons, you should create an idea so Robert Curlee would assess if it would be interesting for other customers as well.

                               

                              In the meantime I guess you could traverse the DOM and look for the button (rx-action-button) that has a specific GUID (you can identify the GUID through the JSON in the view designer) and then "click" (generate a click event) on it. As we do not have a "name" for buttons (we do have one for record editor or grids for example) I guess for now you would have to stick with GUIDS...

                              AngularJS How to Trigger a Click |

                              2018-06-11 08_46_09-Window.png

                              • 13. Re: [View Component] Choosing actions in the property pane
                                Philipp Claus

                                Hey Laurent,

                                 

                                the issue with the grid is resolved. Waiting till the data was rendered inside the grid helped

                                 

                                Regarding the button click I followed your suggestion. Unfortunately it did not work completly.

                                This is what I did in the console to trigger a click on the button in my view:

                                 

                                setTimeout(angular.element("rx-action-button[rx-view-component-id='bb7a4f81-e2ea-4a67-ad37-782db2fac7a3'] > button").triggerHandler('click'),0)
                                

                                 

                                This worked fine and so I decided to include it in my function.

                                 

                                   $scope.test = function () {

                                 

                                   var temp;

                                   temp = setTimeout(angular.element("rx-action-button[rx-view-component-id='bb7a4f81-e2ea-4a67-ad37-782db2fac7a3'] > button").triggerHandler('click'),0);

                                  };

                                 

                                Calling this function leads to the following error:

                                 

                                An unexpected script error has occurred. [$rootScope:inprog] http://errors.angularjs.org/1.4.7/$rootScope/inprog?p0=%24apply

                                 

                                Thanks for your help

                                Phil

                                 

                                 

                                • 14. Re: [View Component] Choosing actions in the property pane
                                  Laurent Matheo

                                  Hi Philipp Claus

                                   

                                  I would be interested to know how you solved your grid problem, if you can share it, and if you would allow me to share it in the Sample Library I released with code samples, it might help others.

                                   

                                  As for your button problem, it is actually an angularJs matter.

                                  Clicking on an object is going to collide with the existing digest cycle, hence the error (apply in progress). You can use $timeout to solve this issue. The explanation is given here:

                                  javascript - $apply already in progress when sending a click event to a button? - Stack Overflow

                                   

                                   

                                  Here is an example I made and will be published soon based on your question.

                                  In my case the button Label and guid are parameters of the view component but you get the id, you can just force the $scope.buttonGuid to your guid.

                                  The $scope.clickButton is called by a button in my html but in your case you can call it directly. The code is not complete yet (I want to look by label too) but it should unblock you in the meantime. You have to embed your button search and click by a $timeout.

                                  I am using $document rather than angular.element.

                                   

                                  (function () {
                                      'use strict';
                                  
                                      angular.module('com.example.samplelibrary.view-components.click-button').directive('comExampleSamplelibraryClickButton', function ($document,
                                                                                                                                                                        $timeout,
                                                                                                                                                                        rxNotificationMessage,
                                                                                                                                                                        rxString) {
                                              return {
                                                  restrict: 'E',
                                                  templateUrl: 'scripts/view-components/click-button/com-example-samplelibrary-click-button.directive.html',
                                  
                                                  scope: {
                                                      rxConfiguration: '='
                                                  },
                                  
                                                  link: function ($scope) {
                                                      var _config = $scope.rxConfiguration.propertiesByName;
                                  
                                                      $scope.buttonLabel = _config.buttonLabel;
                                                      $scope.buttonGuid = _config.buttonGuid;
                                  
                                                      $scope.clickButton = function () {
                                                          $timeout(function(){
                                                              var buttonGuid = rxString.format('rx-action-button[rx-view-component-id=\'%s\'] > button', $scope.buttonGuid),
                                                                  button = $document.find(buttonGuid);
                                  
                                                              if (button) {
                                                                  button.click();
                                                              } else {
                                                                  rxNotificationMessage.error('Cannot find button ' + $scope.buttonGuid);
                                                              }
                                                          });
                                                      };
                                                  }
                                              };
                                          });
                                  })();
                                  
                                  1 2 Previous Next