The below example code has 3 linked user input matrixes: the first, base_input
, allows the user to make static inputs and the next 2 user input matrixes, var_1_input
and var_2_input
(collectively, "var_x_input"), are reactively fed the values from base_input
and the slider input for time horizon, and allow the user to alter the time dimension in their respective left-most user input columns labeled "X". The image below shows the data flows. How can I replace the one isolate()
in this code with other reactivity-management code?
The problem I'm having is the isolate()
conflicts with the js code for managing decimals. The js code ensures that a minimum of 2 decimal places are shown in the matrix input cells. When activating the js code (commented-out below), the reactivity flow between base_input
and the var_x_input
matrixes no longer works (meaning for example an input in base_input[1,1]
needs to be instantly reflected in the upper right cell of var_1_input
), although the decimals work perfectly. If I remove the isolate()
, then another undesirable happens: changing a value in one cell in base_input
resets both of the var_x_input
matrixes instead of just the var_x_input
matrix linked to base_input
. The isolate()
ensures that each var_x_input
is indepenendently linked to its corresponding cell in base_input
.
I'm trying to get all 3 features working: downstream reactivity between base_input
and the var_x_input
matrixes; formatting of specified user input cells with a minimum of 2 decimal places (per the js code); and changing the base_input
cell for one var_x_input
should reset only that var_x_input
matrix and not all of the var_x_input
matrixes.
Code:
library(shinyMatrix)
library(shiny)
library(shinyjs)
# js <- "
# $(document).on('shiny:bound', function(event) {
# var $elem = $(event.target);
# if($elem.hasClass('vue-input')) {
# $elem.find('td').each(function() {
# var $td = $(this);
# var columnIndex = $td.index();
# var $table = $td.closest('.vue-input');
# var tableId = $table.attr('id');
# var isVarInput = tableId.startsWith('var_') && tableId.endsWith('_input');
# var text = $td.find('span').text();
# var num = parseFloat(text);
#
# if (!isNaN(num)) {
# if (isVarInput && columnIndex == 0) {
# // Format with 0 decimal places for the first column of var_x_input
# $td.find('span').html(num.toFixed(0));
# } else {
# // Use 2 decimal places for the second column of var_x_input and other cases
# var decimalPlaces = (text.split('.')[1] || []).length;
# var fixed = decimalPlaces < 2 ? 2 : decimalPlaces;
# $td.find('span').html(num.toFixed(fixed));
# }
# }
# });
# }
# });
#
# $(document).ready(function() {
# $('body').on('focusout', '.vue-input table td input', function(e) {
# var $td = $(this).parent();
# var columnIndex = $td.index();
# var $table = $td.closest('.vue-input');
# var tableId = $table.attr('id');
# var isVarInput = tableId.startsWith('var_') && tableId.endsWith('_input');
#
# var interval = setInterval(function() {
# if ($td.children().is('span')) {
# clearInterval(interval);
# var $span = $td.find('span');
# var text = $span.text();
# var num = parseFloat(text);
#
# if (!isNaN(num)) {
# if (isVarInput && columnIndex == 0) {
# // Format with 0 decimal places for the first column of var_x_input
# $span.html(num.toFixed(0));
# } else {
# // Use 2 decimal places for the second column of var_x_input and other cases
# var decimalPlaces = (text.split('.')[1] || []).length;
# var fixed = decimalPlaces < 2 ? 2 : decimalPlaces;
# $span.html(num.toFixed(fixed));
# }
# }
# }
# }, 50);
# });
# });
# "
matInputBase <- function(name) {
matrixInput(name,
value = matrix(rep(0.20,2),2, 1,dimnames = list(c("A","B"), NULL)),
rows = list(extend = FALSE, names = TRUE),
cols = list(extend = FALSE, names = FALSE, editableNames = FALSE),
class = "numeric")
}
matInputVary <- function(name, x,y) {
matrixInput(
name,
value = matrix(c(x, y), 1, 2, dimnames = list(NULL,c("X","Y"))),
rows = list(extend = TRUE, names = FALSE),
cols = list(extend = TRUE, delta = 0, names = TRUE, editableNames = FALSE),
class = "numeric"
)
}
ui <- fluidPage(
useShinyjs(),
# tags$head(tags$script(HTML(js))),
sliderInput("periods","Time window (W):", min = 1, max = 120, value = 60),
h5(strong("Variable (Y) over window (W):")),
matInputBase("base_input"),
uiOutput("Vectors")
)
server <- function(input, output, session) {
prev_base_input <- reactiveValues(data = matrix(NA, nrow = 2, ncol = 1))
observeEvent(input$base_input, {
for (i in 1:2) {
if (is.na(prev_base_input$data[i,1])||input$base_input[i,1]!=prev_base_input$data[i,1]){
updateMatrixInput(
session,
paste0("var_", i, "_input"),
value = matrix(c(input$periods,input$base_input[i,1]),1,2,dimnames=list(NULL,c("X","Y")))
)
prev_base_input$data[i, 1] <- input$base_input[i, 1]
}
}
})
output$Vectors <- renderUI({
input$resetVectorBtn
varNames <- c("A", "B")
tagList(
lapply(1:2, function(i) {
list(
h5(strong(paste("Adjust", varNames[i], "(Y) at time X:"))),
matInputVary(paste0("var_", i, "_input"), input$periods, isolate(input$base_input[i,1]))
)
})
)
})
}
shinyApp(ui, server)
library(shiny)
library(shinyMatrix)
js <- "
$(document).ready(function() {
$('#base_input').on('shiny:bound', function(event) {
var matrix = $(this).data('values');
$(this).find('td:eq(0) span').text(matrix[0][0].toFixed(2));
$(this).find('td:eq(1) span').text(matrix[1][0].toFixed(2));
});
$(document).on('shiny:updateinput', function(e) {
var $target = $(e.target);
if($target.hasClass('vue-input')) {
var matrix = e.message.value.data;
$target.find('td:eq(0) span').text(matrix[0][0].toFixed(2));
$target.find('td:eq(1) span').text(matrix[0][1].toFixed(2));
}
});
$('#base_input').on('blur', 'table td input', function(e) {
var $td = $(this).parent();
var val = parseFloat($(this).val()).toFixed(2);
var interval = setInterval(function() {
if($td.children().is('span')) {
clearInterval(interval);
$td.find('span').text(val);
}
});
});
});
"
matInputBase <- function(name) {
matrixInput(
name,
value = matrix(rep(20,2),2, 1,dimnames = list(c("A", "B"), NULL)),
rows = list(extend = FALSE, names = TRUE),
cols = list(extend = FALSE, names = FALSE, editableNames = FALSE),
class = "numeric"
)
}
matInputVary <- function(name, x,y) {
matrixInput(
name,
value = matrix(c(x, y), 1, 2, dimnames = list(NULL,c("X","Y"))),
rows = list(extend = TRUE, names = FALSE),
cols = list(extend = TRUE, delta = 0, names = TRUE, editableNames = FALSE),
class = "numeric"
)
}
ui <- fluidPage(
tags$head(
tags$script(HTML(js))
),
sliderInput("periods","Time window (W):", min = 1, max =10, value = 5),
h5(strong("Variable (Y) over window (W):")),
matInputBase("base_input"),
uiOutput("Vectors")
)
server <- function(input, output, session) {
prev_base_input <- reactiveValues(data = matrix(NA, nrow = 2, ncol = 1))
observeEvent(input$base_input, {
for (i in 1:2) {
if (is.na(prev_base_input$data[i,1])||input$base_input[i,1]!=prev_base_input$data[i,1]){
updateMatrixInput(
session,
paste0("var_", i, "_input"),
value = matrix(c(input$periods,input$base_input[i,1]),1,2,dimnames=list(NULL,c("X","Y")))
)
prev_base_input$data[i, 1] <- input$base_input[i, 1]
}
}
}, ignoreInit = FALSE)
output$Vectors <- renderUI({
input$resetVectorBtn
varNames <- c("A", "B")
lapply(1:2, function(i) {
list(
h5(strong(paste("Adjust", varNames[i], "(Y) at time X:"))),
matInputVary(paste0("var_", i, "_input"), input$periods, isolate(input$base_input)[i, 1])
)
})
})
}
shinyApp(ui, server)