0

Change "+ Create record"

How do I change the text "+ Create record" for a given table on a form to something else, like "+ Add Invoice"?

24 replies

null
    • Fred
    • 1 yr ago
    • Reported - view

    Unless Jacques TUR has figured out a way to hack the UI of the reference table, there is no way to modify the UI like that.

    What you can do is use a View element. These do not have any added function like 'create record' or 'add link'. Then you can create buttons at the bottom of the element.

    Here is a picture of something in my DB. You can see a table and at the bottom are buttons that I created.

      • Kent_Signorini
      • 1 yr ago
      • Reported - view

      Fred So I'm trying to do this and not having much luck with my query.

      For the sake of example, what query (select) would I use to duplicate the Cast subtable using a View on the Movies form in the Collections sample database?

      I get to "select Actor where..." and then nothing I put behind the where is valid.

      • Kent_Signorini
      • 1 yr ago
      • Reported - view

      Fred I wonder if Sean (who helped me with some other CSS magic) would have an idea how to remove the "+ Create record" button with some more clever CSS. In my main database, I'm getting Views to work now, but they're doing weird things (as you know from the other post) and Tables have so much more added functionality build in (like resizable rows, right-click-delete, etc.).

      • Sean
      • 1 yr ago
      • Reported - view

      Kent Signorini I can change it, but I need some help from Jacques TUR to get it to work right. Here is the code...

      dialog("Calendar CSS Modifier", "<script>
          var styleSheet = document.styleSheets[2];
          styleSheet.insertRule('.nav-item.head.FastClickContainer_root[title=""Calendar""]{display:none}');
          var interval = setInterval(() => {
              document.querySelector('.list-button > span:nth-child(2)').textContent = 'Add Invoice';
              var bt = document.querySelector('.nx-alert .nx-button-text');
              if (bt) {
                  bt.click();
                  clearInterval(interval);
              }
          });
      </script>", ["close"])
      

      The line that changes the label is this...

      document.querySelector('.list-button > span:nth-child(2)').textContent = 'Add Invoice';
      

      The only way I can get it to work is to put it inside the setInterval function, but then that causes you to have to click the dialog closed instead of it closing automatically.

      • Ninox developper
      • Jacques_TUR
      • 1 yr ago
      • Reported - view

      Sean Your code seems to execute the line AND close the dialog. Good job and good use of the 👍 selector. I tried it on my database and everything works fine. But only if the code is executed after the form is displayed. For that, the best way is to use a formula with the html (or NativeJS) function.

      Otherwise, if several popups are opened, it only changes the first one, the one behind. If you want to change all the displayed tables, use querySelectAll and change each button.

      #{
         document.querySelectorAll('.editor-4col .list-button .i-plus ~ span').forEach( e => e.textContent = 'Add invoice')
      }#

       I also changed the selector because you can have two buttons and in this case, your selector gets the first button, so you have to return the second. I introduced .i-plus on the selector to find the right button. ~ span select the first span after .i-plus

      And if you want to change only a table from its field name, use this code in a formula placed in the same form as the table.

      var fieldName := "Invoice Items";
      html("
      <script>
      // retrieve all add buttons contained in the tables on all open forms and call the function for each of them
      document.querySelectorAll('.editor-4col .list-button .i-plus ~ span').forEach( e => {
          // Find the table component corresponding to the button
          var component = $(e).closest('.component').data('component');
      
          // Test if the table component has the same field name as the one searched
          if (component && component.field.caption == '" + fieldName + "')
             // If so, update the button title
             e.textContent = 'Add invoice'
      })
      </script>
      ")
      • Sean
      • 1 yr ago
      • Reported - view

      Jacques TUR Thanks for the solution. I should have added some context to the post you replied to. In this thread Kent made a request that I don't believe will work with the html() function unless the Form view is open. I used this solution because it loads at startup and doesn't need the Form to be open. I just added the new line to the code I had already posted. I think it is a better approach for run-once requirements than using the html() function.

      In that context the Ninox dialog does not automatically close in the Mac app.

      I have to click the close button.

       

      I don't know why document.querySelector won't work outside the setInterval() function. I did look around online for some explanation, but didn't have any luck. It works inside of the setInterval() function or if you use a button within the <script> tags. Do you know why it has that requirement?

      • Sean
      • 1 yr ago
      • Reported - view

      Addendum:  If I use Forumbee's copy to clipboard function in this thread the dialog will not close automatically. If I manually highlight the code and copy it the dialog closes automatically when run in Trigger after open as intended.

      • Ninox developper
      • Jacques_TUR
      • 1 yr ago
      • Reported - view

      Sean Indeed, when you use the Copy button, it adds special characters instead of spaces. It is this character placed in the middle of the querySelector search string on line 5 that makes the search fail. If you replace it with a space, the dialog box will automatically close again.

      • Sean
      • 1 yr ago
      • Reported - view

      Jacques TUR Unfortunately, when I add this line...

      document.querySelector('.list-button > span:nth-child(2)').textContent = 'Add Invoice';

      like it is posted above, it still prevents the dialog from closing.

      • Ninox developper
      • Jacques_TUR
      • 1 yr ago
      • Reported - view

      Sean 

      if no form with table is opened, as it is the case when the code is executed in the Trigger after opening, querySelector returns null. Consequently, .textContent faild, returns an exception and the code is stopped. So the button.click() is never run and the dialog does not close.

      The querySelector().textContent must be launched from a form with table. 

      I don't understand why you say it won't work with an html function in a form? In my test application it works.

      • Sean
      • 1 yr ago
      • Reported - view

      Jacques TUR 

      Interestingly, the change is applied and the button is renamed after you close the dialog. I wouldn't want to implement the change using the code in Trigger after open unless the dialog automatically closed, however. As I have been gathering class names I have noticed that there are elements in the DOM tree that have display set to none and they weren't set by me. Meaning, there are some elements that exist in the DOM even if we can't see them on the screen.

      Jacques TUR said:
      I don't understand why you say it won't work with an html function in a form? In my test application it works.

       Are you referring to this?

      Sean said:
      In this thread Kent made a request that I don't believe will work with the html() function unless the Form view is open.

       I acknowledge that I am having some trouble changing my mindset regarding CSS in the html() function. Consider this, the prevailing understanding of what could be done and how it had to be done has been around for over 3 years on the forum. This is the first thread I could find on it. When you use this method, the styling is only applied if the Form with the Formula field is open. Just a little over 2 months ago you posted this alternative and less than 2 months ago I found this alternative - insertRule. I still think running one-time code in Trigger after open is a better way to implement style changes. Aren't Formula fields updated every time a change is made in the Form?

      • Ninox developper
      • Jacques_TUR
      • 1 yr ago
      • Reported - view

      Sean Yes for style changes, it's best to do it in Trigger After Open. But this line of code (document.querySelector('.list-button > span:nth-child(2)').textContent = 'Add Invoice';) doesn't change a style, it changes the attribute of an object in the DOM. If this object doesn't exist yet, it can't change it and this causes an execution error. 

      • Ninox developper
      • Jacques_TUR
      • 1 yr ago
      • Reported - view

      Jacques TUR I think the best way is put dialog box in Tigger After Open to adjust style sheet, and put html formula in form to modify textContant of button of the table.

      • Sean
      • 1 yr ago
      • Reported - view

      Jacques TUR 

      Jacques TUR said:
      If this object doesn't exist yet, it can't change it

       It can and does. I've attached a simple demonstration database.

      Jacques TUR said:
      this causes an execution error

       A good reason not to use it. 😁

      • Ninox developper
      • Jacques_TUR
      • 1 yr ago
      • Reported - view

      Sean Now I understand what is happening (I was able to trace it in the debugger):

      1 - at the first pass in the Interval event, there is an execution error because of line 5 that prevents the dialog box from closing. 

      2 - The user is forced to close the dialog box manually in order to use the application.

      3 - From this moment on, the variable bt (line 6) is always null because the button of the dialog box does not exist anymore. This means that the test on line 7 is always negative and the interval is never stopped.

      4 - the loop continues to run in the background and causes an error at each pass, until a form is displayed with a table.

      5 At this point, line 5 executes correctly and modifies the textContent.

      dialog("Calendar CSS Modifier", "<script>
          var styleSheet = document.styleSheets[2];
          styleSheet.insertRule('.nav-item.head.FastClickContainer_root[title=""Calendar""]{display:none}');
          var interval = setInterval(() => {
              document.querySelector('.list-button > span:nth-child(2)').textContent = 'Add Invoice';
              var bt = document.querySelector('.nx-alert .nx-button-text');
              if (bt) {
                  bt.click();
                  clearInterval(interval);
              }
          });
      </script>", ["close"])
      

      If there is no permanent loop, the textContent is not modified because the table does not exist yet at the time of the execution of the Trigger After Open (as I said above).

      Moreover, each time the form is closed, it is deleted and rebuilt each time it is opened. This means that you have to modify the textContent after each opening of the form.

      if you are still skeptical, I invite you to try this code which closes the dialog box and waits for the form to be opened to initialize the textContent. It works the first time, but as soon as the form is closed, the button gets back its original text (create record).

      In this code there are two Interval functions, one to close the dialog box and the other to modify the textContent.

      dialog("Calendar CSS Modifier", "<script>
          var styleSheet = document.styleSheets[2];
          styleSheet.insertRule('.nav-item.head.FastClickContainer_root[title=""Calendar""]{display:none}');
      
          var intervalClose = setInterval(() => {
              var bt = document.querySelector('.nx-alert .nx-button-text');
              if (bt) {
                  bt.click();
                  clearInterval(intervalClose);
              }      });      var intervalBtTable = setInterval(() => {
              var btTable = document.querySelector('.list-button > span:nth-child(2)');
              if (btTable) {
                  btTable.textContent = 'Add Invoice'
                  clearInterval(intervalBtTable);
              }
          });
      </script>", ["close"])
      • Kent_Signorini
      • 1 yr ago
      • Reported - view

      Jacques TUR 
      Could a similar technique be used to hide the title of a View? 

      I have a situation where my view is named the same as my Head and it looks silly.
       

      • Kent_Signorini
      • 1 yr ago
      • Reported - view

      Fred No, not right for that example.

      • Kent_Signorini
      • 1 yr ago
      • Reported - view

      Fred OK, I got it. There's an Artists table in-between where the connections are actually made.

      This worked and has informed my thinking on how to go about doing this in my own database. Thanks for your help!

      let t := this;
      select Artists where Actor.Movie = t
    • Fred
    • 1 yr ago
    • Reported - view
    Kent Signorini said:
    Fred No, not right for that example.

     Well this is what I get:

    I don't know what you are after then.

      • Kent_Signorini
      • 1 yr ago
      • Reported - view

      Fred Once I did this, I got what you got:

      let t := this;
      select Artists where Actor.Movie = t
    • Sean
    • 1 yr ago
    • Reported - view
    Jacques TUR said:
    if you are still skeptical

    I am not nor was I skeptical. Just reporting what I saw. Have a great day.

      • Ninox developper
      • Jacques_TUR
      • 1 yr ago
      • Reported - view

      Sean And you are right, because in the end the permanent loop solution works well 👍.

      If you let the loop run to update the table button, it works fine. Just put a reasonable human delay between two tests so as not to take all the system time, like 500ms (line 13) and never stop the loop (line 15) :

      dialog("Calendar CSS Modifier", "<script>
          var styleSheet = document.styleSheets[2];
          styleSheet.insertRule('.nav-item.head.FastClickContainer_root[title=""Calendar""]{display:none}');      var intervalClose = setInterval(() => {
              var bt = document.querySelector('.nx-alert .nx-button-text');
              if (bt) {
                  bt.click();
                  clearInterval(intervalClose);
              }      });
          var intervalBtTable = setInterval(() => {
              var btTable = document.querySelector('.list-button > span:nth-child(2)');
              if (btTable) {
                  btTable.textContent = 'Add Invoice'
                  //clearInterval(intervalBtTable);
              }
          }, 500);
      </script>", ["close"])
      • Sean
      • 1 yr ago
      • Reported - view

      Jacques TUR Cool, thank you. Another tool to put in the Ninox coding toolbox. 👍

Content aside

  • 1 yr agoLast active
  • 24Replies
  • 358Views
  • 4 Following