Drag and Drop functionality on UI

Hello All,

I have requirement to implement the drag and drop functionality for the UI components.

For example: Lets consider we have number of BoxLayout on the same page, in each boxlayout we have few cardlayouts which containing some details. Now I want to drag and drop any cardlayout from one box to another.  

I thought of providing link to move card from one box to other, but I am searching for perfect drag and drop capability.

Is it possible in Appian? 

  Discussion posts and replies are publicly visible

Parents
  • Hi ,

       As of now we dont have such functionality , but with some work around we can achieve this (like providing links on the card layout to move right ,move left).

    Thanks

    Madhu

  • Thanks Madhu,

    I thought of this workaround but it will only move card one step ahead or back, I have requirement to move card directly in mentioned box.

  • One option is to have the card implemented in BOTH places in your Form and use the 'showWhen' attribute (set using a link) to toggle the visibility between both places. It's not very flexible but it would work and be pretty straightforward to implement.

  • May be its bit complicated but we can achieve your requirement ,you can place two links one is to select the place where the card needs to move and another link is to move ...so that we can make sure to move the card layout  to the desired place..!.

    Thanks

    Madhu

  • Hi , I have a requirement where the user would like to reorder a list of dictionaries (or maybe just a list of type text).  Currently I have implemented a grid where the user can click up and down arrows, but they find this tedious as the list can be quite long.  Would the method you mentioned here be usable for a requirement like this?  If so, what would that look like?

  • It needs some more work but something like this:

    a!localVariables(
      /* some dummy test data */
      local!listLength: 50,
      local!list: fn!enumerate(local!listLength) + 1,
      local!myLongList: a!forEach(
        items: local!list,
        expression: {
          item: "This is item " & fv!item & " of " & local!listLength
        }
      ),
      local!datasubset: a!refreshVariable(
        value: fn!todatasubset(
          local!myLongList,
          a!pagingInfo(startIndex: 1, batchSize: - 1)
        ),
        refreshOnVarChange: { local!myLongList }
      ),
      local!selectedRows: {},
      local!selectedData: {},
      local!selectedIndexes: {},
      {
        a!gridField(
          data: local!datasubset.data,
          columns: {
            a!gridColumn(label: "Items", value: fv!row.item),
            a!gridColumn(
              value: a!richTextDisplayField(
                value: a!richTextIcon(
                  icon: if(
                    fn!contains(
                      fn!tointeger(local!selectedRows),
                      fv!identifier
                    ),
                    "check-square-o",
                    "square-o"
                  ),
                  link: if(
                    fn!length(local!selectedRows) = 2,
                    null,
                    a!dynamicLink(
                      value: fv!identifier,
                      saveInto: {
                        a!save(
                          local!selectedRows,
                          fn!union(
                            fn!append(local!selectedRows, save!value),
                            {}
                          )
                        ),
                        a!save(
                          local!selectedIndexes,
                          wherecontains(
                            local!myLongList.item[local!selectedRows],
                            local!myLongList.item
                          )
                        )
                      }
                    )
                  ),
                  linkStyle: "STANDALONE"
                )
              ),
              width: "ICON"
            )
          }
        ),
        a!buttonLayout(
          showWhen: fn!length(local!selectedRows) = 2,
          primaryButtons: {
            a!buttonWidget(
              label: "Swap Rows",
              saveInto: {
                a!save(
                  local!selectedData,
                  a!forEach(
                    items: local!selectedRows,
                    expression: fn!append(
                      local!selectedData,
                      local!datasubset.data[fv!item]
                    )
                  )
                ),
                a!save(
                  local!myLongList,
                  fn!remove(local!myLongList, local!selectedRows)
                ),
                a!save(local!selectedRows, {}),
                a!save(
                  local!myLongList,
                  fn!reduce(
                    fn!insert,
                    local!myLongList,
                    fn!merge(
                      local!selectedData,
                      {
                        local!selectedIndexes[2] - 1,
                        local!selectedIndexes[1]
                      }
                    )
                  )
                ),
                a!save(
                  {
                    local!selectedData,
                    local!selectedIndexes
                  },
                  {}
                )
              }
            )
          },
          secondaryButtons: {
            a!buttonWidget(
              label: "Clear Selection",
              value: {},
              saveInto: local!selectedRows
            )
          }
        )
      }
    )

    This allows you to pick two (and ONLY two) rows, at which point a 'SWAP ROWS' button becomes enabled. When this is clicked the code removes the two rows from the array, and then inserts them again in the reverse order.

    The key is to store the values and their indexes, and then use this information to re-insert them at their new positions in the array.

Reply
  • It needs some more work but something like this:

    a!localVariables(
      /* some dummy test data */
      local!listLength: 50,
      local!list: fn!enumerate(local!listLength) + 1,
      local!myLongList: a!forEach(
        items: local!list,
        expression: {
          item: "This is item " & fv!item & " of " & local!listLength
        }
      ),
      local!datasubset: a!refreshVariable(
        value: fn!todatasubset(
          local!myLongList,
          a!pagingInfo(startIndex: 1, batchSize: - 1)
        ),
        refreshOnVarChange: { local!myLongList }
      ),
      local!selectedRows: {},
      local!selectedData: {},
      local!selectedIndexes: {},
      {
        a!gridField(
          data: local!datasubset.data,
          columns: {
            a!gridColumn(label: "Items", value: fv!row.item),
            a!gridColumn(
              value: a!richTextDisplayField(
                value: a!richTextIcon(
                  icon: if(
                    fn!contains(
                      fn!tointeger(local!selectedRows),
                      fv!identifier
                    ),
                    "check-square-o",
                    "square-o"
                  ),
                  link: if(
                    fn!length(local!selectedRows) = 2,
                    null,
                    a!dynamicLink(
                      value: fv!identifier,
                      saveInto: {
                        a!save(
                          local!selectedRows,
                          fn!union(
                            fn!append(local!selectedRows, save!value),
                            {}
                          )
                        ),
                        a!save(
                          local!selectedIndexes,
                          wherecontains(
                            local!myLongList.item[local!selectedRows],
                            local!myLongList.item
                          )
                        )
                      }
                    )
                  ),
                  linkStyle: "STANDALONE"
                )
              ),
              width: "ICON"
            )
          }
        ),
        a!buttonLayout(
          showWhen: fn!length(local!selectedRows) = 2,
          primaryButtons: {
            a!buttonWidget(
              label: "Swap Rows",
              saveInto: {
                a!save(
                  local!selectedData,
                  a!forEach(
                    items: local!selectedRows,
                    expression: fn!append(
                      local!selectedData,
                      local!datasubset.data[fv!item]
                    )
                  )
                ),
                a!save(
                  local!myLongList,
                  fn!remove(local!myLongList, local!selectedRows)
                ),
                a!save(local!selectedRows, {}),
                a!save(
                  local!myLongList,
                  fn!reduce(
                    fn!insert,
                    local!myLongList,
                    fn!merge(
                      local!selectedData,
                      {
                        local!selectedIndexes[2] - 1,
                        local!selectedIndexes[1]
                      }
                    )
                  )
                ),
                a!save(
                  {
                    local!selectedData,
                    local!selectedIndexes
                  },
                  {}
                )
              }
            )
          },
          secondaryButtons: {
            a!buttonWidget(
              label: "Clear Selection",
              value: {},
              saveInto: local!selectedRows
            )
          }
        )
      }
    )

    This allows you to pick two (and ONLY two) rows, at which point a 'SWAP ROWS' button becomes enabled. When this is clicked the code removes the two rows from the array, and then inserts them again in the reverse order.

    The key is to store the values and their indexes, and then use this information to re-insert them at their new positions in the array.

Children
  • Hi Stewart,
    We have a very similar requirement to the reordering functionality where we would like users to be able to drag and drop elements in the UI. Since your reply is a couple of years old, I was wondering if there been any additional functionality that would could improve your original solution? 

    Thanks

  • 0
    Certified Lead Developer
    in reply to keiths8733

    I guess what you can do it to add navigation arrows adjacent to elements that you would like to reorder and then maintain some kind of dictionary that takes care of the positioning of the elements. 
    Clicking on the arrows will move the element in the respective direction. 

    If the elements can only move up and down, it would be pretty straight forward, else, you'll have to deal with a little more challenges. 

  • I recently implemented a click&click demo. Click one item to pick it, then click somewhere else to drop it.

    a!localVariables(
      local!columns: {
        a!map(
          name: "TO-DO",
          cards: {
            a!map(name: "ONE", icon: "user"),
            a!map(name: "TWO", icon: "helicopter"),
            a!map(name: "THREE", icon: "cog"),
            a!map(name: "FOUR", icon: "stethoscope"),
            a!map(name: "FIVE", icon: "piggy-bank"),
            a!map(name: "SIX", icon: "user-ninja"),
          }
        ),
        a!map(
          name: "DOING",
          cards: {}
        ),
        a!map(
          name: "DONE",
          cards: {}
        ),
      },
      local!pickedCard,
      local!tempCard,
      a!columnsLayout(
        showDividers: true,
        columns: a!forEach(
          items: local!columns,
          expression: a!localVariables(
            local!column: fv!item,
            local!columnIndex: fv!index,
            a!columnLayout(
              contents: {
                a!cardLayout(
                  showBorder: false,
                  contents: {
                    a!cardLayout(
                      marginBelow: "STANDARD",
                      showBorder: false,
                      style: "STANDARD",
                      link: if(
                        a!isNotNullOrEmpty(local!pickedCard),
                        a!dynamicLink(
                          saveInto: {
                            a!save(
                              target: local!columns[local!pickedCard.column].cards,
                              value: remove(
                                local!columns[local!pickedCard.column].cards,
                                local!pickedCard.card
                              )
                            ),
                            a!save(
                              target: local!columns[local!columnIndex].cards,
                              value: insert(
                                local!columns[local!columnIndex].cards,
                                local!tempCard,
                                1
                              )
                            ),
                            a!save(
                              target: {local!pickedCard, local!tempCard},
                              value: null
                            )
                          }
                        ),
                        null
                      ),
                      contents: a!richTextDisplayField(
                        labelPosition: "COLLAPSED",
                        value: a!richTextHeader(
                          text: fv!item.name
                        )
                      )
                    ),
                    a!forEach(
                      items: local!column.cards,
                      expression: a!cardLayout(
                        marginBelow: "STANDARD",
                        shape: "ROUNDED",
                        showBorder: false,
                        showShadow: true,
                        style: if(
                          a!isNotNullOrEmpty(local!pickedCard),
                          if(
                            and(
                              local!pickedCard.column = local!columnIndex,
                              local!pickedCard.card = fv!index
                            ),
                            "ACCENT",
                            "NONE"
                          ),
                          "NONE"
                        ),
                        link: if(
                          a!isNullOrEmpty(local!pickedCard),
                          a!dynamicLink(
                            saveInto: {
                              a!save(
                                target: local!tempCard,
                                value: fv!item
                              ),
                              a!save(
                                target: local!pickedCard,
                                value: a!map(
                                  column: local!columnIndex,
                                  card: fv!index
                                )
                              )
                            }
                          ),
                          if(
                            and(
                              and(
                                local!pickedCard.column = local!columnIndex,
                                local!pickedCard.card = fv!index
                              ),
                            ),
                            a!dynamicLink(value: null, saveInto: local!pickedCard),
                            a!dynamicLink(
                              saveInto: {
                                a!save(
                                  target: local!columns[local!pickedCard.column].cards,
                                  value: remove(
                                    local!columns[local!pickedCard.column].cards,
                                    local!pickedCard.card
                                  )
                                ),
                                a!save(
                                  target: local!columns[local!columnIndex].cards,
                                  value: insert(
                                    local!columns[local!columnIndex].cards,
                                    local!tempCard,
                                    fv!index + 1
                                  )
                                ),
                                a!save(
                                  target: {local!pickedCard, local!tempCard},
                                  value: null
                                )
                              }
                            )
                          )
                        ),
                        contents: a!sideBySideLayout(
                          items: {
                            a!sideBySideItem(
                              width: "MINIMIZE",
                              item: a!richTextDisplayField(
                                labelPosition: "COLLAPSED",
                                value: {
                                  a!richTextIcon(
                                    icon: fv!item.icon,
                                  ),
                                }
                              )
                            ),
                            a!sideBySideItem(
                              item: a!paragraphField(
                                labelPosition: "COLLAPSED",
                                value: fv!item.name,
                                saveInto: fv!item.name
                              )
                            )
                          }
                        )
                      )
                    )
                  }
                )
              }
            )
          )
        )
      )
    )