Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using htmlwidgets::scaffoldWidget to incorporate external js libraries for a new package to go into a shiny app

I have been impressed by the slick javascript library (http://kenwheeler.github.io/slick/) and want to incorporate it into my shiny apps/flexboard pages.

I would like to use the htmlwidgets package in R incorporate the slick js library, so have started by trying to create a package as is suggested in the online documentation (http://www.htmlwidgets.org/develop_intro.html), by carrying out the following...

devtools::create("slick")              
setwd("slick")                          
htmlwidgets::scaffoldWidget("slick")

I downloaded the js library from https://github.com/kenwheeler/slick/archive/1.6.0.zip

and placed it into the structure of the package so that I have a file structure that looks a bit like this.

R/
| slick.R

inst/
|-- htmlwidgets/
|   |-- slick.js
|   |-- slick.yaml
|   |-- lib/
|   |   |-- slick-1.6.0/
|   |   |   |-- slick/
|   |   |   |   |-- slick.min.js 
|   |   |   |   |-- slick.js
|   |   |   |   |-- slick.css
|   |   |   |   |-- slick-theme.css

My slick.yaml file looks like this...

dependencies:
  - name: slick
    version: 1.6.0
    src: htmlwidgets/lib/slick-1.6.0
    script:
        - slick/slick.min.js
        - slick/slick.js
    stylesheet: 
        - slick/slick.css
        - slick/slick-theme.css

But am really stuck as to how to adjust the inst/htmlwidget/slick.js file and the R/slick.R file in a way that can take a vector of urls and display them in a shiny app. The reason for this, is that it does not seem to match a similar input data concept as the example provided.

For reproducibility and to use the same URLs that are provided in the examples in the package, I am providing a vector of placeholder img urls that I would like to use as the contents. For each image in the carousel.

image_vec <- paste0("http://placehold.it/350x300?text=",seq(1:9))

Perhaps I might need to use something like this?...

lapply(image_vec,function(y){div(img(src=y))})

As always any help on this would be much appreciated.

EDIT

My new slick.yaml file looks like the following...after @NicE's answer post...am I missing something?

dependencies:
  - name: jquery
    version: 3.1.0
    src: htmlwidgets/lib
    script:
      - jquery-3.1.0.min.js
  - name: slick
    version: 1.6.0
    src: htmlwidgets/lib/slick-1.6.0
    script:
        - slick/slick.min.js
        - slick/slick.js
    stylesheet: 
        - slick/slick.css
        - slick/slick-theme.css

and now my file structure looks like the following:

R/
| slick.R

inst/
|-- htmlwidgets/
|   |-- slick.js
|   |-- slick.yaml
|   |-- lib/
|   |   |-- jquery-3.1.0.min.js
|   |   |-- slick-1.6.0/
|   |   |   |-- slick/
|   |   |   |   |-- slick.min.js 
|   |   |   |   |-- slick.js
|   |   |   |   |-- slick.css
|   |   |   |   |-- slick-theme.css

and my /inst/htmlwidgets/slick.js looks like the following

HTMLWidgets.widget({

  name: 'slick',

  type: 'output',

  factory: function(el, width, height) {

    // TODO: define shared variables for this instance
    // create new slick object witht the given id?
    var sl = new slick(el.id);



    return {

          renderValue: function(x) {
                    //add class to the div and center it
                    el.setAttribute("class",x.class);
                    el.style.margin = "auto";

                    //add images to the div
                    content='';    
                    for(var image in x.message)
                    {
                      content += '<div><img src="' + x.message[image] + '"/></div>';
                    }
                    el.innerHTML = content;

                    //initialize the slider.
                    $(document).ready(function(){
                      $("."+x.class).slick(x.options);      
                    });

      },

      resize: function(width, height) {

        // TODO: code to re-render the widget with a new size

      }

    };
  }
});
like image 491
h.l.m Avatar asked Jul 25 '16 09:07

h.l.m


1 Answers

Here's an attempt, using htmlwidgets_0.6:

For the dependencies, the yaml file looks the same, I just added jQuery above slick:

dependencies:
  - name: jquery
    version: 3.1.0
    src: htmlwidgets/lib
    script:
      - jquery-3.1.0.min.js
  - name: slick ...

You can get it here and put it in the lib folder.

In the slick.R file, you need to change the arguments of the slick function to add options and change x to forward all the arguments to the JS code:

slick <- function(message, class="slick_slider", options = list(), width = NULL, height = NULL) {

  # forward options using x
  x = list(
    message = message,
    class = class,
    options = options
  )
...

In the slick.js, you mainly need to change the renderValue to add the images/content to the div and display the carousel:

renderValue: function(x) {
          //add class to the div and center it
          el.setAttribute("class",x.class)
          el.style.margin = "auto";

          //add images to the div
          content='';    
          for(var image in x.message)
          {
            content += '<div><img src="' + x.message[image] + '"/></div>';
          }
          el.innerHTML = content;

          //initialize the slider.
          $(document).ready(function(){
            $("."+x.class).slick(x.options);      
          });

        }

Once you've installed it using devtools::install(), you can use it in a shiny app:

library(shiny)
library(htmlwidgets)
library(slick)

server <- function(input, output) {
  output$test_slick <- renderSlick({    
    slick(paste0("http://placehold.it/350x300?text=",1:9),
          options=list(dots=TRUE,autoplay=TRUE))
})
}

ui <- fluidPage(
  tags$style(HTML("body {background-color: #2682d5}")),
  slickOutput('test_slick',width="350px",height="300px")
)

shinyApp(ui = ui, server = server)
like image 121
NicE Avatar answered Oct 16 '22 03:10

NicE