3

I would like to create alternative UIs for my app depending on URL parameter. Specifically, I'd like to use the drop-down pickerInput from shinyWidgets instead of checkboxGroupInput, but only if parameter mini=TRUE is passed via the URL

library(shinyWidgets)

nazwy=c('Warszawa', 'Krakow', 'Gdansk')

ui<-fluidPage(
 if (mini) {
    pickerInput(inputId = "miasto", choices = nazwy,multiple = TRUE)
 } else {
  checkboxGroupInput('miasto', nazwy)
 })

Can I somehow do it using the conditionalPanel, or is renderUI my only option?

Alternatively, can I make the pickerInput or the checkboxGroupInput appear depending on the window width?

UPDATE

Option 1 given below is a working answer to my initial question, making UI dependent on URL. Option 2 is a better solution for this particular case - adapting UI to window size. HOWEVER, for option 2 to work properly, one needs another variable to store selections, so that the select/pickerInput doesn't go back to default selection every time window is resized. I edited the code accordingly.

1 Answer 1

2

Four options below:

  1. Dependent on URL, renderUI
  2. Dependent on window width, renderUI
  3. Dependent on window width, conditionalPanel (does not work properly)
  4. Dependent on window width, shinyjs

Option 1: Dependent on URL, renderUI

It is possible to make it dependent on the URL, see for example here. Here is an example implementation:

library(shinyWidgets)
library(shiny)

nazwy=c('Warszawa', 'Krakow', 'Gdansk')

ui<-fluidPage(
  uiOutput('myUI')
)


server <- function(input,output,session) {

  output$myUI <- renderUI({
    query <- parseQueryString(session$clientData$url_search)
    if (!is.null(query$mini)) {
      if(query$mini==TRUE)
      {
        pickerInput(inputId = "miasto", choices = nazwy,multiple = TRUE)
      }
      else
      {
        checkboxGroupInput('miasto', 'choose: ',nazwy)
      }
    } 
    else 
    {
      checkboxGroupInput('miasto', 'choose: ',nazwy)
    }
  }) 
}

shinyApp(ui,server,options=list(port=7777))

try both http://127.0.0.1:7777/ and http://127.0.0.1:7777/?mini=TRUE.


Option 2: Dependent on window width, renderUI

If you would want to make it dependent on the window width, here is a possible solution:

library(shinyWidgets)
library(shiny)

nazwy=c('Warszawa', 'Krakow', 'Gdansk')

ui<-fluidPage(
  tags$head(tags$script('
                                var dimension = [0, 0];
                        $(document).on("shiny:connected", function(e) {
                        dimension[0] = window.innerWidth;
                        dimension[1] = window.innerHeight;
                        Shiny.onInputChange("dimension", dimension);
                        });
                        $(window).resize(function(e) {
                        dimension[0] = window.innerWidth;
                        dimension[1] = window.innerHeight;
                        Shiny.onInputChange("dimension", dimension);
                        });
                        ')),
  uiOutput('myUI')
)


server <- function(input,output) {
   output$myUI <- renderUI({
     req(input$dimension)
     if (input$dimension[1]<800) {
        pickerInput(inputId = "miasto", choices = nazwy, 
                     selected=isolate(selected_cities()),multiple = TRUE)
     } else {
        checkboxGroupInput('miasto', 'choose: ',
                     choices=nazwy, selected=isolate(selected_cities()))
     }
   })

   #store selected value to pass on resizing
   selected_cities<-reactive(input$miasto) 

}

shinyApp(ui,server)

option 3: Window width + conditionalPanel. NOTE: Does not work as expected.

   library(shinyWidgets)
    library(shiny)

    nazwy=c('Warszawa', 'Krakow', 'Gdansk')

    ui<-fluidPage(
      tags$head(tags$script('var dimension = [0, 0];
                            $(document).on("shiny:connected", function(e) {
                            dimension[0] = window.innerWidth;
                            dimension[1] = window.innerHeight;
                            Shiny.onInputChange("dimension", dimension);
                            });
                            $(window).resize(function(e) {
                            dimension[0] = window.innerWidth;
                            dimension[1] = window.innerHeight;
                            Shiny.onInputChange("dimension", dimension);
                            });
                            ')),
      conditionalPanel(condition = 'input.dimension[0]>1000',
                       pickerInput(inputId = "miasto", choices = nazwy,multiple = TRUE)
      ),
      conditionalPanel(condition = 'input.dimension[0]<=1000',
                       checkboxGroupInput('miasto', 'choose: ',nazwy))
    )


    server <- function(input,output) {

    }

    shinyApp(ui,server)

Option 4: window width + shinyjs

library(shinyWidgets)
library(shiny)
library(shinyjs)

nazwy=c('Warszawa', 'Krakow', 'Gdansk')

ui<-fluidPage(
  tags$head(tags$script('var dimension = [0, 0];
                        $(document).on("shiny:connected", function(e) {
                        dimension[0] = window.innerWidth;
                        dimension[1] = window.innerHeight;
                        Shiny.onInputChange("dimension", dimension);
                        });
                        $(window).resize(function(e) {
                        dimension[0] = window.innerWidth;
                        dimension[1] = window.innerHeight;
                        Shiny.onInputChange("dimension", dimension);
                        });
                        ')),
  div(id='div1',pickerInput(inputId = "miasto", choices = nazwy,multiple = TRUE)),
  shinyjs::hidden(div(id='div2',checkboxGroupInput('miasto', 'choose: ',nazwy))),
  useShinyjs()
)


server <- function(input,output) {
  observeEvent(input$dimension,ignoreNULL=T,{
    if (input$dimension[1]>1000) {
      shinyjs::show('div1')
      shinyjs::hide('div2')    
    } else {
      shinyjs::show('div2')
      shinyjs::hide('div1')   
    }
  }) 
}

shinyApp(ui,server)
Sign up to request clarification or add additional context in comments.

8 Comments

thanks! I tried your window width solution, it works apart from a warning that it produces (Error in if: argument is of length zero). Can be fixed by adding 'req(input$dimension)'; however, both these solutions use renderUI, which I was hoping to avoid
@MonikaP, I added an example that uses conditionalPanels + the windows width. However, I do not know of a clean way to do the same with the URL parameter, I really think renderUI would be the way to go in that case. Hope this helps!
I uploaded the dimension+renderUI solution (option 2) to the server. Here's the thing: in an iframe with fixed width it works just fine. On a smartphone, when I scroll, the pickerInput keeps reverting to the original value defined by 'selected='. Somehow the javascript is triggered although I'm not resizing the window on the smartphone...?
That is very strange! Can you add console.log(window.innerWidth); to the javascript, and check what the console logs when you scroll? If it does not actually change, maybe we can add a reactiveVal in between and make the renderUI dependent on that.
ha! Found the error. Your code was referring to window height not width
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.