13

I am making animation using animationOptions in sliderInput(). I want to use date/daterange in a slider, but sliderInput() does not accept date. There was a post in the shiny group. As of March, 2013, there was no solution yet. So I searched around and learned that one can achieve this using JavaScript. Here is the post. Unfortunately, I cannot write in JS. So, I searched around more and came up with this link and wrote the following codes. This works, but the numbers on the slider are confusing. Is this the only way one can move around and use date in sliderInput()? Thank you very much for taking your time.

server.R

library(shiny)

shinyServer(function(input, output, session) {


observe({
    updateDateInput(session, "ana", value = as.Date("2014-07-01") + input$foo)
})

output$preImage <- renderImage({


filename <- normalizePath(file.path('./www',
                          paste(input$ana, '.png', sep='')))

list(src = filename,
     alt = paste("There is no image for ", input$ana, ".", sep = ""))

}, deleteFile = FALSE)

})

ui.R

shinyUI(pageWithSidebar(

    headerPanel("renderImage example"),

sidebarPanel(

    sliderInput("foo", "Animation duration", min = -30,
                max = 30, value = 0, step = 1,
                animate = animationOptions(loop = TRUE, interval = 1000)),

    dateInput("ana", "Choose a date:", value = as.Date("2014-07-01"))

    ),

mainPanel(

# Use imageOutput to place the image on the page
imageOutput("preImage")

    )
))
Community
  • 1
  • 1
jazzurro
  • 23,179
  • 35
  • 66
  • 76

2 Answers2

21

I don't know when this feature was added, but now date values are automatically recognized in sliderInput(). And some related parameters are timeFormat and step (1-day by default)

sliderInput("date_range", 
"Choose Date Range:", 
min = as.Date("2016-02-01"), max = Sys.Date(), 
value = c(as.Date("2016-02-25"), Sys.Date())
)

Reference: http://shiny.rstudio.com/reference/shiny/latest/sliderInput.html

Chenguang Yang
  • 311
  • 2
  • 3
  • When I try your code I get: Error in `/.difftime`(n_steps, scale_factor) : second argument of / cannot be a "difftime" object – Art Mar 07 '16 at 19:17
  • 1
    this is an okay answer if you want to step by 1-day. it doesn't work that well if you want to step by month. – rrs Sep 29 '16 at 14:45
9

The sliderInput control offers some limited formatting options. You can add a format parameter to your sliderInput to change the displayed text for the slider.

Ideally, you would want the sliderInput to offer a custom mapping function that maps the current value to an exact date. But since that is not the option, the only wiggle room for improvement is to slightly change the text a little bit with the format parameter.

Accepted format can be found in this document.

Based on the above document, probably you can change your sliderInput into:

sliderInput("foo", "Animation duration", min = -30,
                max = 30, value = 0, step = 1,
                format="## Days",
                animate = animationOptions(loop = TRUE, interval = 1000)),

This will display as +10 Days, when originally it was "+10".

It may not be what you want exactly, but this is what you can do without writing Javascript.


EDIT: show customized slider value using Javascript

Say if you really want to change the current value label for the slider to indicate a somewhat sophisticated value (date, transformed values etc.), you can write some small Javascript snippet to do that.

One example provided by the jslider control (which is what Shiny uses for your sliderInput) indicates that by calling the constructor of a slider with a calculate function, you can manually change the mapping from the current value (numeric) to a custom string (in this case, a date).

Without being too verbose, let's just say that the slider's Javascript object is called slider. Which you can always get by calling:

$(select).slider()

where select is a jQuery selector. Again, in this case it is #foo because the slider has an id foo set in ui.R. When initiated, the slider.settings.calculate function appeared in the example will be bound to be the slider.nice function. So, we can simply override the nice function to achieve our goal.

Below is a modified ui.R with a piece of Javascript to do exactly the nice function overriding.

ui.R

shinyUI(pageWithSidebar(
  headerPanel("renderImage example"),
  sidebarPanel(
    sliderInput("foo", "Animation duration", min = -30,
                max = 30, value = 0, step = 1,
                animate = animationOptions(loop = TRUE, interval = 1000)),
    dateInput("ana", "Choose a date:", value = as.Date("2014-07-01"))
  ),

  mainPanel(
    singleton(tags$head(HTML(
      '
  <script type="text/javascript">
    $(document).ready(function() {
      var slider = $("#foo").slider();
      // override the default "nice" function.
      slider.nice = function(value) {
        var ref_date = new Date("2014-07-01");
        // each slider step is 1 day, translating to 24 * 3600 * 1000 milliseconds
        var slider_date = new Date(ref_date.getTime() + value * 24 * 3600 * 1000);
        return [slider_date.getUTCFullYear(), 
                slider_date.getUTCMonth() + 1, 
                slider_date.getUTCDate()].join("-");
      }
    })
  </script>
'))),
    # Use imageOutput to place the image on the page
    imageOutput("preImage")
  )
))

Another detail we might want to tweak is the label on both ends of the slider. To achieve this we could:

  1. replace the entire slider.domNode by calling slider.generateScales();
  2. directly modify the two <span>'s with class jslider-label.

For example if we take approach 2, we can modify the HTML() part of ui.R as:

    singleton(tags$head(HTML(
      '
  <script type="text/javascript">
    $(document).ready(function() {
      var slider = $("#foo").slider();
      // override the default "nice" function.
      var labels = slider.domNode.find(".jslider-label span");
      labels.eq(0).text("2014-06-01");
      labels.eq(1).text("2014-07-31");
      slider.nice = function(value) {
        var ref_date = new Date("2014-07-01");
        // each slider step is 1 day, translating to 24 * 3600 * 1000 milliseconds
        var slider_date = new Date(ref_date.getTime() + value * 24 * 3600 * 1000);
        return [slider_date.getUTCFullYear(), 
                slider_date.getUTCMonth() + 1, 
                slider_date.getUTCDate()].join("-");
      }
    })
  </script>
'))),

And the labels will display the dates as expected.

This is of course some quick hacking and may not work if some other customization is made to the slider.

I think somehow the Shiny team should consider adding a calculate option in sliderInput which directly maps to slider.settings.calculate, just to make things like this easier.

Xin Yin
  • 2,896
  • 21
  • 20
  • Hi Xin, thank you for your suggestion. I guess, learning JS will eventually allow me to have more flexibility. Would it be challenging to write a mapping function and add it in a shiny script? – jazzurro Aug 16 '14 at 00:57
  • In javascript? It should be relatively simple. I can edit the answer to show you tomorrow. – Xin Yin Aug 16 '14 at 01:10
  • Thank you very much for your help. I am sure there are some other shiny users who want to do something similar. Thank you for your support. – jazzurro Aug 16 '14 at 01:28
  • Hi @jazzurro, I've updated my answer to include a quick hack that changes the slider label. I would recommend you to start learning Javascript ASAP if you build web applications a lot with Shiny. You will encounter many limitations with the vanilla Shiny functions, most of which can be sidestepped or solved with some Javascript snippets. – Xin Yin Aug 16 '14 at 03:53
  • Hi again, thanks for the update. I believe some other shiny users who do not write in JS will appreciate your edit. Yep, after seeing this limitation, I decided to learn JS. I just ordered two books on JS. Once again, thank you for your support. – jazzurro Aug 16 '14 at 04:06