ramnathv / htmlwidgets

HTML Widgets for R
http://htmlwidgets.org
Other
792 stars 205 forks source link

using JS with global scope #260

Open yonicd opened 7 years ago

yonicd commented 7 years ago

I am trying to use customPaging in slickjs as part of building the functionality of slickR. when I use the JS function as something simple like cP1 there is no problem. But when I try to call a function that needs a variable on the global scope I get errors in the console log that seem to hint at a problem in the htmlwidget library

Uncaught ReferenceError: dotImages is not defined
    at c.eval (eval at window.HTMLWidgets.evaluateStringMember (http://localhost:21205/session/viewhtmld330247d204b/lib/htmlwidgets-0.8/htmlwidgets.js:732:23), <anonymous>:1:50)
    at c.b.buildDots (http://localhost:21205/session/viewhtmld330247d204b/lib/slick-1.6.0/slick/slick.min.js:17:7127)
    at c.b.buildOut (http://localhost:21205/session/viewhtmld330247d204b/lib/slick-1.6.0/slick/slick.min.js:17:8024)
    at c.b.init (http://localhost:21205/session/viewhtmld330247d204b/lib/slick-1.6.0/slick/slick.min.js:17:18675)
    at new c (http://localhost:21205/session/viewhtmld330247d204b/lib/slick-1.6.0/slick/slick.min.js:17:2931)
    at a.fn.init.a.fn.slick (http://localhost:21205/session/viewhtmld330247d204b/lib/slick-1.6.0/slick/slick.min.js:18:9395)
    at Object.renderValue (http://localhost:21205/session/viewhtmld330247d204b/lib/slickR-binding-0.0.0.9000/slickR.js:36:39)
    at Object.renderValue (http://localhost:21205/session/viewhtmld330247d204b/lib/htmlwidgets-0.8/htmlwidgets.js:819:25)
    at http://localhost:21205/session/viewhtmld330247d204b/lib/htmlwidgets-0.8/htmlwidgets.js:625:19
    at Array.forEach (native)

below is the script I am running, and here is an analog jsfiddle that works. and this is the slickR.js

Thanks for any pointers to what I am doing wrong with JS(.)

library(slickR)
library(rvest) 
library(reshape2)
library(dplyr)
library(htmlwidgets)

a=c("ATL","BOS","BKN","CHA","CHI","CLE","DAL","DEN","DET","GSW",
    "HOU","IND","LAC","LAL","MEM","MIA","MIL","MIN","NOP","NYK",
    "OKC","ORL","PHI","PHX","POR","SAC","SAS","TOR","UTA","WAS")
teamImg=sprintf("https://i.cdn.turner.com/nba/nba/.element/img/4.0/global/logos/512x512/bg.white/svg/%s.svg",a)

a1=read_html('http://www.espn.com/nba/depth')%>%html_nodes(css = '#my-teams-table a')
a2=a1%>%html_attr('href')
a3=a1%>%html_text()
team_table=read_html('http://www.espn.com/nba/depth')%>%html_table()
team_table=team_table[[1]][-c(1,2),]
playerTable=team_table%>%melt(,id='X1')%>%arrange(X1,variable)
playerName=a2[grepl('[0-9]',a2)]
playerId=do.call('rbind',lapply(strsplit(playerName,'[/]'),function(x) x[c(8,9)]))
playerId=playerId[playerId[,1]!='phi',]
playerTable$img=sprintf('http://a.espncdn.com/combiner/i?img=/i/headshots/nba/players/full/%s.png&w=350&h=254',playerId[,1])

cP1=JS("function(slick,index) {return '<a>'+(index+1)+'</a>';}")
cP2=JS("function(slick,index) {return '<a><img src= ' + dotImages[index] + '  width=100% height=100%></a>';}")

slickR(images = playerTable$img,
       dotImages = teamImg,
       slickOpts = list(initialSlide=0,
                        slidesToShow=5,
                        slidesToScroll=5,
                        focusOnSelect=T,
                        dots=T,
                        customPaging=cP2
       )
       )
ramnathv commented 7 years ago

Here is how I would do it. The problem with the earlier approach is that dotImages is trapped in the JSON payload of the widget and as a result is not available directly in the global scope. There is a way to access it, but a simpler approach would be to directly define it in the global scope.

s2 <- htmltools::tags$script(
  sprintf("var dotImages = %s", jsonlite::toJSON(teamImg))
)
s1 <- slickR(
  images = playerTable$img,
  dotImages = teamImg,
  slickOpts = list(
    initialSlide = 0,
    slidesToShow = 5,
    slidesToScroll = 5,
    focusOnSelect = T,
    dots = T,
    customPaging = cP2
  )
)
htmltools::browsable(htmltools::tagList(s2, s1))
yonicd commented 7 years ago

thanks! that works great for a once over solution. could i call those in the main slickR function call? I would guess that regular users (like me) wouldnt do that in a natural way. I think that I would need to write it in for them under the hood. could i take advantage of putting it in the 'shared variables for this instance'?

thanks again and sorry for the newbie questions

yonicd commented 7 years ago

this is the best I could come up with.

Is there a more efficient way?

thanks again

slickR <- function(images ,
                   slideId='baseDiv',
                   slideIdx=list(1:length(images)),
                   slickOpts=list(dots=T),
                   synchSlides=NULL,
                   dotImages=NULL,
                   width = NULL, 
                   height = NULL,
                   elementId = NULL) {

  if(!is.character(images)) break('images must be a character vector')

  images=lapply(images,function(x){
    if(!grepl('www|http|https|data:image/',x)) x=readImage(x)
    x
  })

  if(length(images)>1) images=unlist(images)

  if(length(slideId)!=length(slideIdx)) slideId=paste0('baseDiv',1:length(slideId))

  x = vector('list',length(slideIdx))

  for(xId in 1:length(x)){

    x[[xId]]$divName=slideId[xId]

    x[[xId]]$images=images[slideIdx[[xId]]]

    if(length(slickOpts)>0){
      if(all(sapply(slickOpts,class)=='list')){
        sOL=slickOpts[[xId]]
      }else{
        sOL=slickOpts
      } 

      if(!is.null(synchSlides)){
        sOL$asNavFor=sprintf(".%s",synchSlides[!(synchSlides%in%slideId[xId])])
      }

#<------------ Chunk to set dotImages in global js scope
      s2=NULL
      if(!is.null(dotImages)){
        s2 <- htmltools::tags$script(
                         sprintf("var dotImages = %s", jsonlite::toJSON(dotImages))
                    ) 
        x[[xId]]$dotImages=dotImages
      } 
 #<-----------------

      if(!is.null(sOL[[1]])) x[[xId]]$slickOpts=sOL
    }
  }

  # forward options using x

  # create widget
  s1=htmlwidgets::createWidget(
    name = 'slickR',
    x,
    width = width,
    height = height,
    package = 'slickR',
    elementId = elementId
  )

#<------- Ifelse to handle existence of dotImages output
  if(!is.null(s2)) {   
      htmltools::browsable(htmltools::tagList(s2, s1))
    }else{
     s1
  }

}
ramnathv commented 7 years ago

I think there is a better way to do this. I will try and post it over the weekend.