Cant transform my functional shiy app to one with shiny modules

116 Views Asked by At

What currently works

Hello I am trying to build a shiny app for species sampling. For each species that is selected I need to select in which kind of sampling, and if it is selected in a pin-point sampling it needs to be counted, here is a working example:

library(shiny)
library(shinyMobile)

ui = f7Page(
  title = "Show navbar",
  f7SingleLayout(
    navbar = f7Navbar("Hide/Show navbar"),
    f7Button(inputId = "toggle", "Toggle navbar", color = "red"),
    f7Text(inputId = "SpeciesName", label = "SpeciesName"),
    shinyMobile::f7Card(
      f7Flex(
        textOutput("SpeciesAgain"),
        uiOutput("Sampling_type_ui"),
        uiOutput("SpeciesCount")
      )
    )
  )
)
server = function(input, output, session) {
  
  output$SpeciesAgain <- renderText({input$SpeciesName})
  
  output$Sampling_type_ui <- renderUI({
    req(input$SpeciesName)
    f7Select(inputId = "Sampling_type", 
             label = "Sampling type", 
             choices = c("Pin-point", "5m circle", "15m circle"))
    
  })
  
  output$SpeciesCount <- renderUI({
    if (req(input$Sampling_type) == "Pin-point") {
      shinyMobile::f7Stepper(inputId = "Species1", label = "Species count", min = 1, max = 1000, step = 1, value = 1)
    }
  })
  
  observeEvent(input$toggle, {
    updateF7Navbar()
  })
}

shinyApp(ui, server)

This is working just as I want since, it waits until I write a species name, for the f7select to appear, and if I select Pin-point I can use the counter to get the number of individuals.

This does not work

However I will need to select several species in the real app, which is why I want to turn this into a Shiny Module. This is what I have tried:

library(shiny)
library(shinyMobile)

#Species <- "Nothofagus"

Species_UI <- function(id){
  f7Text(inputId = NS(id,"SpeciesName"), label = "SpeciesName")
  shinyMobile::f7Card(
    f7Flex(
      textOutput(NS(id, "SpeciesAgain")),
      uiOutput(NS(id, "Sampling_type_ui")),
      uiOutput(NS(id,"SpeciesCount"))
    )
  )
}

Species_Server <- function(id){
  moduleServer(id, function(input, output, session) {
    output$SpeciesAgain <- renderText({input$SpeciesName})
    
    output$Sampling_type_ui <- renderUI({
      req(input$SpeciesName)
      f7Select(inputId = "Sampling_type", 
               label = "Sampling type", 
               choices = c("Pin-point", "5m circle", "15m circle"))
      
    })
    
    output$SpeciesCount <- renderUI({
      if (req(input$Sampling_type) == "Pin-point") {
        shinyMobile::f7Stepper(inputId = "Species1", label = "Species count", min = 1, max = 1000, step = 1, value = 1)
      }
    })
  })
}

library(shiny)
library(shinyMobile)

ui = f7Page(
    title = "Show navbar",
    f7SingleLayout(
      navbar = f7Navbar("Hide/Show navbar"),
      f7Button(inputId = "toggle", "Toggle navbar", color = "red"),
      Species_UI("Species")
    )
  )
  server = function(input, output, session) {
    
    Species_Server("Species")
    
    observeEvent(input$toggle, {
      updateF7Navbar()
    })
  }

  shinyApp(ui, server)

Now when I do this, the UI in the modules does not appear in the app, but I can't figure out what is wrong.

Extra question

in a final option of the app I want to replace the f7Text that I use to input the species for a f7SmartSelect, Where the input for species names is as follows:

library(shiny)
library(shinyMobile)

ui = f7Page(
  title = "Show navbar",
  f7SingleLayout(
    navbar = f7Navbar("Hide/Show navbar"),
    f7Button(inputId = "toggle", "Toggle navbar", color = "red"),
    f7SmartSelect(inputId = "SpeciesName", label = "SpeciesName", 
                  choices = c("Species1", "Species2", "Species3", "Species4", "Species5"),
                  multiple = T, openIn = "popup"),
    shinyMobile::f7Card(
      f7Flex(
        textOutput("SpeciesAgain"),
        uiOutput("Sampling_type_ui"),
        uiOutput("SpeciesCount")
      )
    )
  )
)
server = function(input, output, session) {
  
  output$SpeciesAgain <- renderText({input$SpeciesName})
  
  output$Sampling_type_ui <- renderUI({
    req(input$SpeciesName)
    f7Select(inputId = "Sampling_type", 
             label = "Sampling type", 
             choices = c("Pin-point", "5m circle", "15m circle"))
    
  })
  
  output$SpeciesCount <- renderUI({
    if (req(input$Sampling_type) == "Pin-point") {
      shinyMobile::f7Stepper(inputId = "Species1", label = "Species count", min = 1, max = 1000, step = 1, value = 1)
    }
  })
  
  observeEvent(input$toggle, {
    updateF7Navbar()
  })
}

shinyApp(ui, server)

So that the module is repeated for each species

Any help is welcome

1

There are 1 best solutions below

3
On BEST ANSWER

Two issues in your code:

  • the UI modules should return a tagList() so you need to use
tagList(
    f7Text(inputId = NS(id,"SpeciesName"), label = "SpeciesName"),
    shinyMobile::f7Card(
      f7Flex(
        textOutput(NS(id, "SpeciesAgain")),
        uiOutput(NS(id, "Sampling_type_ui")),
        uiOutput(NS(id,"SpeciesCount"))
      )
    )
)
  • when you create an input in the server (like your f7Select()), you still need to be careful about the namespace. In the server part of a module, you need to use session$ns(id-of-input). This is specified in the section "Using renderUI within modules" of the R Shiny modules article. In your case you should use:
f7Select(inputId = session$ns("Sampling_type"), 
               label = "Sampling type", 
               choices = c("Pin-point", "5m circle", "15m circle"))

Small addition: usually, in the UI part of a module, it is better to define ns <- NS(id) and then to use ns(id-of-input). Similarly, in the server part of a module, you can define ns <- session$ns.