Home > Enterprise >  How can I update the displayed file name in a custom file input component in Shiny?
How can I update the displayed file name in a custom file input component in Shiny?

Time:06-10

Hope someone can help with this.

I'm building a Shiny application and I'm struggling with a custom file input component I made - it is nothing too fancy, I simply followed the tutorials on the Shiny website and used the default Bootstrap classes. I also added javascript, but since I'm an absolute beginner at this, maybe someone can explain what I'm doing wrong: basically the input works just fine, when clicked a file chooser opens and you're able to select what you want. However when I click ok, the displayed file name should change in the file name I selected, but instead it displays the placeholder.

The HTML for the input:

browse_only_fileinput <- function(inputId,
                                  label = NULL,
                                  placeholder = "No file selected",
                                  width = NULL
                                  ) {
  restoredValue <- shiny::restoreInput(id = inputId, default = NULL)

  if (!is.null(restoredValue) && !is.data.frame(restoredValue)) {
    warning("Restored value for ", inputId, " has incorrect format.")
    restoredValue <- NULL
  }

  if (!is.null(restoredValue)) {
    restoredValue <- jsonlite::toJSON(restoredValue, strict_atomic = FALSE)
  }

  inputTag <- tags$input(
    id = inputId,
    name = inputId,
    type = "file",
    `data-restore` = restoredValue,
    class = "form-control",
    placeholder = placeholder
  )

  labelInput <- if (!is.null(label)) {
    tags$label(label,
               class = "form-label browse-only-fi",
               id = paste0(inputId, "-label"),
               `for` = inputId)
  } else {
    NULL
  }

  tagList(
    singleton(
      tags$head(
        tags$script(src = "browse_only_fi.js")
      )
    ),
    div(
      class = "form-group shiny-input-container",
      style = htmltools::css(width = validateCssUnit(width)),
      labelInput,
      inputTag
    )
  )
}

And the javascript code:

$(document).ready(function() {
    var browseOnlyFileInput = new Shiny.InputBinding();
    $.extend(browseOnlyFileInput, {
      find: function(scope) {
        return $(scope).find(".browse-only-fi");
      },
      getValue: function(el) {
          var file = el.files[0];
          if (file) {
              read = new FileReader();
              read.readAsDataURL(file);
          }
          return read.result;
      },
      setValue: function(el, value) {
        $(el).val(value);
      },
      subscribe: function(el, callback) {
          $(el).on("change.browse-only-fi", function (e) {
              callback();
          });
      },
      unsubscribe: function(el) {
          $(el).off(".browse-only-fi");
      }
    });

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

enter image description here

Am I missing something? Thanks in advance

EDIT 1: I just realised that the place holder actually doesn't change even when specifying a different one. Is Bootstrap doing something under the hood?

CodePudding user response:

The text actually comes from the <input type = "file"> element and cannot be changed by JS or css means (Well you could hide the text, but not change it via css).

Just check the following plain HTML snippet and you will realize that the text next to the button appears there "magically".

<input type="file">

This gets even worse when you have a different language setting b/c this text is translated (cf this SO answer).

If you look carefully into the fileInput code, you will see that the shiny developers used a <input type = "text"> element for showing the text (I guess for the very same reason).

Bottom line: if you use <input type="file"> you cannot change the placeholder. If you want ot change it, you have to build the element with different tags (fileInput can be a good inspiration)

  • Related