You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Shiny slickR优化:未填写输入时阻止幻灯片切换

解决方案

要实现点击幻灯片切换箭头时检查当前输入是否为空并阻止切换,核心是利用slick的beforeChange事件拦截切换动作,同时结合输入状态验证。以下是修改后的完整代码及关键说明:

修改后的完整代码

library(shiny)
library(shinyalert)
library(slickR)
library(tidyverse)
library(shinyvalidate)
library(shinyjs)

js <- "
$(document).ready(function(){
  var ss = document.getElementById('slickr');
  
  // 拦截幻灯片切换前的事件
  $(ss).on('beforeChange', function(event, slick, currentSlide, nextSlide) {
    // 当前幻灯片索引(从0开始),对应输入项的序号是currentSlide+1
    var currentInputSeq = currentSlide + 1;
    var inputId;
    
    // 匹配当前幻灯片对应的输入框ID
    if(currentInputSeq === 1){
      inputId = 'Name';
    } else if(currentInputSeq >=2 && currentInputSeq <=7){
      inputId = 'Photo' + (currentInputSeq -1);
    } else {
      // 最后一页是提交按钮,允许切换
      return true;
    }
    
    // 获取输入框的值并检查是否为空
    var inputValue = $('#' + inputId).val();
    if(!inputValue || inputValue.trim() === ''){
      // 阻止幻灯片切换
      event.preventDefault();
      event.stopPropagation();
      // 弹出提示
      alert('请先填写当前项的内容!');
      return false;
    }
  });
  
  // 原有的MutationObserver,同步幻灯片与输入项
  var observer = new MutationObserver(function(mutations) {
    var index = $(ss).find('.slick-current').data('slick-index');
    Shiny.setInputValue('imageIndex', parseInt(index)+1);
  });
  var config = {subtree: true, attributes: true};
  observer.observe(ss, config);
 
  var deleteThis = function(elem){
        elem.style.display = 'none';
  };

})
"

cP1=htmlwidgets::JS("function(slick,index) {return '<a>'+(index+1)+'</a>';}")

# 读取图片文件(确保samplepictures文件夹下有对应数量的图片)
imgs <- list.files("samplepictures/", pattern=".jpg|.jpeg|.png", full.names = TRUE)
imgs <- imgs[!grepl("photo_ages",imgs)]

# 构建输入项列表
photoages <- lapply(1:6,function(x){ 
  numericInput(paste0("Photo",x),
               paste0("Photo ",x," Age Guess:"),
               min = 1,
               max = 100,
               value = NULL)
})
exfn <- function(x= "Guesser"){
  textInput("Name",paste(x))
}
actionfn <- function(){
  actionButton("go", "Submit All Guesses")
}

photoages <- as.vector(photoages)
photoages <- c(list(exfn()),  # 修正原代码拼写错误:texfn改为exfn
               photoages,
               list(actionfn()))

ui <- fluidPage(
  useShinyjs(),
  useShinyalert(), # 可选,用于替换原生alert
  
  titlePanel("Photo Guesses"),
  mainPanel(
    tags$head(
      tags$script(HTML(js)),
      tags$style(HTML("
        .arrows {
          height: 30px;
        }
        .slick-prev {
          left: 10px; 
        }
        .slick-next {
          left: 30px;  
        }
      "))
    ),
    
    fluidRow(
      column(4,align = "left", div(id = "mydiv", uiOutput("photoinput"))) ,
      column(4, align = "left", div(id = "npht", h4("Advance:")), tags$br(), div(id = "arr", class="arrows"))
    ),

    tags$hr(),
    slickROutput("slickr") ,
    tags$br()
  )
)

server <- function(input, output,session) {
 
  # 配置输入验证规则,强制必填
  iv <- InputValidator$new()
  iv$add_rule("Name", sv_required())
  iv$add_rule("Photo1", sv_between(1,100, allow_na = FALSE))
  iv$add_rule("Photo2", sv_between(1,100, allow_na = FALSE))
  iv$add_rule("Photo3", sv_between(1,100, allow_na = FALSE))
  iv$add_rule("Photo4", sv_between(1,100, allow_na = FALSE))
  iv$add_rule("Photo5", sv_between(1,100, allow_na = FALSE))
  iv$add_rule("Photo6", sv_between(1,100, allow_na = FALSE))
  iv$enable()
  
  observeEvent(input[["imageIndex"]],{
    if(input[["imageIndex"]] >7){
      removeUI("#npht")
      removeUI("#arr")
    }
  })
  
  output$slickr <- renderSlickR({
    imgs <- list.files("samplepictures/", pattern=".jpg|.jpeg|.png", full.names = TRUE)
    imgs <- imgs[!grepl("photo_ages",imgs)]
    slickR(imgs) +
      slickR::settings(dots = TRUE,
                       appendArrows = '.arrows',
                       prevArrow = '<button type="button" class="slick-prev">Previous</button>',
                       nextArrow = '<button type="button" class="slick-next">Next</button>'
      ) 
  })
  
  output[["photoinput"]] <- renderUI({
    req(input[["imageIndex"]])
    photoages[input[["imageIndex"]]]
  })
     
}

shinyApp(ui = ui, server = server)

关键修改说明

  1. 拦截幻灯片切换事件:

    • 通过slick的beforeChange事件在切换前触发检查逻辑,利用event.preventDefault()阻止无效切换。
    • 根据当前幻灯片索引匹配对应的输入框ID,检查输入值是否为空,为空则弹出提示并终止切换。
  2. 强化输入验证:

    • 将sv_between中的allow_na = T改为FALSE,确保数字输入框必须填写有效值。
  3. 修复代码错误:

    • 修正原代码中texfn的拼写错误,改为正确的exfn。
  4. 优化箭头按钮定义:

    • 明确指定prevArrow和nextArrow的HTML结构,确保事件能正确绑定到按钮上。

如果偏好使用shinyalert替代原生弹窗,可将JS中的alert替换为:

Shiny.setInputValue('showAlert', true, {priority: 'event'});

并在server中添加:

observeEvent(input$showAlert, {
  shinyalert("提示", "请先填写当前项的内容!", type = "warning")
})

内容的提问来源于stack exchange,提问作者Mike

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 18:28:16