В приведенном ниже коде R Shiny я пытаюсь добавить раскрывающееся меню только к последней строке таблицы, отображаемой с помощью rhandsontable. Обратите внимание, что таблица может быть расширена пользователем по столбцам с помощью кнопки действия «Добавить серию». Как применить раскрывающийся список только к последней строке таблицы, а не к каждой строке таблицы, как это происходит в настоящее время с приведенным ниже кодом? Я пробовал hot_row, hot_rows и hot_cell, но не уверен, что они это поддерживают. См. поясняющую иллюстрацию ниже. Обратите внимание, что раскрывающийся список также должен отображаться с каждым добавленным столбцом, что в настоящее время работает, но раскрывающиеся списки не должны отображаться в Row_A и Row_B. Раскрывающийся список должен отображаться только в Row_C.
Код:
library(rhandsontable)
library(shiny)
ui <-
fluidPage(
rHandsontableOutput('hottable_1'),
actionButton("addSeries","Add series")
)
server <- function(input,output,session)({
seriesTbl_1 <- reactiveVal(
data.frame(
'Series 1' = c(1,24,NA),
row.names = c("Row_A_numeric","Row_B_numeric","Row_C_dropdown")
)
)
observeEvent(input$hottable_1, {seriesTbl_1(hot_to_r(input$hottable_1))})
output$hottable_1 <- renderRHandsontable({
tbl <- seriesTbl_1()
select_option <- c(NA_character_, "Item A", "Item B")
rhandsontable(
tbl,
rowHeaderWidth = 200,
useTypes = TRUE,
selectCallback = TRUE,
overflow = "visible"
) %>%
hot_table(id = "hottable_1") %>%
hot_col(
col = names(tbl),
allowInvalid = FALSE,
type = "dropdown",
source = select_option
)
})
observeEvent(input$addSeries, {
newSeriesCol_1 <- data.frame(c(1,24,NA))
names(newSeriesCol_1) <- paste("Series", ncol(hot_to_r(input$hottable_1)) + 1)
seriesTbl_1(cbind(seriesTbl_1(), newSeriesCol_1))
})
seriesTbl_1_DF <- reactive({seriesTbl_1()})
})
shinyApp(ui, server)



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Ниже приведено решение с использованием Javascript, которое должно это сделать:
Мы используем событие afterInit , где вызывается updateSettings. Это определяет настройки раскрывающегося списка в последней строке.
instance.updateSettings({
cells: function(row, col, prop) {
var cellProperties;
if (row === 2) {
cellProperties = {
type: 'dropdown',
allowInvalid: false,
source: select_option,
};
return cellProperties;
}
}
});
Обратите внимание, что select_option — это вектор, определенный в R, который я передал ниже объекту rhandsontable, чтобы я мог использовать его в JS, обратившись к instance.params. Однако я обернул код в небольшой setTimeout, потому что кажется, что непосредственно при вызове afterInitparams недоступен. Единственная причина его использования заключается в том, что вы можете определить параметры в R. Если у вас нет проблем с их определением непосредственно в JS, вы можете опустить их ниже. Я также пробовал другие мероприятия, но у меня возникло несколько проблем, которые могут возникнуть из-за shiny окружающей среды.
rhandsontable не поддерживается уже несколько лет и, в частности, зависит от handsontable 6.2.2. В более старой версии (см. handontable/handsontable#7689 ) была ошибка, из-за которой заголовки столбцов отображались неправильно после использования updateSettings. По крайней мере, это похоже на проблему, которая была и у меня, поэтому я использовал то, что было зафиксировано, чтобы решить эту проблему в afterRenderer событии:
function(TD, row, column, prop, value, cellProperties) {
this.view.wt.wtOverlays.adjustElementsSize();
}
Это будет выглядеть так:
library(rhandsontable)
library(shiny)
ui <-
fluidPage(
rHandsontableOutput('hottable_1'),
actionButton("addSeries","Add series")
)
server <- function(input,output,session)({
seriesTbl_1 <- reactiveVal(
data.frame(
'Series 1' = c(1,24,NA),
row.names = c("Row_A_numeric","Row_B_numeric","Row_C_dropdown")
)
)
observeEvent(input$hottable_1, {seriesTbl_1(hot_to_r(input$hottable_1))})
output$hottable_1 <- renderRHandsontable({
tbl <- seriesTbl_1()
rhandsontable(
tbl,
rowHeaderWidth = 200,
useTypes = TRUE,
selectCallback = TRUE,
overflow = "visible",
select_option = c(NA_character_, "Item A", "Item B"),
afterInit = htmlwidgets::JS("
function() {
let instance = this;
setTimeout(function (){
select_option = instance.params.select_option
select_option = select_option instanceof Array ? select_option : [select_option]
instance.updateSettings({
cells: function(row, col, prop) {
var cellProperties;
if (row === 2) {
cellProperties = {
type: 'dropdown',
allowInvalid: false,
source: select_option,
};
return cellProperties;
}
}
});
}, 50);
}"),
afterRenderer = htmlwidgets::JS(
"function (TD, row, column, prop, value, cellProperties) {
this.view.wt.wtOverlays.adjustElementsSize();
}
")
) %>%
hot_table(id = "hottable_1")
})
observeEvent(input$addSeries, {
newSeriesCol_1 <- data.frame(c(1,24,NA))
names(newSeriesCol_1) <- paste("Series", ncol(hot_to_r(input$hottable_1)) + 1)
seriesTbl_1(cbind(seriesTbl_1(), newSeriesCol_1))
})
seriesTbl_1_DF <- reactive({seriesTbl_1()})
})
shinyApp(ui, server)
Привет, Ян. При запуске этого кода я получаю следующее предупреждение: «Предупреждение в colClasses(as.data.frame(out, stringsAsFactors = FALSE), rColClasses,: NA, введенные принуждением». А пока я публикую альтернативное решение в другой ответ. Пожалуйста, дайте мне знать, что вы думаете.
Простое исправление — в двух местах формирования фрейма данных (seriesTbl_1 <- reactiveVal(data.frame(...)) и newSeriesCol_1 <- data.frame(...)) заменить NA на NA_character_. Это устраняет предупреждения о принуждении.
Большое спасибо за подсказку. Однако если я это сделаю, числа будут выровнены по левому краю, скорее всего, потому, что они рассматриваются как строки. Я также могу добавить else {cellProperties = {type: 'numeric'}, и тогда единственная разница между текущим решением и новым решением будет заключаться в том, что оно отображает числа, например. «24» вместо «24.00». Что Вы думаете об этом?
Судя по комментарию исмиршерегала, приведенное ниже сообщение работает. Изменения в коде OP заключаются в замене NA в настройке фрейма данных на NA_character_ и добавлении htmlwidgets::onRender(...) к output$hottable:
library(rhandsontable)
library(shiny)
ui <- fluidPage(
rHandsontableOutput('hottable_1'),
actionButton("addSeries", "Add series")
)
server <- function(input, output, session) {
seriesTbl_1 <- reactiveVal(
data.frame(
'Series 1' = c(1, 24, NA_character_),
row.names = c("Row_A_numeric", "Row_B_numeric", "Row_C_dropdown"),
stringsAsFactors = FALSE
)
)
observeEvent(input$hottable_1, {seriesTbl_1(hot_to_r(input$hottable_1))})
output$hottable_1 <- renderRHandsontable({
tbl <- seriesTbl_1()
rhandsontable(
tbl,
rowHeaderWidth = 200,
useTypes = TRUE,
selectCallback = TRUE,
overflow = "visible"
) %>%
hot_table(id = "hottable_1") %>%
htmlwidgets::onRender("
function(el, x) {
var hot = this.hot;
hot.updateSettings({
cells: function (row, col, prop) {
var cellProperties = {};
if (row === 2) { // Third row
cellProperties.type = 'dropdown';
cellProperties.source = ['Item A', 'Item B'];
} else { // First two rows
cellProperties.type = 'numeric';
}
return cellProperties;
}
});
}
")
})
observeEvent(input$addSeries, {
newSeriesCol_1 <- data.frame(
c(1, 24, NA_character_),
stringsAsFactors = FALSE
)
names(newSeriesCol_1) <- paste("Series", ncol(hot_to_r(input$hottable_1)) + 1)
seriesTbl_1(cbind(seriesTbl_1(), newSeriesCol_1))
})
}
shinyApp(ui, server)
Хорошее решение, + 1. Кажется, htmlwidgets::onRender позволяет избежать нескольких проблем, с которыми я столкнулся со своим решением. Как указано в другом комментарии, я думаю, что здесь нет предупреждений о принуждении, потому что числа отображаются в каком-то другом смысле (кажется, есть приведение из-за NA_character_), вместо 24.00, который по умолчанию используется для type: 'numeric', насколько я понимаю, у одного есть 24.
Кажется
library(rhandsontable)не поддерживает этот сценарий. Возможно, вы сможете внедрить JS-решение черезhtmlwidgets::onRender.