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.

MonikaP
  • 147
  • 9

1 Answers1

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)
MonikaP
  • 147
  • 9
Florian
  • 24,425
  • 4
  • 49
  • 80
  • 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 Jan 31 '18 at 15:58
  • @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! – Florian Jan 31 '18 at 16:20
  • 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...? – MonikaP Jan 31 '18 at 16:32
  • 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. – Florian Jan 31 '18 at 16:34
  • ha! Found the error. Your code was referring to window height not width – MonikaP Jan 31 '18 at 16:46
  • @MonikaP; oops! Sorry for that, that was quite sloppy. Feels weird to index with 0 in R though, haha. I am glad you were able to find that solution yourself :) – Florian Jan 31 '18 at 16:55
  • the solution with conditional panels (option 3) doesn't appear to work. UI renders as expected, but only changes of values to the panel defined as second ar recorded. – MonikaP Feb 01 '18 at 11:42
  • @MonikaP, I see.. That's strange though, adding the `console.log()` statement does show the function being called every time. I do not use `conditionalPanels` myself, I added a possible fourth solution that does not use renderUI, based on the shinyjs package. – Florian Feb 01 '18 at 11:59