I am trying to create a modal that diplays text dynamically. Below is a reprex of what I'm thinking. My actual example I am creating a datatable based on reactive user input and interacting with a sql database so creating it in global environment is not desirable. Is there an easy way I can access the dataframe within the randerDataTable? My code below creates an error because table is not a dataframe object. I know renderDataTable does not create a dataframe, rather it creates an html table. Can I still access the data similiarly to a dataframe or parse it into a dataframe?
shinyApp(
ui = basicPage(
dataTableOutput("table")
),
server = function(input, output, session) {
output$table <- renderDataTable({
t <- data.frame(
x = rep(c('dog', 'cat', 'pig'),5),
y = rnorm(15),
z = rnorm(15)
)
DT::datatable(t, rownames=F, selection = 'none', options = list('tipl')) %>%
formatStyle(3, cursor = 'pointer')})
#Display the value of the cell in a modal
observeEvent(input$table_cell_clicked, {
info = input$table_cell_clicked
# do nothing if not clicked yet, or the clicked cell is not in the 1st column
if (is.null(info$value) || info$col != 2) return()
showModal(modalDialog(
title = paste("The animal you selected is:", table$x[info$row]), #creates an error because table is not a df object. Would like to do something like this though.
paste("The value of the cell is:", info$value),
easyClose = TRUE,
footer = NULL
))
})
}
)
You could use a global variable for this :
server = function(input, output, session) {
global_table <- NULL
output$table <- renderDataTable({
global_table <<- data.frame(
x = rep(c('dog', 'cat', 'pig'),5),
y = rnorm(15),
z = rnorm(15)
)
DT::datatable(global_table, rownames=F, selection = 'none', options = list('tipl')) %>%
formatStyle(3, cursor = 'pointer')})
#Display the value of the cell in a modal
observeEvent(input$table_cell_clicked,{
info = input$table_cell_clicked
warning(info$row )
# do nothing if not clicked yet, or the clicked cell is not in the 1st column
if (is.null(info$value) || info$col != 0) return()
showModal(modalDialog(
title = paste("The animal you selected is:", global_table$x[info$row]),
paste("The value of the cell is:", info$value),
easyClose = TRUE,
footer = NULL
))
})
}
But it's more elegant to use a reactive to compute the data
server = function(input, output, session) {
reactive_table <- reactive(
data.frame(
x = rep(c('dog', 'cat', 'pig'),5),
y = rnorm(15),
z = rnorm(15)
))
output$table <- renderDataTable({
DT::datatable(reactive_table(), rownames=F, selection = 'none',
options = list('tipl')) %>%
formatStyle(3, cursor = 'pointer')})
#Display the value of the cell in a modal
observeEvent(input$table_cell_clicked,{
info = input$table_cell_clicked
warning(info$row )
# do nothing if not clicked yet, or the clicked cell is not in the 1st column
if (is.null(info$value) || info$col != 0) return()
showModal(modalDialog(
title = paste("The animal you selected is:", reactive_table()$x[info$row]),
paste("The value of the cell is:", info$value),
easyClose = TRUE,
footer = NULL
))
})
}
Related
I am trying to make a module that accepts a data frame and produces an editable datatable out of it. This worked until I made the module able to accept multiple edits by making the following change:
editTable <- reactive({
datatable(
reactives$input,
#editable = T #PREVIOUS (working fine)
editable = list(target = "all"), #NEW (problem-causing)
rownames = F
)
})
Once the code labelled #NEW is included, clicking labelDo (in this case "Edit") causes the app to crash with this error message:
Warning: Error in split.default: first argument must be a vector
The closest problem I could find to this one is here . This user's problem is the same but mine is not solved (as theirs allegedly is) by putting rownames = FALSE into their datatable() equivalent of the snippet above.
Please go ahead and run the following module and app together and attempt to edit one of the numbers in the table. Click 'edit' and you will get the same result.
Module:
editrUI <- function(id, labelDo, labelUndo) {
ns <- NS(id)
tagList(
dataTableOutput(ns("out")),
actionButton(
inputId = ns("do"),
label = labelDo
),
actionButton(
inputId = ns("undo"),
label = labelUndo
)
)
}
editrServer <- function(id, dataFrame) {
moduleServer(
id,
function(input, output, session){
reactives <- reactiveValues()
reactives$input <- NULL
observe({
reactives$input <- dataFrame
})
editTable <- reactive({
datatable(
reactives$input,
#editable = T #old
editable = list(target = "all"), #new
rownames = F
)
})
output$out <- renderDataTable(
editTable()
)
observeEvent(input$do , {
reactives$input <<- editData(reactives$input, input$out_cell_edit, rownames = F)
})
observeEvent(input$undo , {
reactives$input <- dataFrame
})
return(reactive({reactives$input}))
}
)
}
App:
library(shiny)
source(
#source of module
)
a <- 1:5
df <- tibble(a, a*2)
ui <- fluidPage(
editrUI(id = "id", labelDo = "Edit", labelUndo = "Undo")
)
server <- function(input, output) {
editrServer(id = "id", dataFrame = df)
}
# Run the application
shinyApp(ui = ui, server = server)
It seems this error is caused when input$out_cell_edit is NULL (no cell has been edited).
You can fix it with req(input$out_cell_edit) that will cancel the event in case input$out_cell_edit is NULL.
Also you don't need to use <<- to assign to the reactiveValues.
observeEvent(input$do , {
req(input$out_cell_edit)
reactives$input <- editData(reactives$input, input$out_cell_edit, rownames = F)
})
I am building app where a user can make edits to a datatable and the hit a button to reflect the changes in a non-editable copy of this datatable (in the final project, I will need to have two datasets that need to be matched manually), but for now this small MWE shows the problem I have with making a copy of the reactive table in which changes can be made, without changing the data of the original reactive table. I would like to make this app work, where you click edit a cell in the table dat_joined$data/output$mytable and that those changes do reflect in a new table mydf$data/output$table2. To do mydf$data initially (before any changes are made) needs to be a copy of dat_joined$data This is a follow up on this question and answer: how to make a copy of a reactive value in shiny server function
library(shiny)
library(DT)
library(shinyWidgets)
library(tidyverse)
# create master dataframe
dat_total <- tibble(ID_1 = 1:10, names = letters[1:10],
ID_2 = 11:20, names_2 = LETTERS[c(3:5, 1, 2, 6:8, 10, 9)])
shinyApp(
ui = fluidPage(
title = 'Radio button and a dropdown manue ',
sliderInput("n_rows_table", "Number of rows:",
min = 0, max = 10,
value = 5),
actionBttn(
inputId = "button_1",
label = "Make tables",
size = "sm",
color = "warning"
),
DT::dataTableOutput("mytable"),
actionBttn(
inputId = "button_2",
label = "Process",
size = "sm",
color = "success"),
DT::dataTableOutput("table2")),
server = function(input, output, session) {
# set up reactive values
dat_left <- reactiveValues(data=NULL)
dat_right <- reactiveValues(data=NULL)
dat_joined <- reactiveValues(data=NULL)
# create reactive daraframe
dat <- eventReactive(input$button_1, {
dat_total[1:input$n_rows_table, ] %>%
rowid_to_column()})
# Split the data into a right and a left set
observe({
dat_left$data <- dat() %>%
select(rowid, ID_1, names)
})
observe({
dat_right$data <- dat() %>%
select(rowid, ID_2, names_2,ID_1)
})
# join these again
# This is needed because my actual app will
# be used to manually match 2 datasets
observe({
if (is.null( dat_right$data )) {
NULL
}else{
dat_joined$data <- left_join(dat_left$data,
dat_right$data,
by = "rowid")
}
})
# Print the the datasets
output$mytable <- renderDT({
datatable(dat_joined$data ,
rownames = F,
editable = "cell")
})
# I want to make a copy of the dat_joined$data dataset into dat$mydf
# none of these function as expected
#mydf <- reactiveValues(data=isolate(dat_joined$data))
#mydf <- reactiveValues(data=local(dat_joined$data))
#mydf <- reactiveValues(data=dat_joined$data)
#mydf <- reactiveValues(data=NULL)
# This works, but only saves the cells to w
mydf <- reactiveValues(data=matrix(NA, nrow=10, ncol = 5))
# Ideally the computation only happens when this both an edit is made
# and the button is pressed (now I need to press it between every edit)
# validate_event <- reactive({
# req(input$mytable_cell_edit) & req(input$button_2)
# })
#observeEvent(input$button_2validate_event(), { DOes not work
observeEvent(input$button_2,{
info = input$mytable_cell_edit
str(info)
i = info$row
j = info$col
v = info$value
mydf$data[i, j] <- DT::coerceValue(v, mydf$data[i, j])
})
# print
output[["table2"]] <- renderDT({
datatable(mydf$data)
})
}
)
Any changes you make in the top table is reflected in the bottom table after you press the button "Process". Try this
library(shiny)
library(DT)
library(shinyWidgets)
library(tidyverse)
# create master dataframe
dat_total <- tibble(ID_1 = 1:10, names = letters[1:10],
ID_2 = 11:20, names_2 = LETTERS[c(3:5, 1, 2, 6:8, 10, 9)])
shinyApp(
ui = fluidPage(
title = 'Radio button and a dropdown manue ',
sliderInput("n_rows_table", "Number of rows:",
min = 0, max = 10,
value = 5),
actionBttn(
inputId = "button_1",
label = "Make tables",
size = "sm",
color = "warning"
),
DT::dataTableOutput("mytable"),
actionBttn(
inputId = "button_2",
label = "Process",
size = "sm",
color = "success"),
DT::dataTableOutput("table2")),
server = function(input, output, session) {
# set up reactive values
dat_left <- reactiveValues(data=NULL)
dat_right <- reactiveValues(data=NULL)
dat_joined <- reactiveValues(data=NULL)
dfon <- reactiveValues(top=NULL,
bottom=NULL)
# create reactive daraframe
dat <- eventReactive(input$button_1, {
dat_total[1:input$n_rows_table, ] %>%
rowid_to_column()})
# Split the data into a right and a left set
observe({
req(dat())
dat_left$data <- dat() %>%
dplyr::select(rowid, ID_1, names)
})
observe({
req(dat())
dat_right$data <- dat() %>%
dplyr::select(rowid, ID_2, names_2,ID_1)
})
# join these again
# This is needed because my actual app will
# be used to manually match 2 datasets
observe({
req(dat())
if (!is.null( dat_right$data )) {
dat_joined$data <- left_join(dat_left$data,
dat_right$data,
by = "rowid")
}
})
observe({ ###assign your orig data to a reactiveValues object
req(dat_joined$data)
if (!is.null(dat_joined$data)) {
dfon$top <- dat_joined$data
}
})
# Print the the datasets
output$mytable <- renderDT({
datatable(dfon$top,
rownames = F,
editable = "cell")
})
# Ideally the computation only happens when this both an edit is made
# and the button is pressed (now I need to press it between every edit)
observeEvent(input$mytable_cell_edit, {
info = input$mytable_cell_edit
str(info)
#i = info$row
#j = info$col + 1 # offset by 1
#v = info$value
#dfon$top[i, j] <<- DT::coerceValue(v, dfon$top[i, j])
dfon$top <<- editData(dfon$top, info)
})
observeEvent(input$button_2,{
dfon$bottom <- dfon$top
output$table2 <- renderDT({
datatable(dfon$bottom)
})
})
## further editing of dfon$bottom is performed below...with...observeEvent(input$table2_cell_edit, {...
}
)
In the output below, I have entered cccc for 3rd element in names column, but I have not clicked on the button Process. Therefore, the edited cell is not reflected in the bottom table.
This demo R script has two data frames, that are displayed by two Reactable tables.
When the number of checkboxes in the Iris table exceeds 2, the warning message in the msg table should change.
Here is my non-working attempt
library("reactable")
library("shiny")
library("tidyverse")
max_num_boxes_checked <- 2
warn_last_update_df <- tibble(
warn_msg = "Not too many selected",
last_updated_msg = "Last updated: Sept 23, 2020"
)
ui <- fluidPage(
reactableOutput("msg"),
reactableOutput("table")
)
server <- function(input, output, session){
output$msg <- renderReactable({
reactable(warn_last_update_df,
columns = list(
"last_updated_msg" = colDef(
align = "right",
name = ""
),
"warn_msg" = colDef(
name = ""
)
))
})
output$table <- renderReactable({
reactable(iris,
onClick = "select",
selection = "multiple")
})
observeEvent(input$table,
{
state <- req(getReactableState("table"))
# Get vector of which boxes are checked (their number)
boxes_checked <- state[[4]]
# Number of boxes checked
num_boxes_checked <- (length(boxes_checked))
# Change warning msg based on num checkboxes > 2
if (num_boxes_checked > max_num_boxes_checked) {
warn_last_update_df$warn_msg <- paste("Wow! More than ", max_num_boxes_checked, "checked")
updateReactable("msg")
}
}
)
}
shinyApp(ui, server)
It appears that updateReactable("msg") is not working. A workaround would be to use reactiveValues for the tibble warn_last_update_df. Here is a working code.
max_num_boxes_checked <- 2
warn_last_update_df <- tibble(
warn_msg = "Not too many selected",
last_updated_msg = "Last updated: Sept 26, 2020"
)
ui <- fluidPage(
reactableOutput("msg"),
reactableOutput("table")
)
server <- function(input, output, session){
selected <- reactiveValues(vec=NULL)
DF1 <- reactiveValues(data=NULL)
observe({
selected$vec <- getReactableState("table", "selected")
DF1$data <- warn_last_update_df
})
output$msg <- renderReactable({
reactable(DF1$data, #warn_last_update_df,
columns = list(
"last_updated_msg" = colDef(
align = "right",
name = ""
),
"warn_msg" = colDef(
name = ""
)
))
})
output$table <- renderReactable({
reactable(iris,
onClick = "select",
selection = "multiple")
})
observeEvent(selected$vec,{
# Change warning msg based on num checkboxes > 2
if (length(selected$vec) > max_num_boxes_checked) {
#warn_last_update_df$warn_msg <- paste0("Wow! More than 2 rows checked")
#updateReactable("msg",selected = NA) ## this is not working
DF1$data[1,1] <- paste0("Wow! More than ", max_num_boxes_checked, " rows checked")
}
})
}
shinyApp(ui, server)
I understand similar questions have been asked and I've tried virtually every solution with no luck.
In my application, I've allowed the user to modify individual cells of a DT::datatable. The source of the datatable is a reactive data frame.
After the user makes changes to the clientside datatable, the datatable source is remains unchanged. This is an issue as later on, when I allow the user to add rows to the data table, the row is added onto the source datatable where the clientside datatable then reflects this change. However, this means that if the user makes a change to a cell in the clientside datatable, when the user adds a row to the same table, the change made by the user will be forgotten as it was never made to the source.
I've tried many ways to update the underlying/serverside datatable with no luck. editData keeps giving me errors/NA. I also have tried indexing the serverside table and placing the changed value inside of it, with no luck. I'll post my code below with some comments for specifics..
library(shiny)
library(DT)
library(data.table)
source('~/camo/camo/R/settings.R')
source('~/camo/camo/etl.R')
# Define UI ----
ui <- fluidPage(
titlePanel("PAlpha"),
mainPanel(
fluidRow(
tabsetPanel(id = 'tpanel',
type = "tabs",
tabPanel("Alpha", plotOutput("plot1")),
tabPanel("Beta", plotOutput("plot2")),
tabPanel("Charlie", plotOutput("plot3")),
tabPanel("Delta", plotOutput("plot4")))
),
fluidRow(
splitLayout(
dateInput("sdate", "Start Date"),
dateInput("edate", "End Date"),
textInput("gmin", "Global Minimum"),
textInput("gmax", "Global Maximum")
)
),
fluidRow(
splitLayout(
textInput("groupInp", NULL, placeholder = "New Group"),
actionButton("addGrpBtn", "Add Group"),
textInput("tickerInp", NULL, placeholder = "New Ticker"),
actionButton("addTickerBtn", "Add Ticker")
)
),
fluidRow(
splitLayout(
DT::dataTableOutput('groupsTable'),
DT::dataTableOutput('groupTickers')
),
verbatimTextOutput("print")
)
)
)
# Define server logic ----
server <- function(input, output) {
port_proxy <- dataTableProxy('groupsTable')
rv <- reactiveValues(
portfolio = data.frame('Group' = c('Portfolio'), 'Minimum Weight' = c(0), 'Maximum Weight' = c(0), 'Type' = c('-')),
groups = list(group1 = data.frame('Group' = c('Ticker'), 'Minimum Weight' = c(0), 'Maximum Weight' = c(0), 'Type' = c('-'))),
deletedRows = NULL,
deletedRowIndices = list()
)
output$groupsTable <- DT::renderDataTable(
# Add the delete button column
deleteButtonColumn(rv$portfolio, 'delete_button')
)
output$print <- renderPrint({
rv$portfolio
})
############## LISTENERS ################
observeEvent(input$deletePressed, {
rowNum <- parseDeleteEvent(input$deletePressed)
dataRow <- rv$portfolio[rowNum,]
# Put the deleted row into a data frame so we can undo
# Last item deleted is in position 1
rv$deletedRows <- rbind(dataRow, rv$deletedRows)
rv$deletedRowIndices <- append(rv$deletedRowIndices, rowNum, after = 0)
# Delete the row from the data frame
rv$portfolio <- rv$portfolio[-rowNum,]
})
observeEvent(input$addGrpBtn, {
row <- data.frame('Group' = c(input$groupInp),
'Minimum Weight' = c(0),
'Maximum Weight' = c(0),
'Type' = c('-'))
rv$portfolio <- addRowAt(rv$portfolio, row, nrow(rv$portfolio))
})
observeEvent(input$groupsTable_cell_edit,{
info <- str(input$groupsTable_cell_edit)
i <- info$row
j <- info$col
v <- info$value
rv$portfolio <- editData(rv$portfolio, input$groupsTable_cell_edit) # doesn't work see below
# Warning in DT::coerceValue(v, data[i, j, drop = TRUE]) :
# New value(s) "test" not in the original factor levels: "Portfolio"; will be coerced to NA.
# rv$portfolio[i,j] <- input$groupsTable_cell_edit$value
# rv$portfolio[i,j] <- v #doesn't work
})
}
addRowAt <- function(df, row, i) {
# Slow but easy to understand
if (i > 1) {
rbind(df[1:(i - 1), ], row, df[-(1:(i - 1)), ])
} else {
rbind(row, df)
}
}
deleteButtonColumn <- function(df, id, ...) {
# function to create one action button as string
f <- function(i) {
# https://shiny.rstudio.com/articles/communicating-with-js.html
as.character(actionLink(paste(id, i, sep="_"), label = 'Delete', icon = icon('trash'),
onclick = 'Shiny.setInputValue(\"deletePressed\", this.id, {priority: "event"})'))
}
deleteCol <- unlist(lapply(seq_len(nrow(df)), f))
# Return a data table
DT::datatable(cbind(' ' = deleteCol, df),
# Need to disable escaping for html as string to work
escape = FALSE,
editable = 'cell',
selection = 'single',
rownames = FALSE,
class = 'compact',
options = list(
# Disable sorting for the delete column
dom = 't',
columnDefs = list(list(targets = 1, sortable = FALSE))
))
}
parseDeleteEvent <- function(idstr) {
res <- as.integer(sub(".*_([0-9]+)", "\\1", idstr))
if (! is.na(res)) res
}
# Run the app ----
shinyApp(ui = ui, server = server)
As far as I have looked, there is no ready-to-go solution available. You could try to use rhandsontable. It does not provide all the functionality of the DT table, however it allows for the editing. Last time I tried using it there were some minor issues in some edge cases. (Trying to save different data type or something similar.)
Alternatively you can do the stuff manually, along these lines. This is the minimal working example of editing the underlying data frame. Currently I overwrite it every time the user clicks on the table, you would need to change that to handle normal user behavior. It is meant merely as a proof of concept.
library(DT)
library(shiny)
ui <- fluidPage(
DT::dataTableOutput("test")
)
myDF <- iris[1:10,]
js <- c("table.on('click.dt','tr', function() {",
" var a = table.data();",
" var data = []",
" for (i=0; i!=a.length; i++) {",
" data = data.concat(a[i]) ",
" };",
"Shiny.setInputValue('dataChange', data)",
"})")
server <- function(input, output) {
output$test <- DT::renderDataTable(
myDF,
editable='cell',
callback=JS(js)
)
observeEvent(input$dataChange, {
res <- cbind.data.frame(split(input$dataChange, rep(1:6, times=length(input$dataChange)/6)),
stringsAsFactors=F)
colNumbers <- res[,1]
res <- res[,2:ncol(res)]
colnames(res) <- colnames(myDF)
myDF <<- res
print(myDF)
})
}
shinyApp(ui = ui, server = server)
I have written a shiny app that permits the user to amend individual rows of a dataframe but when I try to include an option to append new rows I get this warning on the console:
Input to asJSON(keep_vec_names=TRUE) is a named vector. In a future version of jsonlite, this option will not be supported, and named vectors will be translated into arrays instead of objects. If you want JSON object output, please use a named list instead. See ?toJSON.
and in a text input box that should contain an item from one column of the data frame the following appears instead:
[object Object]
There are a few answers here that refer to the warning message but in different conditions than apply in my case, and they appear to have little in common with each other apart from the warning message.
Here is my app for amending the dataframe. It works perfectly.
require(shiny)
in.df <- data.frame(name = c("Alice","Bob","Charles"),
age = c(22, 25, 36))
rownames(in.df) <- NULL
runApp(
list(
ui = fluidPage(
sidebarPanel(
numericInput("line", "Line number", value = 1),
textInput("name", "Name:"),
numericInput("age", "Age:", value = 25),
actionButton("amendButton", "Amend an entry")
),
mainPanel(
tableOutput("table"))
),
server = function(input, output, session){
values <- reactiveValues()
values$df <- in.df
current_line <- reactive({
il <- input$line
nr <- nrow(values$df)
if(il > nr){
return(nr)
} else if(il <= 0){
return(1)
} else{
return(il)
}
})
amendData <- observe({
if(input$amendButton > 0){
newLine <- isolate(c(input$name, input$age))
values$df <- isolate(values$df[- current_line(), ])
isolate(values$df <- rbind(as.matrix(values$df), unlist(newLine)))
values$df <- values$df[order(values$df[,1]),]
}
})
observe({
updateTextInput(session = session,
inputId = 'name',
value = values$df[unlist( current_line()),1]
)
updateNumericInput(session = session,
inputId = 'age',
value = values$df[unlist( current_line()),2]
)
updateNumericInput(session = session ,
inputId = 'line',
value = current_line()
)
})
output$table <- renderTable(values$df )
}
)
)
It seemed to me that it would be a simple matter to add an 'append' option in the following way:
Add a new action button
actionButton("appendButton", "Append an entry")
Include a corresponding handler that can be very similar indeed to the handler for the addButton:
addData <- observe({
if(input$appendButton > 0){
newLine <- isolate(c(input$name, input$age))
isolate(values$df <- rbind(as.matrix(values$df), unlist(newLine)))
values$df <- values$df[order(values$df[,1]),]
}
})
The only difference of substance between the two handlers is that the new one does not need the line
values$df <- isolate(values$df[- current_line(), ])
because in the append case no old row is being removed.
But it does not work: I get the warning and the odd change to the text input box that I described.
In shiny 1.6 I got a running app after I changed amendData <- observe to amendData <- observeEvent. Otherwise the code got stuck in an infinite loop.
However, in order to be able to add new rows I had to change reactive value current_line. The code always resets it to an existing row so that one can never add new entries.
I had changed current_line so that it also allowed it to be nrow + 1 and cleared the numeric input fields when current_line was larger than the number of rows.
Now, I finally saw the situation that was described in the question.
It was caused by values$df <- rbind(as.matrix(values$df), unlist(newLine)). R added the new row with a name. The named rows of the data frame seemed to be the problem when sent to the UI. My guess is that this is a problem deeply buried in the reactive messaging system of Shiny.
require(shiny)
in.df <- data.frame(name = c("Alice","Bob","Charles"),
age = c(22L, 25L, 36L))
rownames(in.df) <- NULL
runApp(
list(
ui = fluidPage(
sidebarPanel(
numericInput("line", "Line number", value = 1),
textInput("name", "Name:"),
numericInput("age", "Age:", value = 25),
actionButton("amendButton", "Amend an entry")
),
mainPanel(
tableOutput("table"))
),
server = function(input, output, session){
values <- reactiveValues()
values$df <- in.df
current_line <- reactive({
il <- req(input$line)
nr <- nrow(values$df)
if(il > nr){
return(nr+1)
} else if (il <= 0){
return(1)
} else {
return(il)
}
})
amendData <- observeEvent(input$amendButton, {
isolate({
newLine <- c(input$name, as.numeric(input$age))
values$df <- values$df[- current_line(), ]
values$df <- rbind(values$df, unname(newLine))
})
values$df <- values$df[order(values$df[,1]),]
})
observe({
updateNumericInput(session = session, inputId = 'line',
value = current_line())
if (current_line() <= nrow(values$df)) {
updateNumericInput(session = session, inputId = 'age',
value = values$df[current_line(), 2])
updateTextInput(session = session, inputId = 'name',
value = values$df[current_line(), 1])
}
else {
updateNumericInput(session = session, inputId = 'age', value = "")
updateNumericInput(session = session, inputId = 'name', value = "")
}
})
output$table <- renderTable( values$df )
}
)
)