Having multiple saves in braces

Certified Senior Developer

Hello,

Is there any reason that Appian would only execute the first a!save inside a bracket, and not the rest of it? The code goes into the second part of the if statement, and only runs whichever is the first function in the braces. If I switch the order of the 2, it still only runs the first one. 

Attached is my code. Thank you for your help. 

if(
index(
local!existingEvaluationPlanSnapshot,
"evaluation_id_fk",
null
) = local!relevantEvaluation.id_pk,
a!save(
ri!evaluationAction,
local!existingEvaluationPlanSnapshot
),
{
a!save(
ri!evaluationActionToDelete,
local!existingEvaluationPlanSnapshot
),
a!save(
ri!evaluationAction,
'type!{urn:com:appian:types}evaluationActions'(
evaluation_id_fk: local!relevantEvaluation.id_pk,
action_plan_id_fk: ri!actionPlan.id_pk,
created_by: loggedInUser(),
created_on: now()
)
)
}
),

  Discussion posts and replies are publicly visible

  • 0
    Certified Lead Developer

    I've gotten multiple saves in an array to work many times in the past.  Idon't see anything overtly incorrect in the code you've posted, though I feel like there could be a bit more context provided - is this happening in a button click, a field save, etc?  If you could post another code sample with a bit of expanded form context, it might help us spot anything incorrect.  I appreciate that you already troubleshot by switching the ordering, that's informative, though I'm still not sure what the issue could be.

    As an aside, it'll make your code easier to post and read if you use the "Insert --> Insert Code" functionality here to generate a code box that you can then paste your code into; doing it this way generally maintains indentation (assuming you're pasting in code that's indented properly) and also limits how much space the code "wastes" on the post in general (the box it provides is scrollable which is nice).  Just FYI Slight smile

  • 0
    Certified Senior Developer
    in reply to Mike Schmitt

     a!buttonLayout(
          primaryButtons: {
            a!buttonWidget(
              label: "Save" & if(
                ri!actionPlan.status_id_fk = cons!PIX_REF_TYPE_ACTION_PLAN_STATUS_DRAFT,
                " Draft",
                ""
              ),
              confirmHeader: "Save Action Plan?",
              confirmMessage: if(
                ri!actionPlan.status_id_fk = cons!PIX_REF_TYPE_ACTION_PLAN_STATUS_DRAFT,
                " Action plan will be saved as a draft",
                " Changes will be saved"
              ),
              saveInto: {
                /*Saving evaluation action plan*/
                if(
                  local!isSavingEvaluation,
                  if(
                    rule!APN_isBlank(
                      index(
                        local!existingEvaluationPlanSnapshot,
                        "id_pk",
                        null
                      )
                    ),
                    a!save(
                      ri!evaluationAction,
                      'type!{urn:com:appian:types:PIX}PIX_evaluationActions'(
                        evaluation_id_fk: local!relevantEvaluation.id_pk,
                        action_plan_id_fk: ri!actionPlan.id_pk,
                        created_by: loggedInUser(),
                        created_on: now()
                      )
                    ),
                    if(
                      index(
                        local!existingEvaluationPlanSnapshot,
                        "evaluation_id_fk",
                        null
                      ) = local!relevantEvaluation.id_pk,
                      a!save(
                        ri!evaluationAction,
                        local!existingEvaluationPlanSnapshot
                      ),
                      {
                        a!save(
                          ri!evaluationActionToDelete,
                          local!existingEvaluationPlanSnapshot
                        ),
                        a!save(
                          ri!evaluationAction,
                          'type!{urn:com:appian:types}evaluationActions'(
                            evaluation_id_fk: local!relevantEvaluation.id_pk,
                            action_plan_id_fk: ri!actionPlan.id_pk,
                            created_by: loggedInUser(),
                            created_on: now()
                          )
                        )
                      }
                    ),
                  ),
                  {}
                )
                }
                ),
             a!buttonWidget(...)
        }
    )

    Here is more of the code. To give more context, this save function is in the saveInto for the a save button. Please let me know if this amount of context is enough, or if there is anything else that may be helpful. Is there anything that could potentially be the cause of it? 

  • 0
    Certified Lead Developer
    in reply to KC

    Thanks for the extended snippet.  I don't find any obvious errors in it - though I don't know if you need to manually typecast in your a!save statements into the evaluationActions type; if the Rule Input is the defined as the correct CDT already, then typecasting will automatically occur upon save, and you could simply pass a dictionary with the field(s) you want.

    So, I was able to copy this over to a blank interface in one of my sandbox environments and define some of the local variables and rule inputs (making them all one step more generic), and when I try the button press, I can verify that both saves in the final list-of-save are working for me.  So your problem could be something more subtle, I suppose - maybe there's an issue being caused by the type! operators (though if so I haven't seen it before).  Here's the revised code I used, in a blank interface with text rule inputs (in my case) created to handle the 3 rule input variables:

    a!localVariables(
      local!isSavingEvaluation: a!refreshVariable(
        value: true(),
        refreshAlways: true()
      ),
      local!relevantEvaluation: {
        id_pk: 123
      },
      local!existingEvaluationPlanSnapshot: a!refreshVariable(
        value: {
          id_pk: 234,
          evaluation_id_fk: /* comment out only one of the following: */
            123
            /*321*/
        },
        refreshAlways: true()
      ),
      a!buttonLayout(
        primaryButtons: {
          a!buttonWidget(
            label: "Save",
            confirmHeader: "Save Action Plan?",
            confirmMessage: "Changes will be SaveD",
            saveInto: {
              /*Saving evaluation action plan*/
              if(
                local!isSavingEvaluation,
                if(
                  rule!APN_isBlank(
                    property( local!existingEvaluationPlanSnapshot, "id_pk", null )
                  ),
                  a!save(
                    ri!evaluationAction,
                    {
                      name: "new",
                      evaluation_id_fk: local!relevantEvaluation.id_pk,
                      action_plan_id_fk: 345,
                      created_by: loggedInUser(),
                      created_on: now()
                    }
                  ),
                  if(
                    property( local!existingEvaluationPlanSnapshot, "evaluation_id_fk", null ) = local!relevantEvaluation.id_pk,
                    a!save(
                      ri!evaluationAction,
                      local!existingEvaluationPlanSnapshot
                    ),
                    {
                      a!save(
                        ri!evaluationActionToDelete,
                        local!existingEvaluationPlanSnapshot
                      ),
                      a!save(
                        ri!evaluationAction,
                        /*'type!{urn:com:appian:types}evaluationActions'(*/
                        {
                          name: "revised",
                          evaluation_id_fk: local!relevantEvaluation.id_pk,
                          action_plan_id_fk: 456,
                          created_by: loggedInUser(),
                          created_on: now()
                        /*)*/
                        }
                      )
                    }
                  ),
                ),
                {}
              )
            }
          ),
          /*a!buttonWidget(...)*/
        }
      )
    )

    Here is my result when executing the button click:

  • I ran into a similar situation recently where a!save's within an if() would only execute the first a!save, and was just able to replicate after a number of different attempts.  There appears to be a bug where in some situations, only the first a!save will fire within an if().  In my situation the CDT I'm using in the if() is a List of CDT with 1 item, vs a single CDT item.  I can replicate this with a List of Dictionary as well (replication code below). 

    We're on 19.4, and I will submit a bug report today.  This bug was around prior on 19.1 as well, I can confirm as we just completed our 19.4 upgrade (been troubleshooting since prior..). 

    I have found that you can work-around this by either adding a nested index() to ensure 1 item returned, or moving the if() calls into the second parameter of each a!save, so all will always fire but save their current value back (no change) if the should not update. I'm guessing here your local!existingEvaluationPlanSnapshot is actually a List type with 1 item.

    You should have 2 options here, first would be to wrap a second index() call to ensure only one result such as:

    if(
    index(
    index(
    local!existingEvaluationPlanSnapshot,
    "evaluation_id_fk",
    null
    ),
    1,
    null) = local!relevantEvaluation.id_pk,

    Second you can try this for your saveInto setup which executes all saves always but the value parameter depends on whether or not you want to change the variable:

    with(
    local!eval: index(
    local!existingEvaluationPlanSnapshot,
    "evaluation_id_fk",
    null
    ) = local!relevantEvaluation.id_pk,
    {
    a!save(
    ri!evaluationAction,
    if(local!eval,local!existingEvaluationPlanSnapshot,ri!evaluationAction)
    ),
    a!save(
    ri!evaluationActionToDelete,
    if(local!eval,ri!evaluationActionToDelete,local!existingEvaluationPlanSnapshot)
    ),
    a!save(
    ri!evaluationAction,
    if(local!eval,ri!evaluationAction,
    'type!{urn:com:appian:types}evaluationActions'(
    evaluation_id_fk: local!relevantEvaluation.id_pk,
    action_plan_id_fk: ri!actionPlan.id_pk,
    created_by: loggedInUser(),
    created_on: now()
    )
    )
    )
    }
    )

    For replicating this issue, try this code in a new interface.  When you click Submit, Test Field 1 is the only a!save to execute.  I have 2 options in there to swap to for correct functionality:

    a!localVariables(
    local!test1: "Initialized",
    local!test2: "Initialized",
    local!test3: "Initialized",
    /* When we have a List of dictionary with 1 item, the bug presents itself */
    local!data: {{version: 3}},
    /* Work-around #1 below */
    /*local!data: index({{version: 3}},1,{}),*/
    a!formLayout(
    label: "Form",
    contents: {
    a!sectionLayout(
    contents: {
    a!columnsLayout(
    columns: {
    a!columnLayout(
    contents: {
    a!textField(readOnly: true, labelPosition: "ADJACENT", label: "Version", value: local!data.version),
    a!textField(readOnly: true, labelPosition: "ADJACENT", label: "Is Version > 2", value: tointeger(local!data.version)>2)
    }
    ),
    a!columnLayout(
    contents: {
    a!textField(readOnly: true, labelPosition: "ADJACENT", label: "Test Field 1", value: local!test1),
    a!textField(readOnly: true, labelPosition: "ADJACENT", label: "Test Field 2", value: local!test2),
    a!textField(readOnly: true, labelPosition: "ADJACENT", label: "Test Field 3", value: local!test3)
    }
    )
    }
    )
    }
    )
    },
    buttons: a!buttonLayout(
    primaryButtons: {
    a!buttonWidget(
    label: "Submit",
    submit: true,
    style: "PRIMARY",
    saveInto: {

    if(
    tointeger(local!data.version)>2,
    {
    a!save(local!test1,"saved1"),
    a!save(local!test2,"saved1"),
    a!save(local!test3,"saved1")
    },
    {
    a!save(local!test1,"saved2"),
    a!save(local!test2,"saved2"),
    a!save(local!test3,"saved2")
    }
    )

    /* UNCOMMENT BELOW FOR WORK-AROUND #3 */

    /*,*/
    /*with(*/
    /*local!save: tointeger(local!data.version)>2,*/
    /*{*/
    /*a!save(local!test1,if(local!save,"saved3",local!test1)),*/
    /*a!save(local!test2,if(local!save,"saved3",local!test2)),*/
    /*a!save(local!test3,if(local!save,"saved3",local!test3))*/
    /*}*/
    /*)*/
    }
    ),
    a!buttonWidget(
    label: "Reset",
    submit: true,
    style: "SECONDARY",
    saveInto: {
    a!save(local!test1,"Initialized"),
    a!save(local!test2,"Initialized"),
    a!save(local!test3,"Initialized")
    }
    )
    }
    )
    )
    )

  • Our posts crossed Mike :)  I'm assuming if you turn your locals into a List with an extra {}, you can replicate the issue.  Type vs List of Type appears to be the issue, but it does seem buggy since the logic executes within if() but stops after the first a!save.

  • 0
    Certified Lead Developer
    in reply to Chris

    That might be - people need to remember that if() statements can do funny things when the results are lists, since if() itself can handle a list of input parameters of any length (as long as that length is an odd number).

  • 0
    Certified Senior Developer
    in reply to Chris

    I was able to resolve the issue by changing the saveInto field to have the a!save for each of the values come first. Thanks for your help. 

  • Yes, definitely.  To add here from my test code, if we change to:

    local!data: {{version: 3},{version: 3}},

    and the if() expression to:

    tointeger(local!data.version)>{2,2},

    ..Then the first TWO saves occur.  So, for a list type in the if() expression, the amount of a!saves fired from that list will match how many items are in the if() expression list.  List definitely can be fun(ny), but not a "bug" I guess!  Also allows for interesting designs I haven't really used before.

    with(
    local!data: {
    {item: 1},
    {item: 2},
    {item: 3},
    {item: 4},
    {item: 5}
    },

    if(
    tointeger(local!data.item)>makerange(count(local!data.item),3),
    makerange(count(local!data.item),true),
    makerange(count(local!data.item),false)
    )
    )

  • 0
    Certified Lead Developer
    in reply to Chris

    Possibly because the contents of a saveInto: block are promised to execute in order from top to bottom, but the contents of an if() aren't?

  • 0
    A Score Level 1
    in reply to Chris

    Somewhat reviving a zombie thread here but someone has just asked me about this very same problem and sent me this thread, which they found a bit difficult to understand. Chris's answer is correct, but I thought it might be worth adding some specifics as to what is happening, as well as a simpler example and explanation.

    Ultimately, this issue occurs because the first input to fn!if() is an array, normally of one item. Here's an example of the issue, but returning bits of text rather than a!save() functions:

    if(
      {true},
      {
        "first value if true",
        "second value if true"
      },
      {
        "first value if false",
        "second value if false"
      }
    )

    In this example, I've wrapped the boolean condition in curly braces, and so it's a list of boolean with a length of 1. Because I've passed a list of one boolean as the condition, and lists in the value parameters, only the first item of the relevant value parameter will be returned. Similarly, if the condition is set to {false}, we'll only be returned the first item in the second list of items.

    This behaviour is actually documented here, but here's the text for completeness:

    • When a list is passed to the condition parameter, it is treated as a list of conditions and the value parameters will be treated as lists of values to return.
      • This means that when passed an empty list, if() always returns an empty list.
      • When passed a list containing a single boolean and lists in the value parameters, only the first item of the selected list will be returned.

    Hopefully that makes it clear what is happening - it's certainly an easy one to be caught out by!