I am trying to create simple webapp where I want to take in multiline input from user using HTML textarea control. Is there any out of the box way of creating such an input control in Shiny?
Help page of textInput doesn't show much options
textInput {shiny} R Documentation
Create a text input control
Description
Create an input control for entry of unstructured text values
Usage
textInput(inputId, label, value = "")
Arguments
inputId
Input variable to assign the control's value to
label
Display label for the control
value
Initial value
Value
A text input control that can be added to a UI definition.
Examples
textInput("caption", "Caption:", "Data Summary")
You can add a textarea using tags and it should be picked up by Shiny automatically:
tags$textarea(id="foo", rows=3, cols=40, "Default value")
Or if you're more comfortable with straight HTML you can also do
HTML('<textarea id="foo" rows="3" cols="40">Default value</textarea>')
In either case, input$foo should reflect the textarea's value.
For benefit of others, I will post how I solved the problem using custom UI control following Shiny tutorial
Firstly, I crearted textarea.js file as follows
$(document).on("click", "textarea.inputTextarea", function(evt) {
// evt.target is the button that was clicked
var el = $(evt.target);
// Raise an event to signal that the value changed
el.trigger("change");
});
var inputTextareaBinding = new Shiny.InputBinding();
$.extend(inputTextareaBinding, {
find: function(scope) {
return $(scope).find(".inputTextarea");
},
getValue: function(el) {
return $(el).text();
},
setValue: function(el, value) {
$(el).text(value);
},
subscribe: function(el, callback) {
$(el).on("change.inputTextareaBinding", function(e) {
callback();
});
},
unsubscribe: function(el) {
$(el).off(".inputTextareaBinding");
}
});
Shiny.inputBindings.register(inputTextareaBinding);
Then I added following function in ui.R of shiny webapp before shinyUI() is called
inputTextarea <- function(inputId, value="", nrows, ncols) {
tagList(
singleton(tags$head(tags$script(src = "textarea.js"))),
tags$textarea(id = inputId,
class = "inputtextarea",
rows = nrows,
cols = ncols,
as.character(value))
)
}
Then I used above defined function to create the desired textarea control element in ui.R
shinyUI(pageWithSidebar(
# Application title
headerPanel("Test Header Panel"),
sidebarPanel(),
mainPanel(
inputTextarea('exampleTextarea', '',20,35 )
)
))
May or may not be relevant here, but I made the shinyAce package to wrap up and expose the Ace text editor in Shiny. Ace is primarily used for code editing (complete with syntax highlighting for a variety of languages), but provides a text-area-like interface for writing composing multi-line text/code.
You can check out the example to see if that might be what you're looking for. (Try different "modes" for syntax highlighting and themes for color combinations.)
From version 0.14 shiny has an implementation of textAreaInput.
Building off of Joe's answer (https://stackoverflow.com/a/14452837/5776618), you can also nest tags into your own function to achieve the same output as the standard Shiny built-in input functions.
textareaInput <- function(id, label, value, rows=20, cols=35, class="form-control"){
tags$div(
class="form-group shiny-input-container",
tags$label('for'=id,label),
tags$textarea(id=id,class=class,rows=rows,cols=cols,value))
}
This is a way to avoid writing the JS code (if you want to) while still...
having a function that calls the same way the built-in Shiny inputs are called, and
includes the div, label, and Bootstrap's form-control CSS style (so that it looks like the built-in Shiny input controls)
Using the function is same as if you are using the built-in or if you built a custom UI.
textareaInput("textareaID","Text Area Label", "Insert text here...", rows = 20, cols = 35)
Here's a quick solution that preserves the shiny input feel, but allows custom number of columns:
textareaInput <- function(inputID, label, value="", rows=10, columns=80) {
HTML(paste0('<div class="form-group shiny-input-container">
<label for="', inputID, '">', label,'</label>
<textarea id="', inputID, '" rows="', rows,'" cols="',
columns,'">', value, '</textarea></div>'))
}
In your ui.R script, you can add:
textareaInput("shazam", "My text box")
Note: To get a Bootstrap feel to the textarea, you can use:
textareaInput <- function(inputID, label, value="", rows=10) {
HTML(paste0('<div class="form-group shiny-input-container">
<label for="', inputID, '">', label,'</label>
<textarea class="form-control" id="', inputID,
'" rows="', rows, '">', value, '</textarea></div>'))
}
Related
I am trying to set the selected values of the f7SmartSelect from the package shinyMobile to NULL (aka no values selected) after user clicks on an actionButton.
I have found a few solutions on this, but they all adress selectizeInput and unfortunately I cant figure out how to properly implement those javascript- Functions(?) for SmartSelect.
So here is a MRE:
library(shiny)
library(shinyMobile)
shinyApp(
ui = f7Page(
f7SingleLayout(
navbar = f7Navbar(title = " "),
f7Button("resetSmartSelect", "Reset Smart Select"),
f7SmartSelect(
inputId = "smartId",
multiple = TRUE,
label = "Choose a variable:",
choices = colnames(mtcars)[-1],
openIn = "popup"
)
)
),
server = function(input, output, session) {
observeEvent(input$resetSmartSelect, {
#code to reset the values of 'smartId' aka the smartSelect and also uncheck the boxes
#I have tried shinyjs::reset('smartId'), but it did nothing
})
}
)
Thanks in advance!
EDIT:
I found the following javascript parts which should somehow solve my problem, but I cant figoure out how because I have no knowledge in js..
function(){
var select = this.$input[0];
$('#resetSmartSelect').on('click', function(){
select.selectize.setValue([]);
});
}
maybe it should be smartSelect.setValue([]) instead but dont know how to implement it correctly
found a solution for this. If someone in the future faces a similar problem, just put this on top of your UI:
tags$head(
tags$script(HTML('
$(document).ready(function() {
$("#idOfTheResetButton").on("click", function() {
var smartSelect = app.smartSelect.get("#idOfTheSmartSelect");
for(let i = 0; i < smartSelect.$selectEl[0].length; i++) {
smartSelect.$selectEl[0][i].selected = false;
}
smartSelect.setValue([]);
});})
'
)))
probably not the best solution I guess. I just tried a few things out and this one works :)
In R Shiny I am trying to dynamically set a download button's label using reactive renderText and textoutput.
It works as expected but the label is always shown in the new line, and hence the button looks wacky next to a regular button
as shown here
Backend logic is -
In server.R, an input field's value is used to generate conditional labels
output$mycustomlabel <- renderText({ if(input$inputtype=="One") return("Download label 1") else return("Download label 2")})
Then in UI.R, that label is used as
downloadButton("download.button.test", textOutput("mycustomlabel"))
Can someone guide why does it display text on new line, and how can I keep it on same line?
If you want to change the button label you probably need to update it with javascript.
An easier approach could be to have two different buttons and use conditional panels to display one of the buttons:
ui <- fluidPage(
radioButtons('inputtype', 'Set label', c('One', 'Two')),
conditionalPanel(
'input.inputtype == "One"',
downloadButton('btn1', 'Download label 1')
),
conditionalPanel(
'input.inputtype == "Two"',
downloadButton('btn2', 'Download label 2')
)
)
Note that with this approach you do need two observers in the server function.
I'm doing this same thing with a dynamic label on a downloadButton. In my case, I want the user to choose between downloading a dataframe as an Excel file or a CSV file.
Here's what I'm doing:
In the ui definition, where you want the button to show up, use
uiOutput( 'myCustomButtonUI' )
In the server definition, include:
output$myCustomButtonUI <- renderUI({
myCustomLabel <- 'Placeholder'
if( input$inputtype == 'One' ) myCustomLabel <- 'Download Label 1'
if( input$inputtype == 'Two' ) myCustomLabel <- 'Download Label 2'
downloadButton( inputId = 'download.button.test',
label = myCustomLabel )
})
output$download.button.text <- downloadHandler(
filename = "<some filename>",
content = .... <go look up downloadHandler() if you're unfamiliar> ..."
)
The idea is that, because you want your button to be dynamic, it needs to be rendered on the server side. The output of the server side is a tiny piece of UI that is placed in your larger UI by the uiOutput function.
I want to remove the check all/none checkbox from a Reactable table.
In this image, I do not want the orange circled checkbox to appear.
Using Chrome Inspector, I examine the css of this checkbox and set display: none;
This removes the entire column of checkboxes. How do I remove just this one?
R Script
library(reactable)
reactable(iris,
onClick = "select",
selection = "multiple")
U can append some javascript code and make it run when the reactable is rendered:
ie
// Hide the select all check box
document.querySelector('.rt-select-input[aria-label="Select all rows"]').parentElement.parentElement.style.display = "none";
The final R-code
library(reactable)
library(htmlwidgets)
e<-reactable(iris,
onClick = "select",
selection = "multiple")
javascript <- JS('
document.querySelector(\'.rt-select-input[aria-label="Select all rows"]\').parentElement.parentElement.style.display="none";
')
(p <- prependContent(e,onStaticRenderComplete(javascript)))
Improvements
In order to streamline the process and specifically target the wanted checkbox (as the aforementioned method would be unsuccessful when handling 2 tables in the same page) I wrote a function that'll dynamically target the wanted checkbox:
hide.select.all <- function(x){
javascript <- JS(paste0('
let id = null;
for (const script of document.querySelectorAll("script[data-for]")) {
if(script.text.includes("', x$x$tag$attribs$dataKey ,'")) {
id="#" + script.dataset.for;
break;
}
}
if(id) document.querySelector(id + \' .rt-select-input[aria-label="Select all rows"]\').parentElement.parentElement.style.display="none";
'))
prependContent(x,onStaticRenderComplete(javascript))
}
hide.select.all(e)
I create a shiny app and I need to add a language option. I would like add a selectizeInput like :
selectizeInput("language",
"Select language:",
choices = c("English"="en","Français"="fr"),
selected = app_default_languague, multiple=FALSE, width = '70%'
)
but with flags in front of each country. Flags logo are in "www/" directory. Do you know how i can make this please ?
There is an example in Rstudio gallery but i don't understand it ....
http://shiny.rstudio.com/gallery/selectize-rendering-methods.html
selectizeInput is based on the Javascript library selectize.js which has powerful settings including templates to render your own options and the selected item.
In order to do that you have to use a bit of Javascript code. This code is stored in rendersjsItem. That is a Javascript function that generates and returns a piece of HTML like this:
<div class="option">
<img width="25" height="25" class="flag" src="fr.svg" />
French
</div>
This assumes that your files are located in the www folder with the country code as name, e.g. "fr.svg". The example below specifies names and country codes in the vector countries.
All you need to do is take rendersjsItem and use it as render option for the selectizeInput. If you want to change the styling you can modify the CSS of the flag class.
Full Solution
library(shiny)
countries <- c(German = "de", Czech = "cz", French = "fr")
app_default_languague <- "cz"
# Assuming the flag icons are directly in the 'www' folder with names "de.svg", "cz.svg", etc.
rendersjsItem <-
I("{
option: function(item, escape) {
return '<div class=\"option\"><img width=\"25\" height=\"25\" class=\"flag\"' +
'src=\"' + item.value + '.svg\" />' +
item.label + '</div>';
}
}")
ui <- fluidPage(
tags$head(
# Add CSS to format the language selection options with flag + text
tags$style(HTML(".flag {height:24px; width:24px; margin-right: 12px}"))
),
title = "Selectize Test",
selectizeInput("language", "Select language",
choices = countries,
selected = app_default_languague, multiple=FALSE, width = "70%",
options = list(
placeholder = "Type a language name, e.g. German",
render = rendersjsItem
)),
textOutput("SelectedLanguage")
)
server <- function(input, output, session) {
output$SelectedLanguage <- renderText(input$language)
}
shinyApp(ui, server)
Side Note
The World Wide Web Consortium (W3C) recommends that flag icons should not be used to indicate languages. There are multiple reason. Instead, use the name of the language in it's target language, i.e. do not write "German" but "Deutsch". Examples: Deutsch, English, Español, Français, Italiano, ...
See also ux.stackexchange.
Import them using jpeg library and and create a vector
From the shiny dashboard github, I've gathered that it's possible to create drop down menus at the top right of the header, but there are only 3 "types" (messages, notifications, and tasks).
https://rstudio.github.io/shinydashboard/structure.html#structure-overview
Is there a method for creating a custom dropdown? I'd like to make a settings dropdown, where I give the user some checkboxes that they can use to adjust the dashboard in ways (displaying/hiding things, filtering data, etc.)
I customized one of the three types of menu to allow this. You could then add actionItem(s) for items. tabSelect property when true simulate the selection of a sidebarMenuItem.
dropdownActionMenu <- function (..., title=NULL, icon = NULL, .list = NULL, header=NULL) {
items <- c(list(...), .list)
lapply(items, shinydashboard:::tagAssert, type = "li")
type <- "notifications" # TODO créer action + CSS
dropdownClass <- paste0("dropdown ", type, "-menu")
tags$li(class = dropdownClass, a(href = "#", class = "dropdown-toggle",
`data-toggle` = "dropdown", icon, title), tags$ul(class = "dropdown-menu",
if(!is.null(header)) tags$li(class="header",header),
tags$li(tags$ul(class = "menu", items))))
}
actionItem = function (inputId, text, icon = NULL, tabSelect=FALSE) {
if(!is.null(icon)) {
shinydashboard:::tagAssert(icon, type = "i")
icon <- tagAppendAttributes(icon, class = paste0("text-", "success"))
}
if(tabSelect) {
tags$li(a(onclick=paste0("shinyjs.tabSelect('",inputId,"')"),icon,text))
} else {
tags$li(actionLink(inputId,text,icon))
}
}
javascript function to select tab (to be inserted after useShinyjs() in body)
extendShinyjs(text="shinyjs.tabSelect=function(tabName){$('a[data-value='+tabName+']').click();}")
Sample code
dashboardHeader(
dropdownActionMenu(title="test",
actionItem("mnuFirst","First"),
actionItem("mnuSecond","Second")
)
)
Shiny Dashboard is based on admin LTE. So the existing type of drop downs are designed for admin LTE use case, which is quite different from many Shiny app usage.
If something is not even available in admin LTE, it's less likely to be supported in Shiny dashboard.
For your specific question, you can put some controls in the side bar. Another possibility is to use the wrench icon in box, which is not implemented in Shiny yet.