Error with cascading drop down

I have two level cascading dropdown in an editable grid. In the grid there is a "Add new row" which adds a new row on every click. 

The issue is with the variable (local!items) I am using to generate the choice values of the second drop down (Item drop down). For the first row in the grid, it populates correct, but when user clicks to add second row and then try to select values in first dropdown, then it shows error.

I know the reason why the error as the variable is not updating on every new row addition. I'm unable to figure out the way and have hard time resolving this error.

Attached is the snippet of code I have and screenshot of error.

Any help is highly appreciated.

Thanks!

a!localVariables(

  local!supplyType: rule!OSR_QRY_T_Ref_getSupplyTypeDataByID(
    isActive: true()
  ), /* Gets all items in supply table to be used as Category */

  local!items,
  
  /* Code snippet starts here, these dropdowns are in foreach() of editable grid*/
             /* fv!item is a request row in editable grid */ 
              a!dropdownField(      /* first dropdown */
                label: "category " & fv!index,
                placeholderLabel: "---Please select Category---",
                choiceLabels: local!supplyType.data.SupplyTypeName,
                choiceValues: local!supplyType.data.SupplyTypeID,
                value: index(
                  fv!item,
                  "SupplyTypeID",
                  {}
                ),
                saveInto: {
                  fv!item.SupplyTypeID,
                  a!save(
                    local!items,             /* On every interaction with Category dropdown, this should be updated but it's not updating for items while generating a new row */ 
                    rule!OSR_QRY_T_Ref_getItemDataByID(
                      isActive: true(),
                      supplyTypeID: index(
                        fv!item,
                        "SupplyTypeID",
                        {}
                      )
                    ).data /* Gets Items in a specific supply category */
                  ),
                  a!save(
                    fv!item.ItemID,
                    null
                  )
                }
              ),
              
              a!dropdownField(         /* second dropdown */
                label: "item " & fv!index,
                placeholderLabel: "---Please select Item---",
                choiceLabels: index(
                  local!items,
                  "ItemName",
                  {}
                ),
                choiceValues: index(
                  local!items,
                  "ItemID",
                  {}
                ),
                value: index(
                  fv!item,
                  "ItemID",
                  {}
                ),
                saveInto: fv!item.ItemID
              )
)

  Discussion posts and replies are publicly visible

  • +1
    Certified Lead Developer

    You're not defining local!items correctly.  It looks like local!items is supposed to contain a list of items for the current grid row, but you're defining it in a global context and then updating it from individual rows, without taking usage by other rows into account.

    To fix this you will need to define local!items locally within every grid row (i.e. inside your a!forEach() statement that generates individual rows).

  • 0
    Certified Lead Developer

    This is a really sticky situation.

    So what I think is happening is that you have 1 array of items.  That stores all the choice values for a possible list of items.  What can be contained in that list is determined by what you choose in ANY of the category dropdowns.  You only have one list.  It can be updated by any dropdown.  That means, absolutely all of the category dropdowns have to have exactly the same setting or it won't work.  Because each of the items dropdowns will only be running off the same list.

    you now have: 

    {4, 11, 3}

    What you need is:

    {

    {4, 11, 3},

    {8, 12, 5, 27}, /*whatever the real numbers are*/

    {1, 2, 6, 9},

    {4, 11, 3},

    }

    You're going to be creating a dynamically expanding list of lists. You need to create local!items as an empty list of lists to start. 

    local!items: {}, (You may have to start with {{}} to make sure append function doesn't produce a flat array)

    Now, when you create a row, you have to save a dropdown index and append an empty list to the end of local!items.

    Each one of your category dropdowns has to a!save not to local!items, like it does now, but a!save to local!items[dropdownIndex].  When you delete a row, if you allow such a thing, you'll have to remove the whole list, local!items[dropdownIndex], from local!items.

    The new feature of 20.1 to look at your local variables is going to be invaluable in seeing what's going on.

  • 0
    Certified Lead Developer
    in reply to Dave Lewis
    You need to create local!items as an empty list of lists to start.

    Wouldn't it be quite a bit simpler to define a separate local!items variable within every grid row, like I suggested above?  Appian doesn't really handle lists of lists in any sort of easily-usable way, in my experience.

  • 0
    Certified Lead Developer
    in reply to Mike Schmitt

    Poster is already utilizing a forEach.  The fv!index automatically solves the "dropdownIndex" problem.  So yes, using a!forEach to construct it, you need to make sure that there's a different variable, (or different portion of a variable) for each list of dropdown options that can dynamically change.

    Your problem is that you only had one, so each of the dynamic dropdowns all switch when you change any category.

  • 0
    Certified Lead Developer
    in reply to Dave Lewis
    and as far as I am aware would require the same variables being redefined every time the a!forEach is run, which is every time anything gets changed?

    That's not the way a!localVariables() works, luckily.  By default it only refreshes its definition anytime one of its referenced values changes.

    It would need to be implemented with care to avoid causing a performance hit, since essentially we're loading a separate local!items for every member in the grid row, but assuming that the target item list isn't querying hundreds of items from the DB every time or something, I'd expect it should work OK.

  • 0
    Certified Lead Developer
    in reply to Dave Lewis

    oof dat ninja edit Sweat smile

    So yes, using a!forEach to construct it, you need to make sure that there's a different variable

    This kinda misses my point.  Each iteration of the grid row a!forEach statement should have its own localVariables definition just to hold the value of local!items -- there would be a different local!items instantiated for every member of the row.  I probably should have been more explicit about that in my original reply.

  • +1
    Certified Lead Developer
    in reply to Mike Schmitt

    Here's a small and somewhat simplified working example of the way I'd suggest you set this up.  Note that here, instead of querying from a DB table for sub-choices, I do a local if() statement... but the structure of everything should otherwise be the same.

    a!localVariables(
      local!fruit: {
        "apples",
        "oranges"
      },
      
      local!choiceOptions: {
        "fresh",
        "preserved"
      },
      
      a!sectionLayout(
        contents: {
          a!gridLayout(
            headerCells: {
              a!gridLayoutHeaderCell( label: "Fruit" ),
              a!gridLayoutHeaderCell( label: "Choice" ),
              a!gridLayoutHeaderCell( label: "Style" )
            },
            rows: a!forEach(
              items: local!fruit,
              expression: a!localVariables(
                local!choice,
                local!secondaryChoiceOptions: if(
                  local!choice = "fresh",
                  {"whole", "sliced", "smoothied"},
                  local!choice = "preserved",
                  {"canned", "candied", "salsa'd"},
                  {}
                ),
                local!secondaryChoice,
                a!gridRowLayout(
                  contents: {
                    a!textField(
                      value: fv!item,
                      readOnly: true()
                    ),
                    a!dropdownField(
                      choiceValues: local!choiceOptions,
                      choiceLabels: proper(local!choiceOptions),
                      placeholderLabel: "--make choice--",
                      value: local!choice,
                      saveInto: {
                        local!choice,
                        /* below we will blank out the secondary choice when primary choice is changed */
                        a!save(
                          local!secondaryChoice,
                          null()
                        )
                      }
                    ),
                    a!dropdownField(
                      choiceValues: local!secondaryChoiceOptions,
                      choiceLabels: proper(local!secondaryChoiceOptions),
                      placeholderLabel: "--choose style--",
                      value: local!secondaryChoice,
                      saveInto: local!secondaryChoice
                    )
                  }
                )
              )
            )
          )
        }
      )
    )

  • 0
    Certified Lead Developer
    in reply to Mike Schmitt

    No, I understand perfectly.  For us advanced super users it might be manageable, but it might be a little more challenging to follow or uncover what's going on to someone not used to the pattern, especially for other developers maintaining it after.

    I also wonder if it would play havoc with the new 20.1 reporting of what your local variables contain.  If they're nested deep within the a!forEaches, will it even be able to find them?

    If you were trying to make a list of nested lists, you'd be insane.  Not in my Appian!  You make 3 or 4 forEaches nested inside each other with their own variable definitions inside each one of them if you have to and do it right now.  Don't look back.

    But with just a list of lists, especially since it's just a few integers, I can see advantages and disadvantages to both.  Whatever is easier for the developer, or whichever one performs better.

  • 0
    Certified Lead Developer
    in reply to Dave Lewis

    I also wonder if it would play havoc with the new 20.1 reporting of what your local variables contain.  If they're nested deep within the a!forEaches, will it even be able to find them?

    I've tried it.  Good news, there's no havoc created - but bad news, it refuses to show variables defined within looping functions (it knows there are some and puts a special item on the list explaining that it's not showing these... though i wonder if in the future they could make it handle this case).

    Anyway - I sorta agree: cascading dropdowns within a list of items necessary for an editable grid is not going to be a very easy use case no matter what.

  • Thanks  and  for elaborate answers...

    Was off from work, so couldn't reach community in meantime. 

    For the current scenario, we'll not be having much data in items table, so Mike's solution works best and I found easy to implement in this case.

     haven't tried it yet though but would definitely try to refactor the code later as per the approach you suggested.

    Thanks guys...