Conditional formatting

Comments

14 comments

  • Avatar
    Karl Makatenas

    If you want the background color to change when Apple changes, then put a JavaScript function in the Advanced tab of Apple and execute On Change.  Otherwise, if you want the background color to change when a user edits Orange, execute the function in the Orange Advanced tab.


    function ChangeColor()
    {
        window.CustomAPNamespace = {};
        
        var options = {fieldId: "Apple"};
        eFormHelper.getFieldValue(
            options,
            function(result)
            {
                window.CustomAPNamespace.apple = result.data;
            }
        );
       
        options = {fieldId: "SubForm/Orange"};
        eFormHelper.getFieldValue(
            options,
            function(result)
            {
                window.CustomAPNamespace.orange = result.data;
            }
        );
       
        var apple = window.CustomAPNamespace.apple;
        var orange = window.CustomAPNamespace.orange;
        orange = orange.split(",");
       
        var fields = $('input[id^="Orange-"]');
        
        for (var i = 0; i < orange.length; i++)
        {
            var field = fields.eq(i)[0];
            if (apple === orange[i])
            {
                field.classList.add("PinkBG");
            }
            else
            {
                field.classList.remove("PinkBG");
            }
        }
    }

     

     

    And throw this in the CSS:

    .PinkBG {background-color: #F8dbdb;}

  • Avatar
    Karl Makatenas

    I haven't tried to see what happens if a user adds a comma to one of those fields, but it would probably break.  If that's a concern, you'll have to implement some changes to avoid the comma problem.

  • Avatar
    Loren Bratzler

    Karl,

    I am attempting to do something similar on an application I am working on.  I have a sub-form that is populated by an auto-lookup at form load.  After it is populated, I want to loop through all the entries in the sub-form and set background colors in a field based on the values loaded in the field.

    Here is a screen-shot of the subform:

     

    If the Status = 'Normal Hours', I want to set the background color to green.
    If the Status = 'Closed', I want to set the background color to red.
    If the Status = 'Open at 10AM', I want to set the background color to yellow.

    I am struggling to understand the JavaScript code that you displayed above in your reply to Phread.  Any help or insight would be appreciated!

  • Avatar
    Karl Makatenas

    The JavaScript has three sections, then there's a touch of CSS at the end.

    On the first line, I define a custom namespace, CustomAPNamespace, and leave it as an empty object which will get filled with data.

    In the first section, I give a helper function the internal path to an existing field.  This is the internal name of the field, or it's the internal name of the subform with a forward slash then the field.  When the helper function grabs data, I put it into the CustomCSNamespace object.

    The second section makes the data easier to deal with by aliasing it.  The important part here is the .split("."); which turns the return string into an array.  If you know how to use the debugger, you can set a breakpoint on this line and check the input and output data.

    The last section loops through all existing fields in the repeating subform that have a certain internal name and applies a CSS class to those elements.

    Here's some untested code that goes along with the CSS below.

    function ChangeColor()
    {
      window.CustomAPNamespace = {};

      var options = {fieldId: "SubForm1/Status"};
      eFormHelper.getFieldValue(
        options,
        function(result)
        {
          window.CustomAPNamespace.status = result.data;
        }
      );

      var status = window.CustomAPNamespace.status;
      status = status.split(",");

      var fields = $('input[id^="Location-"]');

      for (var i = 0; i < status.length; i++)
      {
        var field = fields.eq(i)[0];
        if (status[i] === "Normal Hours")
        {
          field.classList.add("Open");
        }
        else if (status[i] === "Open at 10AM")
        {
          field.classList.add("Delayed");
        }
        else if (status[i] === "Closed")
        {
          field.classList.add("Closed");
        }
        else
        {
          field.classList.add("Unknown");
        }
      }
    }

     

    For the CSS, you can use something like this:

    .Open {background-color: #c0ffc0;}
    .Delayed {background-color: #ffc0c0;}
    .Closed {background-color: #ffffc0;}
    .Unknown {background-color: #c0c0c0;}

     

    So the two main lines to edit are around line 4:
    var options = {fieldId: "Internal Name With Path Here"};
    and about halfway down:
    var fields = $('input[id^="Just The Internal Name with a hyphen-"]');

  • Avatar
    Loren Bratzler

    Karl - Thanks for the reply.  I'm still having some trouble getting this to work.  Here is what I came up with for my JavaScript:

    function colorBackgrounds()
    {
       subformData = {};
       subformData.officeStatus = getFieldNameObjectValue("SubForm1/Status");

       debugger;
       var officeStatus = subformData.officeStatus;
       officeStatus = officeStatus.split(",");

       var fields = $('input[id^="Location-"]');

       for (var i = 0; i < officeStatus.length; i++)
       {
          var field = fields.eq(i)[0];
          if (officeStatus[i] === "Normal Hours")
          {
             field.classList.add("GreenBG");
          }
          if (officeStatus[i] === "Closed")
          {
             field.classList.add("RedBG");
          }
          if (officeStatus[i] === "Open at 10AM")
          {
             field.classList.add("YellowBG");
          }
       }
    }

    Note that I have a common function for getting field values that I use in all my AgilePoint JS files:

    function getFieldNameObjectValue(fieldName)
    {
       var value;
       eFormHelper.getFieldValue({ fieldId: fieldName }, function (result) { value = result.data; });
       return value;
    }

    and I defined CSS entries as follows:

    .RedBG {background-color: #FF4500;}
    .GreenBG {background-color: #00FF00;}
    .YellowBG {background-color: #FFFF00;}

     

    The part that I still don't understand is what is going on with these commands:

    var fields = $('input[id^="Location-"]');
    var field = fields.eq(i)[0];

    In your description above you mentioned populating the first command like this:
    var fields = $('input[id^="Just The Internal Name with a hyphen-"]');

    and in your sample script you used the value "Location-" for the internal name.  Is that correct?  "Location" is the first field in the sub-form.  

    Going through the debugger, I can see where the split command is creating the array of "Status" values:

     

    And this is what I see when the "fields" variable is created

     

    Then when the loop starts, the first "field" variable is populated and I can see classList values:

     

    And as I step through the code, I can see that the IF statement is evaluating the "Normal Hours" entry correctly and adding a value to the classList attribute:

    But after the loop finishes and the function ends, none of the subform entries get a background color.

    Is there anything else that I am missing here?

     

     

  • Avatar
    Karl Makatenas

    Have you added the CSS classes to the CSS tab?

     

    If so, then the only adjustments to make are to ensure that the internal names are correct.  Is the internal name within your subform exactly "Location"?  From your screenshot, it looks like that's the case, so maybe something is overwriting it.  Try looking in the Appearance tab in the Location field and making sure to Clear all the existing background colors.

    The section you referenced is just some magic to pull the raw DOM object out of the jQuery framework so I can use native JS functions on them.  There are definitely jQuery-equivalent functions that exist, but I'm so used to native JS that I tend to gravitate there by default.

    One more thing you can do in the debugger is directly access the DOM node from an object in memory.  In your screenshot that shows all the entries with "input#Location-blah-blah" stuff, you can open up the array item and click on the DOM node, and it will select and highlight the element in the DOM.  From there, you can switch to the "Elements" tab in Chrome or the "Inspector" tab in Firefox to view some of the applied CSS styles.  You may find something there that's interfering with the .GreenBG class.

  • Avatar
    Karl Makatenas

    Also, I find this function intriguing:

    function getFieldNameObjectValue(fieldName)
    {
       var value;
       eFormHelper.getFieldValue({ fieldId: fieldName }, function (result) { value = result.data; });
       return value;
    }

    From all my tests in the past, "value" would not be scoped correctly and I'd get an error or undefined, so I used the global namespace shim to deal with that in my own code, but since then I've found other nuances that made me resort entirely to jQuery:

    AWM.helper.getFieldValue = function(controlId: string)
    {
      try
      {
        var node = document.getElementById(controlId);
        if (node.className.indexOf('richTextClass') > -1 || node.tagName.toLowerCase() === 'textarea')
        {
          var iframe = node.previousSibling;
          return iframe.contentDocument.body.innerHTML;
        }
        else
        {
          return node.value;
        }
      }
      catch(e)
      {
        console.log('Warning: AWM.helper.getFieldValue - Could not locate node with id "' + controlId + '".');
      }
      return null;
    };

  • Avatar
    Loren Bratzler

    I do have my CSS defined:

    And I verified that the background color was "cleared" on the sub-form fields.

    I didn't specify in my first post that I want to color the background of the Status field and not the Location field.  So I guess that means this command should read as:

    var fields = $('input[id^="Status-"]');

    (and "Status" is the correct internal name of the field)

    You mentioned above to "open up the array item and click on the DOM node".  I think is what you were referring to:

    and then over to the right, I do see my .GreenBG entry but it is struck-through:

    But most of this is way over my head so I don't really know what I am looking at  :(

     

    Regarding the common Helper Method I referenced above.  That is something that I got from a Mentoring session with AgilePoint Professional Services back when I was first diving into using JavaScript with AgilePoint forms.  I use it in all my applications and I have never had a problem with it.  It always returns the value from the field I pass to it regardless of whether it is a Text Box, or Number Field, etc.

     

     

     

  • Avatar
    Karl Makatenas

    Well you're doing a great job because you nailed the problem :)

    Since the background-color style has strikethrough, that means another background color is overwriting it somewhere.  In that exact same pane, you can scroll through the potentially long list of styles that are applied and see many other strikethrough sections.  Eventually, you may hit one that has a background-color style that actually is being applied, and if you move your mouse to the left side, you can uncheck it temporarily to see if your style then takes precedence.

    Depending on the CSS weights, you may need to add more selectors to your CSS, but the newbie way of handling it is by throwing "!important" after the style.  Here are some screenshots that may help.

    So try this below and see if it works.  If not, then you'll have to do a little more work figuring out the DOM structure and pinning down how that other style is overriding your own.

  • Avatar
    Loren Bratzler

    I do see the background-color that is overriding mine now and note that it is also tagged as !important:

     

    and even though I flagged mine as !important, it is still being trumped by the other:

    and you are correct that if I un-check the overriding one

    then mine takes effect:

     

     

  • Avatar
    Loren Bratzler

    Karl,

    Digging through some of my other apps, I remembered that I have an application that uses JavaScript to set background colors for a field without using any CSS.  However, these are not fields in a Sub-Form they are just fields on the main eForm.

    Here is a snippet of that code for one of the fields:

    function hilightW9()
    {
       var w9Object = getFieldObject("W9Compliance");
       var w9Style = $(w9Object.data).attr('style');
       var w9Value = getFieldNameObjectValue("W9Compliance");

       if (w9Value == "NO" || w9Value == "NOT FOUND")
       {
          w9Style = w9Style + "background-color: yellow !important;";
          $(w9Object.data).attr('style',w9Style);
       }
       else
       {
          w9Style = w9Style + "background-color: #F4F4F4 !important;";
          $(w9Object.data).attr('style',w9Style);
       }
    }


    /* Common Helper Methods */

    function getFieldObject(fieldName)
    {
       var fieldObj;
       eFormHelper.getField({ fieldId: fieldName }, function (result) { fieldObj = result; });
       return fieldObj;
    }

    function getFieldNameObjectValue(fieldName)
    {
       var value;
       eFormHelper.getFieldValue({ fieldId: fieldName }, function (result) { value = result.data; });
       return value;
    }

     

    First line returns back the field object by calling the getFieldObject Common Helper function.
    Second line gets the style from the object and saves it in a variable.
    Third line then gets the actual data stored in the field.

    The script then checks the value of the data from the field.  If it is equal to "NO" or "NOT FOUND", then "background-color: yellow !important;" is concatenated to the existing style variable and then that style variable is updated back to the object.

    I am wondering if something like this might work for me in this new scenario, but I'm not sure if I can make the Common Helper method return back the specific occurrence of the SubForm object that I need to manipulate.  And even if I can, will the other background-color still override mine.

     

  • Avatar
    Karl Makatenas

    I have a whole bunch of issues with the eFormHelper namespace and targeting array entries in repeatable subforms, and usually I can't get them working.  So I default to writing my own scripts in these scenarios.

    This is a CSS weighting issue that I've had to get around a few times.  Since AgilePoint's default way to handle the CSS you inject is to prepend ".eFormSurface " (including the trailing space) to it, create a style node in the head and dump all style declarations there to avoid the .eFormSurface selector.  So instead of adding styles to the CSS area, put this at the bottom of your colorBackgrounds() function:

    var style = document.createElement("style");
    var styleText = `
    #builderTabs input[readonly].RedBG, #previewForm input[readonly].RedBG {background-color: #ff4500 !important;}
    #builderTabs input[readonly].GreenBG, #previewForm input[readonly].GreenBG {background-color: #00ff00 !important;}
    #builderTabs input[readonly].YellowBG, #previewForm input[readonly].YellowBG {background-color: #ffff00 !important;}
    `;

    var styleTextNode = document.createTextNode(styleText);
    style.appendChild(styleTextNode);
    document.head.appendChild(style);

    Notice the excessive targeting to maximize the weight.  From your screenshot, I think it's just above AgilePoint's selector weight so it should overwrite.

  • Avatar
    Loren Bratzler

    WOO-HOO!!  It's Working!!!

    Only problem I had was when I copied your code above into the AgilePoint JavaScript editor, I kept getting an illegal token error on the line that assigns the long string to the styleText variable.

    It may have been some hidden character coming from the Community post causing it.  I ended up manually re-typing the code and I did a concatenation to build the string:

    I really appreciate the help on this.  Never would have been able to figure this out on my own.

    Of course there's a part of me that wonders why doing something like this is so difficult!

  • Avatar
    Karl Makatenas

    So it appears that AgilePoint's JavaScript interpreter is not up to spec with ES6.  At least you figured out the shim, so good work!

    And I always ask myself that question because I know there's a better way to do nearly everything, but it's impossible to know all the options available.  That's why developers are so eager to help each other out and get help from others.  I'm sure someone will come along in the future and show us how to do this in ten seconds :).

Please sign in to leave a comment.