1
0
-1

I need to have the following behavior on my form.   A value is calculated for a field A based upon the values of other fields (say B, C, and D).   However, I need it to be such that the user can type into the field A to overwrite the calculated value, and have the value stick; that is, not be overwritten by the calculation rule firing again upon a change.  Now, that appears to be impossible by using the calculation rule on the field A, but is it possible by using the calculation field on the last field (D) used for the calculation?   That is, can a value on a field be changed from a rule on another field?   I tried using sfc.setRawValue(A,"value") in the calculation rule of field D, but it doesn't seem to work.   Furthermore, if the user does not change the value in calculated field A, I want the values in B, C, and D to remain, else, if the user manually changes the value in calculated field A, I want the values in B, C, and D to be blanked out.   Is this possible?   Any suggestions on how to get my desired behavior?

    CommentAdd your comment...

    6 answers

    1.  
      1
      0
      -1

      I would argue that no matter which technique you use, a) this is a very complicated solution, and b) it creates a behavior that is difficult for the user to understand and predict, and could confusing at best, and irritating at worse. (Particularly the B/C/D fields blanking out without me asking them to - if I saw that I would think it was a bug.)

      My suggestion is to simplify both the implementation and the user experience. Drop a button on the form, that if clicked, performs the calculation. It's obvious, and easy.

      Or perhaps give them a choice like this:

      (o) Calculate "Field A"
      (  ) Enter "Field A"

      ... and then use some hide-show and editability logic to show the appropriate fields.

      Also note the little nugget from one of the comments below, which is very helpful:

      > Returning undefined from a calculation rule will leave the value unchanged

      My 2c.

      1. Jeffery Mullins

        It is time that I clarified what we are doing with this. What we want to do is to allow a person to enter EITHER a birth date or an age into a form. If they enter a birth date, we want the age calculated. So, in my question, A is the age field, and B, C, and D are the Day, Month, and Year dropdowns using the standard DOB block widget that Avoka supplies. (The age field is right beside the Dob Year dropdown, so they are in proximity for the user to see what happens.) That is fine for current applications. But we also have a need to do historical runs of the software, that is, run it at the age a person was when they first submitted the form, which might have been years ago. So, in that case, we need to override the calculated age with the past age. When that is done, we need to blank out the date of birth so that that is not passed on to downstream systems and the age recalculated to current age. These are my requirements, and the user doing historical runs is someone in our office who is supposed to know what they are doing so that they will not get confused. The button approach is not desirable for the user experience that we want. Does this all make more sense now?

      CommentAdd your comment...
    2.  
      1
      0
      -1

      Glenn, how do I connect the business rule that you gave to the calculation on the fields to get it to fire?

      1. Glenn Crook

        In your business rule script editor, your dependencies are in the bottom right panel, as long as they have the icon in the first column which represents a dynamic dependency, ie when that field changes value this rule will get run, then the rule should get triggered, if they don't then click the Edit Parameter button on the right. The dependencies also need to be of type node to work with the script provided.

      CommentAdd your comment...
    3.  
      1
      0
      -1

      The important Composer behaviours to know for this that all rules get passed an evt object which contains evtSource (source node that triggered the event from), evtName (event name, in calculations this will usually be "change") and evtData (if extra data is passed with the event).

      For your calculation and the specific requirements I would recommend putting this on a "Business Rule [General Purpose]" and using node type dependencies and doing something like:

      var A_value = sfc.getRawValue({A});
      var B_value = sfc.getRawValue({B});
      var C_value = sfc.getRawValue({C});
      var D_value = sfc.getRawValue({D});
      var total = sfc.convertToInteger(B_value)+sfc.convertToInteger(C_value)+sfc.convertToInteger(D_value);
      if (evt.evtSource == {A}) {
          if (A_value != total) {
              sfc.setRawValue({B},"");
              sfc.setRawValue({C},"");
              sfc.setRawValue({D},"");
          }
      }
       else if ((evt.evtSource == {B} && !sfc.isBlank(B_value)) || 
      (evt.evtSource == {C} && !sfc.isBlank(C_value)) || 
      (evt.evtSource == {D} && !sfc.isBlank(D_value))) {
          sfc.setRawValue({A},total);
      }

      This script will run when any of the four fields change their value and will update field A with the total of B, C and D when the value when the source is field B, C or D. When the source of the change event is A which will happen as part of the line "sfc.setRawValue({A},total)" it will look at the value of A and the value of the total and if A has a value that is not the total it will clear the fields. Please test this will all appropriate cases including save and resuming a form as well as on receipt mode. The easy way to test this is to set initial values.

      1. Jeffery Mullins

        I have not gotten this to work. evt.evtSource evaluates to an HTML div statement instead of the field name.

      CommentAdd your comment...
    4.  
      1
      0
      -1

      You just need to keep track of whether the user has changed the field.
      For example if your calculation is A = B+C+D then you can do this as follows


      1) Introduce a new hidden field called 'Changed' say (leave it non-hidden while you debug)


      2) Add the following calculation rule on the 'Changed' field
      if ({Changed}=="true") {
      return "true";
      }
      return {B}+{C}+{D}=={A} ? "false" : "true";


      This will set 'Changed' to "true" if the user has ever changed the field (ie it has a value differing from the calculated value) otherwise it will be "false"


      3) Modify your original calculation on field A as follows
      if ({Changed}=="true") {
      return undefined;
      }
      return {B}+{C}+{D};


      Returning undefined from a calculation rule will leave the value unchanged (so this basically says if the user has changed the field then use that value otherwise calculate it)

      4) To blank out the fields B,C, and D when the user changes field A, add the following calculation rule to fields B,C and D


      return {Changed}=="true" ? "" : undefined;


      This says if A has ever been changed blank me out otherwise leave me unchanged
      You may also want to consider setting an editabilty condition on these fields so the user cant reenter values and get confused
      The editibilty rule would just be return {Changed}!="true";

      1. Jeffery Mullins

        This seems to be a nice elegant solution, but the only problem is that the default value of "false" that I set on the text field Changed is reset to true upon form load! This messes up the algorithm. The same thing happens if I use a checkbox and use boolean true and false instead of Strings "true" and "false" for text fields.

      2. Jeffery Mullins

        The above comment is not right. I had my code checking for a boolean rather than a String (didn't enclose in quotes inadvertently) with the text box. When I fixed that, the default stayed as "false". The checkbox still changed, but I was just checking if ({Changed}) thinking that the value was already a boolean, but it seems like a reassignment was done rather than just a check on the value. Either that, or I inadvertently changed it somewhere in the script.

      CommentAdd your comment...
    5.  
      1
      0
      -1

      Hi Jeff,

       

      You can return the total using a business rule to calculate the sum of your fields and making the total field a static node instead of a dynamic node. that way the rule is not fired each time the value changes. 

      To be able to clear the values on the other fields, if the user changes the total calculated is a little trickier and I would suggest making the calculated total not editable and always make the user enter the values to calculate the total. The use case you bring up could create user error and wrong calculations.

       

      If you continue down this path you will need to make all the nodes static and use a business rule to clear the fields on key out or the change event of the total field. - I don't think that is a good user experience though as even if the user tabs out of the field and does not make a change it would trigger the rule and if the the user changes the field you are still relying on them to calculate the values for the correct total. 

      1. Jeffery Mullins

        The total field has to be dynamic for validation rule firing. Also, making the calculated total not editable goes against the requirement that I am trying to meet.

      CommentAdd your comment...
    6.  
      -1
      -2
      -3

      You could also do it all in JQuery using the change event. You'd have to inspect and get the final ids of your input elements but the example below works. 

      In my form I have: TextFieldA, TextFieldB, TextFieldC (where TextFieldC is the calculated field).

      I had to "inspect" the elements in my form and find the ids of the TextFields. A=acs-542, B=acs-543, C=acs-544 in my case.

      Then add a business rule with the following script (note that you have to also change the attribute data-value):

      $('#acs-542,#acs-543').on('change'
      , function (event) {
      var a = $('#acs-542').val();
      var b = $('#acs-543').val();
      $('#acs-544').val(a + b);
      $('#acs-544').attr('data-value', a + b);
      }
      );

      $('#acs-544').on('change'
      , function (event) {
      $('#acs-542').val('');
      $('#acs-542').attr('data-value', '');
      $('#acs-543').val('');
      $('#acs-543').attr('data-value', '');
      }
      );

      1. Brad White

        Note that I'm just appending the two fields. You'd have to convert to a Number if you wanted to perform some math.

      2. Glenn Crook

        This is not a supported way of doing this as it will not properly update the Composer data structures required when a field is updated. Use sfc.setRawValue(noderef,value) and sfc.getRawValue(noderef) to interact with the field values. noderef is Composer for passing a node object that you get from a node type dependency or a UID string which is a path to the field.

      CommentAdd your comment...