delete one or more rows in output table in Shiny - r

I can show the output table in my Shiny app but I also wanted to have a 'delete' button next to each row in the output table so I can delete certain row and do some recalculation
I have the basic template of my Shiny app but need to add the 'delete' buttons next to each row in the output table and I have no idea... Is there a way in Shiny?
Any recommendation welcome and thanks in advance!
library(shiny)
library(data.table)
​
# Define list of products
products <- c("207STX",
"208STX",
"209ABC",
"210ABC")
​
# Create function to process shopping cart and create model input
process_cart <- function(cart_df) {
​
# Do some data processing
df <- copy(cart_df)
# Check if product has SmartStax
df[, STX := grepl("STX", Product)]
​
# Collapse into a single observation
obs_df <- data.table(total_quantity = sum(df$Quantity),
qty_stx = sum(df$Quanity[df$STX]))
​
return(obs_df)
​
}
​
# Run model on observation
predict_discount <- function(obs_df) {
# This is a fake model for demonstration purposes only
discount <- obs_df[, 20 * log(total_quantity) +
1.3 * qty_stx]
discount <- max(discount, 0)
return(discount)
}
​
# Define UI for application
ui <- fluidPage(
​
# Application title
titlePanel("Shopping Cart Example"),
​
# Sidebar
sidebarLayout(
sidebarPanel(
selectInput(inputId = "product_name",
label = "Product Name",
choices = products),
​
numericInput(inputId = "product_quantity",
label = "Quantity",
value = 0,
min = 0),
​
actionButton(inputId = "add_to_cart",
label = "Add to Cart"),
​
actionButton(inputId = "clear_cart",
label = "Clear Cart")
),
​
​
mainPanel(
h2("Shopping Cart"),
tableOutput(outputId = "cart_df"),
h2("Total Discount"),
textOutput(outputId = "discount_amt")
)
)
)
​
​
server <- function(input, output, session) {
​
# Definie initial empty table
cart_df <- data.table()
​
add_to_cart <- observeEvent(input$add_to_cart, {
# Update cart
new_row <- data.frame(Product = input$product_name,
Quantity = input$product_quantity)
new_df <- rbind(cart_df, new_row)
cart_df <<- new_df[, .(Quantity = sum(Quantity)), by = Product]
output$cart_df <- renderTable(cart_df)
​
# Create observation for prediction
obs_df <- process_cart(cart_df)
​
# Run model to predict discount
discount <- predict_discount(obs_df)
output$discount_amt <- renderText(sprintf("$%.2f", discount))
​
# Reset input
updateNumericInput(session, "product_quantity", value = 0)
})
​
clear_cart <- observeEvent(input$clear_cart, {
cart_df <<- data.table()
output$cart_df <- renderTable(cart_df)
})
​
}
​
# Run the application
shinyApp(ui = ui, server = server)

Here is a working demo:
library(shiny)
library(formattable)
library(glue)
initial_table <- cbind(
iris[1:10,],
data.frame(
delete = glue(
"<button rowid='{1:10}'
onclick='Shiny.setInputValue(\"removeRow\",this.getAttribute(\"rowid\"))'>Delete</button>"),
rowid = 1:10
)
)
colnames(initial_table)[ncol(initial_table)-1] <- " "
ui <- fluidPage(
dataTableOutput("deletable")
)
server <- function(input, output, session) {
mytable <- reactiveVal(initial_table)
output$deletable <- renderDataTable(
datatable(
mytable(),
escape = FALSE,
selection = "none",
options = list(
columnDefs = list(list(targets = ncol(initial_table),visible = FALSE))
)
)
)
observeEvent(input$removeRow,{
removeRow <- as.integer(input$removeRow)
tblRowRemoved <- mytable()[-which(mytable()$rowid == removeRow),]
mytable(tblRowRemoved)
})
}
shinyApp(ui, server)

Related

How do we duplicate existed attribute values with different attribute name in shinyapp?

It would be great some one could help on below requirement.
url <- "https://bbolker.github.io/mpha_2019/gapminder_index.csv"
dt <- fread(url)
# UI
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
textInput("newcolumnname", "Custom Attribute Name"),
selectInput("formula", "Enter Custom Formula", choices = unique(names(dt)), multiple = TRUE),
actionButton("addnewcolumn", "Add new column")
),
mainPanel(
DT::DTOutput("data_tbl")
)
)
)
#SERVER
server <- function(input, output, session) {
reactive_dt <- eventReactive(input$addnewcolumn, {
if (input$newcolumnname != "" &&
!is.null(input$newcolumnname) && input$addnewcolumn > 0) {
#newcolval <- dt$input$formula
newcolval <- dt[,input$formula]
newcol <- data.table(newcolval)
names(newcol) <- input$newcolumnname
dt <<- cbind(dt, newcol)
}
dt
})
output$data_tbl <- DT::renderDT({ head(reactive_dt(),5) })
}
#Run the Shiny App to Display Webpage
shinyApp(ui = ui, server = server)
Requirement details:-
would like to concatenate the values of "Category/Provider" attribute values under new column called "Category_provider", unfortunately instead of values it's showing attribute names in UI table. what would be the correction in my code to achieve the requirement.
Try this,
url <- "https://bbolker.github.io/mpha_2019/gapminder_index.csv"
dt <- as.data.frame(fread(url))
# UI
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
textInput("newcolumnname", "Custom Attribute Name"),
selectInput("formula", "Enter Custom Formula", choices = unique(names(dt)), multiple = TRUE),
actionButton("addnewcolumn", "Add new column")
),
mainPanel(
DT::DTOutput("data_tbl")
)
)
)
#SERVER
server <- function(input, output, session) {
reactive_dt <- eventReactive(input$addnewcolumn, {
if (input$newcolumnname != "" &&
!is.null(input$newcolumnname) && input$addnewcolumn > 0) {
newcol <- apply(dt[,input$formula] , 1, function(x) paste(x, collapse = "_"))
cn <-colnames(dt)
dt <<- data.frame(dt, newcol)
colnames(dt) <- c(cn,input$newcolumnname)
}
dt
})
output$data_tbl <- DT::renderDT({ head(reactive_dt(),5) })
}
#Run the Shiny App to Display Webpage
shinyApp(ui = ui, server = server)

How to select last options user selected with shiny checkbox group input control

I have found the solution in the first answer to this question (checkboxGroupInput - set minimum and maximum number of selections - ticks) does not work as expected. The reproducible example is as follows:
rm(list = ls())
library(shiny)
my_min <- 1
my_max <- 3
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
checkboxGroupInput("SelecetedVars", "MyList:",paste0("a",1:5), selected = "a1")
),
mainPanel(textOutput("Selected"))
)
)
server <- function(input, output,session) {
output$Selected <- renderText({
paste(input$SelecetedVars,collapse=",")
})
observe({
if(length(input$SelecetedVars) > my_max)
{
updateCheckboxGroupInput(session, "SelecetedVars", selected= tail(input$SelecetedVars,my_max))
}
if(length(input$SelecetedVars) < my_min)
{
updateCheckboxGroupInput(session, "SelecetedVars", selected= "a1")
}
})
}
shinyApp(ui = ui, server = server)
When selecting checkboxes as you go down the list new selections are added to the tail of the input$SelectedVars vector and thus the tail(input$SelecetedVars,my_max) returns the last three vars the user selected. However as you go back up the list the vars are added to the head of the input$SelectedVars vector so tail(input$SelecetedVars,my_max) continues to return the vars already selected.
My current patch to this is to add a note on my app that says only three vars can be selected at a time. However this relies on the user to understand they have to un-check variables themselves. So for the sake of simplicity I am wondering if there is a way to have the most recent selected var to be appended to the tail of the vector so you can always display the last vars the user selected.
EDIT 2020/12/17: Including new reprex to illustrate infinite cycling produced from #Ben's 2020/12/16 edit. I removed the min vars as well as this wont be used in my case.*
library(shiny)
library(shinyjs)
library(tsibble)
library(tsibbledata)
library(tidyr)
library(plotly)
df <- aus_production # demo data from tsibbledata package
my_max <- 2
vars_list <- c("Beer", "Tobacco", "Bricks", "Cement", "Electricity", "Gas")
ui <- fluidPage(
useShinyjs(),
sidebarLayout(
sidebarPanel(
checkboxGroupInput("SelectedVars", "MyList:",vars_list, selected = "Beer")
),
mainPanel(
plotlyOutput("plot1", height = "40vh"),
textOutput("Selected"))
)
)
server <- function(input, output,session) {
last_checked <- reactiveVal("Business")
output$Selected <- renderText({
paste(input$SelectedVars,collapse=",")
})
observeEvent(input$SelectedVars, {
shinyjs::disable("SelectedVars")
s <- input$SelectedVars
isolate({
if(length(s) > my_max)
{
removed <- last_checked()[1]
} else {
removed <- c(setdiff(last_checked(), s))
}
Sys.sleep(.5)
complete <- c(last_checked(), c(setdiff(s, last_checked())))
last_checked(complete[!complete %in% removed])
updateCheckboxGroupInput(session, "SelectedVars", selected = last_checked())
shinyjs::enable("SelectedVars")
})
}, ignoreInit = TRUE, ignoreNULL = FALSE)
output$plot1 <- renderPlotly({
req(input$SelectedVars)
vars <- input$SelectedVars
df_plot <- df %>%
select(Quarter:Tobacco)
if(length(input$SelectedVars) == 2){
plot_ly(data = df_plot,
type = "scatter",
mode ="lines"
) %>%
add_trace(x = ~Quarter,
y = ~df_plot[[2]]) %>%
add_trace(x = ~Quarter,
y = ~df_plot[[3]])
} else {
plot_ly(df_plot) %>%
add_lines(x = ~Quarter,
y = ~df_plot[[2]])
}
})
}
shinyApp(ui = ui, server = server)

How to update numericInput default value based on DT row selected

I'd like to update the numericInput value based on the different row users select from a DT table.
Below is my simple example. So, if users select the 1st row, the value should be 50. If users select the 2nd row, the value should be 100. Is there a way to do it without using the 'refresh' button?
library(shiny)
library(DT)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
numericInput("price",
"Average price:",
min = 0,
max = 50,
value = 0),
actionButton('btn', "Refresh")
),
mainPanel(
DT::dataTableOutput('out.tbl')
)
)
)
server <- function(input, output, session) {
price_selected <- reactive({
if (input$out.tbl_rows_selected == 1) {
price = 50
} else {
price = 100
}
})
observeEvent (input$btn, {
shiny::updateNumericInput(session, "price", value = price_selected())
})
output$out.tbl <- renderDataTable({
Level1 <- c("Branded", "Non-branded")
Level2 <- c("A", "B")
df <- data.frame(Level1, Level2)
})
}
shinyApp(ui = ui, server = server)
Solution using your reactive variable
price_selected <- reactive({
if (isTRUE(input$out.tbl_rows_selected == 1)) {
price = 50
} else {
price = 100
}
})
shiny::observe({
shiny::updateNumericInput(session, "price", value = price_selected())
})
Note that you can observe input$out.tbl_rows_selected directly (you don't need the reactive variable)

Shiny R: Modifying the variable class

I am trying to create a shiny-app that load data-set, present the variable list and their classes and allow the user to modify the class of a selected variable. All the functions in the following code are working except to the last function in the server- observeEvent which not working when trying to modify the variable class. Any suggestions?
Thank you in advance,
Rami
`
rm(list = ls())
library(shiny)
library(shinydashboard)
library(DT)
ui <- dashboardPage(
dashboardHeader(title = "Shiny Example"),
#--------------------------------------------------------------------
dashboardSidebar(
sidebarMenu(
menuItem("Data", tabName = "data", icon = icon("th"))
)
),
#--------------------------------------------------------------------
dashboardBody(
#--------------------------------------------------------------------
tabItem(tabName = "data",
fluidPage(
fluidRow(
box(
selectInput('dataset', 'Select Dataset', list(GermanCredit = "GermanCredit",
cars = "cars",
iris = "iris")),
title = "Datasets",width = 4, status = "primary",
checkboxInput("select_all", "Select All Variable", value = TRUE),
conditionalPanel(condition = "input.select_all == false",
uiOutput("show.var"))
),
box(
title = "Variable Summary", width = 4, status = "primary",
DT::dataTableOutput('summary.data')
),
box(
title = "Modify the Variable Class", width = 4, status = "primary",
radioButtons("choose_class", label = "Modify the Variable Class",
choices = list(Numeric = "numeric", Factor = "factor",
Character = "character"),
selected = "numeric"),
actionButton("var_modify", "Modify")
)
)
)
)
)
)
#--------------------------------------------------------------------
# Server Function
#--------------------------------------------------------------------
server <- function(input, output,session) {
#--------------------------------------------------------------------
# loading the data
get.df <- reactive({
if(input$dataset == "GermanCredit"){
data("GermanCredit")
GermanCredit
}else if(input$dataset == "cars"){
data(cars)
cars
}else if(input$dataset == "iris"){
data("iris")
iris
}
})
# Getting the list of variable from the loaded dataset
var_list <- reactive(names(get.df()))
# Choosing the variable - checkbox option
output$show.var <- renderUI({
checkboxGroupInput('show_var', 'Select Variables', var_list(), selected = var_list())
})
# Setting the data frame based on the variable selction
df <- reactive({
if(input$select_all){
df <- get.df()
} else if(!input$select_all){
df <- get.df()[, input$show_var, drop = FALSE]
}
return(df)
})
# create list of variables
col.name <- reactive({
d <- data.frame(names(df()), sapply(df(),class))
names(d) <- c("Name", "Class")
return(d)
})
# render the variable list into table
output$summary.data <- DT::renderDataTable(col.name(), server = FALSE, rownames = FALSE,
selection = list(selected = 1, mode = 'single'),
options = list(lengthMenu = c(5, 10, 15, 20), pageLength = 20, dom = 'p'))
# storing the selected variable from the variables list table
table.sel <- reactive({
df()[,which(colnames(df()) == col.name()[input$summary.data_rows_selected,1])]
})
# Trying to modify the variable class
observeEvent(input$var_modify,{
modify.row <- which(colnames(df()) == col.name()[input$summary.data_rows_selected,1])
if( input$choose_class == "numeric"){
df()[, modify.row] <- as.numeric(df()[, modify.row])
} else if( input$choose_class == "factor"){
df()[, modify.row] <- as.factor(df()[, modify.row])
} else if( input$choose_class == "character"){
df()[, modify.row] <- as.character(df()[, modify.row])
}
})
}
shinyApp(ui = ui, server = server)
`
I would use reactiveValues() instead.
library(shiny)
# Define UI for application that draws a histogram
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("classType", "Class Type:", c("as.numeric", "as.character"))
),
mainPanel(
textOutput("class")
)
)
))
server <- shinyServer(function(input, output) {
global <- reactiveValues(sample = 1:9)
observe({
global$sample <- get(input$classType)(global$sample)
})
output$class <- renderText({
print(class(global$sample))
})
})
shinyApp(ui = ui, server = server)
In case you are interested:
Concerning your attempt: reactive() is a function and you called the output of the function by df()[, modify.row]. So in your code you try to change the output of the function, but that does not change the output of futures calls of that function.
Maybe it is easier to see in a simplified version:
mean(1:3) <- 1
The code can not change the mean function to output 1 in future. So thats what reactiveValues() help with :). Hope that helps!

Hyperlink from one DataTable to another in Shiny

I have a Shiny app that consists of two pages:
Page 1 displays a DataTable with summary information (ensembles).
Page 2 displays detailed pricing info (items) for a specific ensemble, which is selectable.
When the user clicks on a row on page 1, I want them to be taken to page 2, with the corresponding ensemble selected.
The below code creates the Shiny app and the two pages, but requires the user to switch pages and enter the ensemble number manually.
app.R
library(shiny)
## Create item pricing data
set.seed(1234)
init_items = function() {
item.id=1:1000
ensemble.id=rep(1:100,each=10)
cost=round(runif(1000,10,100), 2)
profit=round(cost*runif(1000,0.01,0.15), 2)
price=cost+profit
data.frame(item.id, ensemble.id, cost, price, profit)
}
items = init_items()
## Create ensemble pricing data
init_ensembles = function(items) {
items %>% group_by(ensemble.id) %>% summarize_each(funs(sum), cost, price, profit)
}
ensembles = init_ensembles(items)
## Attach dependencies
## https://github.com/timelyportfolio/functionplotR/issues/1#issuecomment-224369431
getdeps <- function() {
htmltools::attachDependencies(
htmltools::tagList(),
c(
htmlwidgets:::getDependency("datatables","DT")
)
)
}
# Define UI for application
ui <- shinyUI(
navbarPage("Linked Table Test",
tabPanel("Page 1", uiOutput("page1")),
tabPanel("Page 2", uiOutput("page2"), getdeps())
)
)
# Define server logic
server <- shinyServer(function(input, output, session) {
output$page1 <- renderUI({
inclRmd("./page1.Rmd")
})
output$page2 <- renderUI({
inclRmd("./page2.Rmd")
})
})
# Run the application
shinyApp(ui = ui, server = server)
page1.Rmd
# Ensembles
Click on an ensemble to display detailed pricing information.
```{r}
tags$div(
DT::renderDataTable(ensembles, rownames = FALSE)
)
```
page2.Rmd
# Items
```{r}
inputPanel(
numericInput("ensemble.id", label = "Ensemble ID:", 0, min(ensembles$ensemble.id), max(ensembles$ensemble.id))
)
tags$div(
renderText(paste0("Detailed pricing information for ensemble #",input$ensemble.id,":"))
)
tags$div(
DT::renderDataTable(items %>% filter(ensemble.id==input$ensemble.id) %>% select(-ensemble.id), rownames = FALSE)
)
```
This should give you the tools to do what you want:
library(shiny)
library(DT)
ui <- fluidPage(
tabsetPanel(
tabPanel("One",
DT::dataTableOutput("test1")
),
tabPanel("two",
numericInput("length","Length",0,0,10)
)))
server <- function(input, output, session) {
df <- reactive({
cbind(seq_len(nrow(mtcars)),mtcars)
})
output$test1 <- DT::renderDataTable({
df()
},rownames=FALSE,options=list(dom="t"),
callback=JS(
'table.on("click.dt", "tr", function() {
tabs = $(".tabbable .nav.nav-tabs li a");
var data=table.row(this).data();
document.getElementById("length").value=data[0];
Shiny.onInputChange("length",data[0]);
$(tabs[1]).click();
table.row(this).deselect();})'
))
}
shinyApp(ui = ui, server = server)
When you click a row in the datatable, it switches tabs, and changes the value of the numeric input to the value of the first column in the row you selected.
edit: you will probably have to put your datatables explicitly in the shiny app and not include them from a r markdown script, since I don't believe shiny objects in R Markdown have html Ids in a reliably readable way.
edit: I took your code and got it to work:
library(shiny)
library(dplyr)
## Create item pricing data
set.seed(1234)
init_items = function() {
item.id=1:1000
ensemble.id=rep(1:100,each=10)
cost=round(runif(1000,10,100), 2)
profit=round(cost*runif(1000,0.01,0.15), 2)
price=cost+profit
data.frame(item.id, ensemble.id, cost, price, profit)
}
items = init_items()
## Create ensemble pricing data
init_ensembles = function(items) {
items %>% group_by(ensemble.id) %>% summarize_each(funs(sum), cost, price, profit)
}
ensembles = init_ensembles(items)
## Attach dependencies
## https://github.com/timelyportfolio/functionplotR/issues/1#issuecomment-224369431
getdeps <- function() {
htmltools::attachDependencies(
htmltools::tagList(),
c(
htmlwidgets:::getDependency("datatables","DT")
)
)
}
# Define UI for application
ui <- shinyUI(fluidPage(
tabsetPanel(#id="Linked Table Test",
tabPanel("Page 1", DT::dataTableOutput("page1")),
tabPanel("Page 2", inputPanel(
numericInput("ensemble.id", label = "Ensemble ID:", 0, min(ensembles$ensemble.id), max(ensembles$ensemble.id))
),
textOutput("page2"), DT::dataTableOutput("table2"),getdeps())
)
))
# Define server logic
server <- shinyServer(function(input, output, session) {
output$page1 <- DT::renderDataTable(ensembles, rownames = FALSE,
callback=JS(
'table.on("click.dt", "tr", function() {
tabs = $(".tabbable .nav.nav-tabs li a");
var data=table.row(this).data();
document.getElementById("ensemble.id").value=data[0];
Shiny.onInputChange("ensemble.id",data[0]);
$(tabs[1]).click();
table.row(this).deselect();
})'
))
output$table2 <- DT::renderDataTable(items %>% filter(ensemble.id==input$ensemble.id) %>% select(-ensemble.id), rownames = FALSE)
output$page2 <- renderText({
print(input$ensemble.id)
paste0("Detailed pricing information for ensemble #",input$ensemble.id,":")
})
})
# Run the application
shinyApp(ui = ui, server = server)

Resources