如何在R Shiny和shinyjqui中动态创建和保存用户定义的组合?

ukqbszuj  于 2023-10-13  发布在  其他
关注(0)|答案(2)|浏览(98)

这是我的拖放应用程序:

library(shiny)
library(shinyjqui)
library(shinyjs)
library(dplyr)

###### part 1 ------------------------------------------------------------------
#creating the list of items
df <- structure(list(AG = c("A",  "B", "C", "D")),
                row.names = c(NA,-4L), class = "data.frame")

# cells of table
connections1 <- paste0("droppable_cell", ifelse(1:2 == 1, "", 1:2), "_1")
connections2 <- paste0("droppable_cell", ifelse(1:2 == 1, "", 1:2), "_2")

connections <- c(connections1, connections2)

# Define a named list for vec_suggestion1 
vec_suggestion1 <- list(  
  droppable_cell_1 =   c("A", "B", "A", "B"),
  droppable_cell_2 =  c("A", "B", "B", "A")
)

# Create the data frame
my_df <- data.frame(connections = connections,
  stringsAsFactors = FALSE
)

my_df$vec_suggestion1 <- vec_suggestion1[my_df$connections]

###### part 2 ------------------------------------------------------------------

myComplexTableUI <-   div(id = "capture", class = "table-container",
                          div(class = "grid-table",
                              id = "montag",
                              div(class = "grid-row",
                                  div(class = "grid-cell grid-cell-text", "Montag"),
                                  lapply(1:2, function(i) {
                                    div(id = paste0("droppable_cell_", i), class = "grid-cell droppable-cell", "")
                                  })
                              )
                          )
                        
                        )

###### part 3 ------------------------------------------------------------------
# my js

jsCode <- "
$(function() {
    function createSortable(day) {
        $('[id^=droppable_cell' + day + '_]').sortable({
            connectWith: '#A, #B, [id^=droppable_cell' + day + '_]',
            drop: function(event, ui) {
                $(this).append(ui.draggable);
            }
        });
    }

    createSortable('1'); // For day1
    createSortable('2'); // For day2

  $('[id^=droppable_cell]').on('sortupdate', function(e, ui) {
        var cellId = $(this).attr('id');
        var item = ui.item.text();
        Shiny.setInputValue('dropEvent', {cell: cellId, item: item}, {priority: 'event'});
    });
});

shinyjs.pageCol = function(params) {
    $('[id^=droppable_cell]').sortable({
        connectWith: '#A, #B, [id^=droppable_cell_1], [id^=droppable_cell_2]',
        drop: function(event, ui) {
            $(this).append(ui.draggable);
        }
    });

    var dataArray = Object.values(params[0]);
    dataArray = dataArray[0].map((col, i) => dataArray.map(row => row[i]));

    console.log('dataArray: ', dataArray);

    var cacheA = $('#A').html();
    var cacheB = $('#B').html();

    var cacheGridCells1 = $('[id^=droppable_cell_1]').html();

shinyjs.setSuggestion = function (idxSuggestion) {
  $.each(dataArray, function (index, value) {
    var cellSelector = '#' + dataArray[index][0];
    var classIndex = idxSuggestion === 1 ? 1 : 2;
    
    // Retrieve the items for the current cell from dataArray
    var items = dataArray[index][idxSuggestion];
    if (typeof items === 'string') {
      items = [items]; // Convert to array if there is only one item
    }
    
    // Clear the cell content
    $(cellSelector).html('');
    
    // Append each item to the cell
    $.each(items, function (i, item) {
      if (item === null) {
        return true;
      }
      
      // Determine the style based on the item value
      var itemStyle = '';
      if (item === 'A') {
        itemStyle = 'background-color: #ffcc66;'; // Corresponding to Bootstrap's warning color
      } else if (item === 'B') {
        itemStyle = 'background-color: #5cb85c;'; // Corresponding to Bootstrap's success color
      }
      
      var cellHTML = '<div data-value=\"' + item
                   + '\" class=\"btn btn-default ui-sortable-handle\" style=\"' + itemStyle + ' margin: 1px;\" jqui_sortable_idx=\"letters__' 
                   + (index + 1).toString()
                   + '\">'
                   + item
                   + '</div>';
      
      $(cellSelector).append(cellHTML);
    });
  });
}
    shinyjs.resetDnD = function (params) {
    $('#A').html(cacheA).sortable('refresh');
    $('#B').html(cacheB).sortable('refresh');
    $('[id^=droppable_cell_1]').html(cacheGridCells1).sortable('refresh');
    }
};


      "
ui <- fluidPage(
  
  useShinyjs(),
  extendShinyjs(text = jsCode, functions = c("pageCol", "setSuggestion")),
  
  ###### part 4 ------------------------------------------------------------------
  
  # css table design
  tags$head(
    tags$style(
      HTML("
        .custom-title-panel button {
          margin-left: 10px;
          margin-top: 10px; 
        }
        .grid-table {
          width: 220px;
          border-collapse: collapse;
        }
        .grid-cell {
          width: 100%;
          height: 210px;
          border: 1px solid black;
          background-color: white;
          text-align: left;
          margin: 0;
          padding: 5px;
        }
        .grid-cell-text {
          display: flex;
          align-items: center;
          justify-content: center;
          height: 50px;
          background-color: steelblue;
          color: white;
          font-size: 18px;
        }
        .table-container {
          display: flex;
          position: absolute;
          left: 260px;
          top: 20px;
          margin-top: 0px;
          overflow: hidden;
        }
      ")
    )
  ),
  
  ##################################################################################
  
  
  # btn reset
  tags$script(
    HTML(
      "$(document).ready(function() {
          $('#btn_resetDnD').click(function() {
            $('.droppable-cell').html(''); // Remove content from all elements with the class 'droppable_cell'
          });
        });"
    )
  ),

  
  # my items:      
  tags$div(
    style = "position: relative; height: 50px;", # Setting a height to contain the buttons
    tags$div(style = "position: absolute; top: 30px; left: 20px;",
             orderInput("A", "", items = df$AG[1], as_source = TRUE, connect = connections, width = "100%", item_class = "warning")
    ),
    tags$div(style = "position: absolute; top: 30px; left: 65px;",
             orderInput("B", "", items = df$AG[2], as_source = TRUE, connect = connections, width = "100%", item_class = "success")
    )
  ),
  
  # my table:
  myComplexTableUI,
  
  # my buttons:
  tags$div(style = "position: absolute; top: 500px; left: 260px; display: flex; flex-direction: row;",
           actionButton("btn_suggestion1", "Suggestion1"),
           actionButton("btn_resetDnD", "Reset")
           
  )
  )

server <- function(input, output, session) {
  
  shinyjs::js$pageCol(my_df)
  
  observeEvent(input$btn_suggestion1, {
    shinyjs::disable("btn_suggestion1")
    shinyjs::js$setSuggestion(1)
    shinyjs::enable("btn_suggestion1")
  })
  
}

shinyApp(ui, server)

应用程序基本上是这样做的:

我想动态地创建'vec_addition1'输入,它目前是硬编码的。我希望系统能够识别并保存用户的输入,当他们拖动到'droppable_cell1'。

vec_suggestion1 <- list(  
  droppable_cell_1 =   c("A", "B", "A", "B"),
  droppable_cell_2 =  c("A", "B", "B", "A")
)

我的目标是为用户提供拖放A和B的理想组合的能力。此信息应动态保存到“vec_addition2”。随后,A和B的任何其他组合也应该被保存,但保存到'vec_conversion 3','vec_conversion 4'等。沿着每个新向量的创建,应添加相应的新按钮,例如“btn_allocation2”、“btn_allocation3”等。

gj3fmq9x

gj3fmq9x1#

下面的方法删除了自定义JS,并为每个单元格使用orderInput,这简化了对列表的跟踪。现在我去掉了item_class参数,以保持简单(我们可以用jqui_sortable()沿着处理div列表,以保存样式)。但是,保存自定义组合的过程应该是明确的:

library(shiny)
library(shinyjqui)
library(dplyr)

df <-structure(list(AG = c("A",  "B", "C", "D")), row.names = c(NA, -4L), class = "data.frame")

# cells of table
tableOrderInputIds <- paste0("Montag", "_droppable_cell_", 1:2)

# Define a named list for vec_suggestion1
# should vec_suggestions be global? Shared across shiny sessions?
if (file.exists("vec_suggestions.RData")) {
  load(file = "vec_suggestions.RData")
} else {
  vec_suggestions <- list(
    vec_suggestion1 = list(
      Montag_droppable_cell_1 = c("A", "B", "A", "B"),
      Montag_droppable_cell_2 = c("A", "B", "B", "A")
    ),
    vec_suggestion2 = list(
      Montag_droppable_cell_1 = c("B", "B", "B", "B"),
      Montag_droppable_cell_2 = c("A", "A", "A", "A")
    )
  )
}

###### part 2 ------------------------------------------------------------------

myComplexTableUI <- div(id = "capture",
                        class = "table-container",
                        div(
                          class = "grid-table",
                          id = "montag",
                          div(
                            class = "grid-row",
                            div(class = "grid-cell grid-cell-text", "Montag"),
                            lapply(tableOrderInputIds, function(x) {
                              div(
                                orderInput(
                                  inputId = x,
                                  label = NULL,
                                  items = NULL,
                                  connect = tableOrderInputIds,
                                  width = "100%",
                                  style = "min-height: 200px;"
                                ),
                                class = "grid-cell"
                              )
                            })
                          )
                        ))

ui <- fluidPage(
  # css table design
  tags$head(tags$style(
    HTML(
      "
        .custom-title-panel button {
          margin-left: 10px;
          margin-top: 10px;
        }
        .grid-table {
          width: 220px;
          border-collapse: collapse;
        }
        .grid-cell {
          width: 100%;
          height: 210px;
          border: 1px solid black;
          background-color: white;
          text-align: left;
          margin: 0;
          padding: 5px;
        }
        .grid-cell-text {
          display: flex;
          align-items: center;
          justify-content: center;
          height: 50px;
          background-color: steelblue;
          color: white;
          font-size: 18px;
        }
        .table-container {
          display: flex;
          position: absolute;
          left: 260px;
          top: 20px;
          margin-top: 0px;
          overflow: hidden;
        }
      "
    )
  )),
  # my items:
  tags$div(
    style = "position: relative; height: 50px;",
    # Setting a height to contain the buttons
    tags$div(
      style = "position: absolute; top: 30px; left: 20px;",
      orderInput(
        "A",
        "",
        items = df$AG[1],
        as_source = TRUE,
        connect = tableOrderInputIds,
        width = "100%"
      )
    ),
    tags$div(
      style = "position: absolute; top: 30px; left: 65px;",
      orderInput(
        "B",
        "",
        items = df$AG[2],
        as_source = TRUE,
        connect = tableOrderInputIds,
        width = "100%"
      )
    )
  ),
  # my table:
  myComplexTableUI,
  # my buttons:
  column(
    12,
    selectizeInput(
      "select_suggestion",
      "Select / Add suggestion",
      choices = names(vec_suggestions),
      multiple = FALSE,
      options = list('create' = TRUE,
                     'persist' = FALSE)
    ),
    actionButton("load_suggestion", "Load suggestion"),
    actionButton("btn_resetDnD", "Reset"),
    actionButton("save_suggestion", "Save suggestion"),
    style = "position: absolute; top: 500px; left: 20px;"
  )
)

server <- function(input, output, session) {
  # user_suggestion <- reactiveValues(droppable_cell_1 = NULL, droppable_cell_2 = NULL)
  user_suggestion <- do.call(shiny::reactiveValues, setNames(vector(mode = "list", length = length(tableOrderInputIds)), tableOrderInputIds))
  
  observeEvent(input$load_suggestion, {
    lapply(tableOrderInputIds, function(x) {
      updateOrderInput(session, inputId = x, items = vec_suggestions[[input$select_suggestion]][[x]])
    })
  }, ignoreNULL = FALSE)
  
  observeEvent(input$save_suggestion, {
    # should vec_suggestions be global? Shared across shiny sessions?
    vec_suggestions <<- modifyList(vec_suggestions, setNames(list(reactiveValuesToList(user_suggestion)), input$select_suggestion))
    save(vec_suggestions, file = "vec_suggestions.RData")
    showNotification("Saved suggestions to disk.")
  })
  
  observeEvent(input$btn_resetDnD, {
    lapply(tableOrderInputIds, function(x) {
      updateOrderInput(session, inputId = x, items = list())
    })
  })
  
  observe({
    lapply(tableOrderInputIds, function(x) {
      user_suggestion[[x]] <- input[[x]]
    })
  })
}

shinyApp(ui, server)

hiz5n14c

hiz5n14c2#

在R Shiny中,您可以遵循以下几个步骤来实现这一点
创建一个reactiveValues对象来存储组合向量,然后在用户拖放时进行更新。可以作为空列表初始化。
现在要更新“sortupdate”事件中的reactiveValues对象:在“sortupdate”事件中,检索单元格id和拖动的项,然后使用update函数用新的组合更新values$suggestions对象。现在使用renderUIuiOutput函数添加按钮和相应的操作。在renderUI中,现在使用observeEvent函数定义所有按钮的操作
在事件中,使用renderUI函数根据values$suggestions中特定组合索引的值为droppable_cell1droppable_cell2生成新的项目列表。
希望这对你有帮助!

相关问题