How to update/save variables in interfaces that have no user interaction (no saveinto options)

 I want to update and save a variable in SAIL, but I don't have any user components that have a saveInto in my screen. I only have rich text display elements. 
For example, in the code below, how do I save the even numbers in the second array? 
You can see that I commented a saveInto because there is no saveInto in a rich text component.
a!localVariables(
  local!allNumbers : {1,2,3,4},
  local!evenNumbers,
  {
    /*display only even numbers and update the second array*/
    a!forEach(
      items: local!allNumbers,
      expression: if(
        mod(fv!item,2)=0,
        a!richTextDisplayField(
          value: {a!richTextItem(fv!item)},
          /*saveInto : local!temp,a!save(local!evenNumbers,append(local!evenNumbers,fv!item))*/
        ),{})
    ),
    /* show the second array */
    a!forEach(
      items: local!evenNumbers,
      expression: a!richTextDisplayField(
        value: {a!richTextItem(fv!item)})
    )
  }
)    

  Discussion posts and replies are publicly visible

Parents
  • 0
    Certified Lead Developer

    Without knowing a bit more detail about your use case, all I can say is that you'd need to add some sort of button or link in order to save the variable somewhere else (such as into a rule input to be passed back into a process variable). 

    User interaction is required to execute a saveInto, which in general is why the rich text display field doesn't have it.

  • Thanks Mike.

    I reframed the question as below coding exercise. As you see, the below does not need a user-interaction component. Can we still do this with SAIL?

    Of the below text arrays, display the ones containing the words "rises" and "likes" in one section, and display the rest separately. I guess we can use two different rich text display components for each section.

    Can you please type in a few lines of code to accomplish this? Does this make sense?

    "Today is Tuesday",
    "Sun rises in the East",
    "Charlie likes milk",
    "Amanda lives in New York",
    "Time and Tide waits for none"

  • 0
    Certified Lead Developer
    in reply to Sathish Mark

    You would take care of all such logic in the local variable declarations themselves.  See my example revision to your original code snippet:

    a!localVariables(
      local!allNumbers : {1,2,3,4},
      local!evenNumbers: a!forEach(
        items: local!allNumbers,
        expression: if(
          mod(fv!item, 2) = 0,
          fv!item,
          {}
        )
      ),
      {
        /* show the second array */
        a!forEach(
          items: local!evenNumbers,
          expression: a!richTextDisplayField(
            value: {a!richTextItem(fv!item)})
        )
      }
    )

  • Hi Mike, Thanks for this. It is true when declaring variables such logic can be applied.

    However my original question was to update the variable after the declaration, and on cases where there are no interacting UI components. In my original question, I deliberately kept the declaration of local!evenNumbers as empty, because I want to update it later. So unfortunately it doesn't answer my original question. I hope I'm able to articulate this problem.

  • +1
    Certified Lead Developer
    in reply to Sathish Mark

    My original answer still applies, then - user interaction on a form is required in order to make later modifications such as the ones you're after.  This is the way Appian Expression Language, along with its system of local variables, is designed to work at a core level.

    I realize it's probably confusing for certain people, such as those accusomed to more traditional programming languages, but it would be a good idea to get used to how it works in Appian's case as it will make dealing with more advanced logic easier going forward.

  • 0
    Certified Lead Developer
    in reply to Sathish Mark
    Of the below text arrays, display the ones containing the words "rises" and "likes" in one section, and display the rest separately. I guess we can use two different rich text display components for each section.

    To answer this specific example, since I sorta skipped over it before:

    There are principally 2 standard ways in which this could be implemented easily.  Assuming you start with an array of the text phrases you gave (i'll assume they might be a local variable or passed in as a rule input).

    Option 1: declare 2 local variables manually where each one is a "child list" containing the desired half of the list (one would have the phrases with "rises" or "likes", and the other would use the same logic inverted to get the rest of the lines).  Then in the interface part, display each local variable list in your preferred method.

    Option 2: instead, you could simply do the same logic you would've used to filter the two "child lists", but just put it in the value declaration within your section / display fields.  This would be slightly less code but harder overall to debug if necessary.

    (note that in neither of these solutions would we need, or want, to update the local variable definition(s) within the display elements themselves)

  • 0
    Certified Lead Developer
    in reply to Mike Schmitt

    And, here is some example code for my two listed options above:

    Option 1 (version 1):

    a!localVariables(
      local!phrases: {
        "Today is Tuesday",
        "Sun rises in the East",
        "Charlie likes milk",
        "Amanda lives in New York",
        "Time and Tide waits for none"
      },
      
      local!subset1: a!forEach(
        local!phrases,
        if(
          or(
            find("rises", fv!item) > 0,
            find("likes", fv!item) > 0
          ),
          fv!item,
          {}
        )
      ),
      
      /* now do the exact same logic in reverse */
      local!subset2: a!forEach(
        local!phrases,
        if(
          and(
            find("rises", fv!item) = 0,
            find("likes", fv!item) = 0
          ),
          fv!item,
          {}
        )
      ),
      
      a!formLayout(
        contents: {
          a!sectionLayout(
            label: "First Subset",
            contents: {
              a!richTextDisplayField(
                value: {
                  a!richTextBulletedList(
                    items: {
                      local!subset1
                    }
                  )
                }
              )
            }
          ),
          a!sectionLayout(
            label: "Second Subset",
            contents: {
              a!richTextDisplayField(
                value: {
                  a!richTextBulletedList(
                    items: {
                      local!subset2
                    }
                  )
                }
              )
            }
          )
        }
      )
    )

    Option 1 (version 2):
    This is my preferred method, as it's probably the easiest to maintain.

    a!localVariables(
      local!phrases: {
        "Today is Tuesday",
        "Sun rises in the East",
        "Charlie likes milk",
        "Amanda lives in New York",
        "Time and Tide waits for none"
      },
      
      local!subset1: a!forEach(
        local!phrases,
        if(
          or(
            find("rises", fv!item) > 0,
            find("likes", fv!item) > 0
          ),
          fv!item,
          {}
        )
      ),
      
      /* instead of doing the same logic in reverse this time, we'll use the initial results to filter the original list. */
      /* this way, if the logic ever updates, we only need to update it in one place. */
      local!subset2: a!forEach(
        local!phrases,
        if(
          contains(
            local!subset1,
            fv!item
          ),
          {},
          fv!item
        )
      ),
      
      a!formLayout(
        contents: {
          a!sectionLayout(
            label: "First Subset",
            contents: {
              a!richTextDisplayField(
                value: {
                  a!richTextBulletedList(
                    items: {
                      local!subset1
                    }
                  )
                }
              )
            }
          ),
          a!sectionLayout(
            label: "Second Subset",
            contents: {
              a!richTextDisplayField(
                value: {
                  a!richTextBulletedList(
                    items: {
                      local!subset2
                    }
                  )
                }
              )
            }
          )
        }
      )
    )

    Option 2:

    a!localVariables(
      local!phrases: {
        "Today is Tuesday",
        "Sun rises in the East",
        "Charlie likes milk",
        "Amanda lives in New York",
        "Time and Tide waits for none"
      },
      
      a!formLayout(
        contents: {
          a!sectionLayout(
            label: "First Subset",
            contents: {
              a!richTextDisplayField(
                value: {
                  a!richTextBulletedList(
                    items: {
                      a!forEach(
                        local!phrases,
                        if(
                          or(
                            find("rises", fv!item) > 0,
                            find("likes", fv!item) > 0
                          ),
                          fv!item,
                          {}
                        )
                      )
                    }
                  )
                }
              )
            }
          ),
          a!sectionLayout(
            label: "Second Subset",
            contents: {
              a!richTextDisplayField(
                value: {
                  a!richTextBulletedList(
                    items: {
                      a!forEach(
                        local!phrases,
                        if(
                          and(
                            find("rises", fv!item) = 0,
                            find("likes", fv!item) = 0
                          ),
                          fv!item,
                          {}
                        )
                      )
                    }
                  )
                }
              )
            }
          )
        }
      )
    )

    All of the above options give the following interface output:

Reply
  • 0
    Certified Lead Developer
    in reply to Mike Schmitt

    And, here is some example code for my two listed options above:

    Option 1 (version 1):

    a!localVariables(
      local!phrases: {
        "Today is Tuesday",
        "Sun rises in the East",
        "Charlie likes milk",
        "Amanda lives in New York",
        "Time and Tide waits for none"
      },
      
      local!subset1: a!forEach(
        local!phrases,
        if(
          or(
            find("rises", fv!item) > 0,
            find("likes", fv!item) > 0
          ),
          fv!item,
          {}
        )
      ),
      
      /* now do the exact same logic in reverse */
      local!subset2: a!forEach(
        local!phrases,
        if(
          and(
            find("rises", fv!item) = 0,
            find("likes", fv!item) = 0
          ),
          fv!item,
          {}
        )
      ),
      
      a!formLayout(
        contents: {
          a!sectionLayout(
            label: "First Subset",
            contents: {
              a!richTextDisplayField(
                value: {
                  a!richTextBulletedList(
                    items: {
                      local!subset1
                    }
                  )
                }
              )
            }
          ),
          a!sectionLayout(
            label: "Second Subset",
            contents: {
              a!richTextDisplayField(
                value: {
                  a!richTextBulletedList(
                    items: {
                      local!subset2
                    }
                  )
                }
              )
            }
          )
        }
      )
    )

    Option 1 (version 2):
    This is my preferred method, as it's probably the easiest to maintain.

    a!localVariables(
      local!phrases: {
        "Today is Tuesday",
        "Sun rises in the East",
        "Charlie likes milk",
        "Amanda lives in New York",
        "Time and Tide waits for none"
      },
      
      local!subset1: a!forEach(
        local!phrases,
        if(
          or(
            find("rises", fv!item) > 0,
            find("likes", fv!item) > 0
          ),
          fv!item,
          {}
        )
      ),
      
      /* instead of doing the same logic in reverse this time, we'll use the initial results to filter the original list. */
      /* this way, if the logic ever updates, we only need to update it in one place. */
      local!subset2: a!forEach(
        local!phrases,
        if(
          contains(
            local!subset1,
            fv!item
          ),
          {},
          fv!item
        )
      ),
      
      a!formLayout(
        contents: {
          a!sectionLayout(
            label: "First Subset",
            contents: {
              a!richTextDisplayField(
                value: {
                  a!richTextBulletedList(
                    items: {
                      local!subset1
                    }
                  )
                }
              )
            }
          ),
          a!sectionLayout(
            label: "Second Subset",
            contents: {
              a!richTextDisplayField(
                value: {
                  a!richTextBulletedList(
                    items: {
                      local!subset2
                    }
                  )
                }
              )
            }
          )
        }
      )
    )

    Option 2:

    a!localVariables(
      local!phrases: {
        "Today is Tuesday",
        "Sun rises in the East",
        "Charlie likes milk",
        "Amanda lives in New York",
        "Time and Tide waits for none"
      },
      
      a!formLayout(
        contents: {
          a!sectionLayout(
            label: "First Subset",
            contents: {
              a!richTextDisplayField(
                value: {
                  a!richTextBulletedList(
                    items: {
                      a!forEach(
                        local!phrases,
                        if(
                          or(
                            find("rises", fv!item) > 0,
                            find("likes", fv!item) > 0
                          ),
                          fv!item,
                          {}
                        )
                      )
                    }
                  )
                }
              )
            }
          ),
          a!sectionLayout(
            label: "Second Subset",
            contents: {
              a!richTextDisplayField(
                value: {
                  a!richTextBulletedList(
                    items: {
                      a!forEach(
                        local!phrases,
                        if(
                          and(
                            find("rises", fv!item) = 0,
                            find("likes", fv!item) = 0
                          ),
                          fv!item,
                          {}
                        )
                      )
                    }
                  )
                }
              )
            }
          )
        }
      )
    )

    All of the above options give the following interface output:

Children
No Data