Soph2010
Soph2010

Reputation: 613

How to add a button that removes input rows?

I have some code to program a small shiny app for which I need to add input rows and, if needed, delete them again. I have figured out how to add inputs, however I can't figure out how to delete them.

Here is a MWE of my code. There is no output since I cannot share the excel sheet behind the code from which the output is generated. However, it should suffice for simply helping me find the correct code to remove the added rows with full input (except the first row):

library(shiny)

GeographyList <- c("Africa","Asia Pacific","Europe","Global", "United States","Latin America & Caribbean")
RegionList <- c("Emerging", 
                "Developed")
ClassList <- c("1",
               "2",
               "3")


# Define UI for app that draws a plot ----
ui <- fluidPage(
  fluidRow(
    mainPanel(
      uiOutput("inputwidgets"),

      actionButton("number", 
                   "Add Row"),

      # Input: Click to update the Output
      actionButton("update", "Update View"),

      # Output: Plot ----
      h4("Allocation"),
      plotOutput("distPlot")
      )
    )
  )


# Define server logic required to call the functions required ----
server <- function(input, output, session) {

  # Get new input by clicking Add Row
  observeEvent(input$number, {
    output$inputwidgets = renderUI({
      input_list <- lapply(1:input$number, function(i) {
        # for each dynamically generated input, give a different name
        fluidRow(
          column(2,
                 selectInput(paste0("Geography", i),
                             label = paste0("Geography", i),
                             choices = GeographyList,
                             multiple = FALSE,
                             selected = NA)
          ),
          column(3,
                 selectInput(paste0("Region", i),
                             label = paste0("Region", i),
                             choices = RegionList,
                             multiple = FALSE,
                             selected = NA)
          ),
          column(4,
                 selectInput(paste0("Class", i),
                             label = paste0("Class", i),
                             choices = ClassList,
                             multiple = FALSE,
                             selected = NA)
          ),
          column(3, 

                 # Input: Specify the amount ----
                 numericInput(paste0("amount",i), label="Amount", 0)
          ))
      })
      do.call(tagList, input_list)
    })

  })


  output$distPlot <- renderPlot({
    if (input$update == 0)
      return()

    isolate(input$number)
    isolate(input$amount)
    slices <- c(input$amount1,input$amount2,input$amount3,input$amount4)
    pie(slices)
  })


}

# Create Shiny app ----
shinyApp(ui = ui, server = server)

Appreciate any tips since I am very new to shiny! Thanks in advance.

Upvotes: 0

Views: 41

Answers (1)

Thomas
Thomas

Reputation: 1302

You could build your lapply loop based on a reactiveValue which is triggered by your add button and a delete button:

1. Edit: using sapply in output$distPlot according to lapply rows

2. Edit: using existing input values

library(shiny)

GeographyList <- c("Africa","Asia Pacific","Europe","Global", "United States","Latin America & Caribbean")
RegionList <- c("Emerging",
                "Developed")
ClassList <- c("1",
               "2",
               "3")

# Define UI for app that draws a plot ----
ui <- fluidPage(
    fluidRow(
        mainPanel(
            uiOutput("inputwidgets"),

            actionButton("number",
                         "Add Row"),
            actionButton("delete_number",
                         "Delete Row"),


            # Input: Click to update the Output
            actionButton("update", "Update View"),

            # Output: Plot ----
            h4("Allocation"),
            plotOutput("distPlot")
        )
    )
)


# Define server logic required to call the functions required ----
server <- function(input, output, session) {

    reac <- reactiveValues()

    observeEvent(c(input$number,input$delete_number), {
        # you need to add 1 to not start with 0
        add <- input$number+1
        # restriction for delete_number > number
        delete <- if(input$delete_number > input$number) add else input$delete_number
        calc <- add - delete
        reac$calc <- if(calc > 0) 1:calc else 1
    })
    # Get new input by clicking Add Row
    observe({
        req(reac$calc)
        output$inputwidgets = renderUI({
            input_list <- lapply(reac$calc, function(i) {
                Geography <- input[[paste0("Geography",i)]]
                Region <- input[[paste0("Region",i)]]
                Class <- input[[paste0("Class",i)]]
                amount <- input[[paste0("amount",i)]]

                # for each dynamically generated input, give a different name
                fluidRow(
                    column(2,
                           selectInput(paste0("Geography", i),
                                       label = paste0("Geography", i),
                                       choices = GeographyList,
                                       multiple = FALSE,
                                       selected = if(!is.null(Geography)) Geography
                           )
                    ),
                    column(3,
                           selectInput(paste0("Region", i),
                                       label = paste0("Region", i),
                                       choices = RegionList,
                                       multiple = FALSE,
                                       selected = if(!is.null(Region)) Region
                           )
                    ),
                    column(4,
                           selectInput(paste0("Class", i),
                                       label = paste0("Class", i),
                                       choices = ClassList,
                                       multiple = FALSE,
                                       selected = if(!is.null(Class)) Class
                           )
                    ),
                    column(3,

                           # Input: Specify the amount ----
                           numericInput(
                               paste0("amount",i),
                               label="Amount",
                               value = if(!is.null(amount)) amount else 0
                           )
                    ))
            })
            do.call(tagList, input_list)
        })

    })


    output$distPlot <- renderPlot({
        req(reac$calc, input$update)

        slices <- sapply(reac$calc, function(i) {
            c(input[[paste0("amount",i)]])
        })

        pie(slices)
    })

}

# Create Shiny app ----
shinyApp(ui = ui, server = server)

Upvotes: 2

Related Questions