ericrayanderson/shinymaterial

initShinyMaterialDropdown.getValue : getValue, error if ans equal null

Closed this issue · 4 comments

Where

In the package, file shiny-material-dropdown.js, function initShinyMaterialDropdown.getValue.

Problem

If ans = null, then typeof(null) == 'object' return true and ans.length create an error.

Code

getValue: function (el) {
              var ans;
              ans = $(el).val();
              if(typeof(ans) == "string"){
                return ans.replace(new RegExp("_shinymaterialdropdownspace_", 'g'), " ");
              } else if(typeof(ans) == "object"){
                for (i = 0; i < ans.length; i++) { 
                  if(typeof(ans[i]) == "string"){
                  ans[i] = ans[i].replace(new RegExp("_shinymaterialdropdownspace_", 'g'), " ");
                  }
              }
              return ans;
              } else {
                return ans;
              }
            }

Solution

test first if ans !== null

The javascript null come from the option multiple = T without the selected option in the material_dropdown R function and genrate a javascript error.
It cause shiny server never lauch.

Works

library(shiny)
library(shinymaterial)

ui <- material_page(
  title = "Error",
  material_dropdown(input_id = 'test', label = 'some test', choices = 1:10, multiple = F),
  plotOutput("myPlot")
)

server <- function(input, output) {
  output$myPlot <- renderPlot({
    print(input$test)
    plot(1:max(input$test))
  })
}

shinyApp(ui = ui, server = server)

Don't works

library(shiny)
library(shinymaterial)

ui <- material_page(
  title = "Error",
  material_dropdown(input_id = 'test', label = 'some test', choices = 1:10, multiple = T),
  plotOutput("myPlot")
)

server <- function(input, output) {
  output$myPlot <- renderPlot({
    print(input$test)
    plot(1:max(input$test))
  })
}

shinyApp(ui = ui, server = server)

Thank you for finding this issue and also digging into the code to find the solution. I will implement this, unless you want to submit a PR, that works as well.

I let you do the job; it's a minor modification and maybe others functions are concern.

For this issue, just add in getValue :

if(ans === null){return(ans);}

Result

$(document).ready(function () {
    function initShinyMaterialDropdown(callback) {
        $('.shiny-material-dropdown').material_select();
        callback();
    }

    initShinyMaterialDropdown(function () {

        var shinyMaterialDropdown = new Shiny.InputBinding();
        $.extend(shinyMaterialDropdown, {
            find: function (scope) {
                return $(scope).find("select.shiny-material-dropdown");
            },
            getValue: function (el) {
              var ans;
              ans = $(el).val();
              if(ans === null){return(ans);}
              if(typeof(ans) == "string"){
                return ans.replace(new RegExp("_shinymaterialdropdownspace_", 'g'), " ");
              } else if(typeof(ans) == "object"){
                for (i = 0; i < ans.length; i++) { 
                  if(typeof(ans[i]) == "string"){
                  ans[i] = ans[i].replace(new RegExp("_shinymaterialdropdownspace_", 'g'), " ");
                  }
              }
              return ans;
              } else {
                return ans;
              }
            },
            subscribe: function (el, callback) {
                $(el).on("change.shiny-material-dropdown", function (e) {
                    callback();
                });
            },
            unsubscribe: function (el) {
                $(el).off(".shiny-material-dropdown");
            }
        });

        Shiny.inputBindings.register(shinyMaterialDropdown);
    });
})

Enhancement

Also each time you return ans so maybe you can simplify your function like this :

$(document).ready(function () {
    function initShinyMaterialDropdown(callback) {
        $('.shiny-material-dropdown').material_select();
        callback();
    }

    initShinyMaterialDropdown(function () {

        var shinyMaterialDropdown = new Shiny.InputBinding();
        $.extend(shinyMaterialDropdown, {
            find: function (scope) {
                return $(scope).find("select.shiny-material-dropdown");
            },
            getValue: function (el) {
              var ans;
              ans = $(el).val();
              if(ans === null){
                // Do nothing
              } else if(typeof(ans) == "string"){
                ans = ans.replace(new RegExp("_shinymaterialdropdownspace_", 'g'), " ");
              } else if(typeof(ans) == "object"){
                for (i = 0; i < ans.length; i++){ 
                  if(typeof(ans[i]) == "string"){
                    ans[i] = ans[i].replace(new RegExp("_shinymaterialdropdownspace_", 'g'), " ");
                  }
                }
              } 
            return ans;
            },
            subscribe: function (el, callback) {
                $(el).on("change.shiny-material-dropdown", function (e) {
                    callback();
                });
            },
            unsubscribe: function (el) {
                $(el).off(".shiny-material-dropdown");
            }
        });

        Shiny.inputBindings.register(shinyMaterialDropdown);
    });
})

The issue has been fixed in the dev version.

devtools::install_github("ericrayanderson/shinymaterial")