---
title: "shinyFeedback Introduction"
author: "Andy Merlino"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{shinyFeedback Introduction}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

## Functionality

The `shinyFeedback` package displays user friendly messages that appear alongside `shiny` inputs.  e.g.

![](feedback_screenshot.png)

`shinyFeedback` currently works with the following `shiny` and `shinyWidgets` inputs:

- `shiny::dateInput()`
- `shiny::dateRangeInput()`
- `shiny::fileInput()`
- `shiny::numericInput()`
- `shiny::passwordInput()`
- `shiny::selectInput()`
- `shiny::sliderInput()`
- `shiny::textAreaInput()`
- `shiny::textInput()`
- `shinyWidgets::airDatepickerInput()`
- `shinyWidgets::pickerInput()`


## Setup

In order to use `shinyFeedback` you need to include the `useShinyFeedback()` function at the top of your UI.

The following is a minimal example of a `shiny` app that uses `shinyFeedback`.  Run the following code in your R console to run the app.

```{r, eval=FALSE}
library(shiny)
library(shinyFeedback)

ui <- fluidPage(
  useShinyFeedback(), # include shinyFeedback
  
  textInput(
    "myInput",
    "Warn if >3 characters",
    value = ""
  )
)

server <- function(input, output, session) {
  observeEvent(input$myInput, {
    
    if (nchar(input$myInput) > 3) {
      showFeedbackWarning(
        inputId = "myInput",
        text = "too many chars"
      )  
    } else {
      hideFeedback("myInput")
    }
    
  })
}

shinyApp(ui, server)
```

The above app has one `textInput()` input in the UI.  In the server function, we write the code to conditionally display a feedback message. If the text input has more than 3 characters, the feedback message is displayed.

# `feedback()`

The `feedback()` function is an alternative to using `showFeedback()` and `hideFeedback()`.  With `feedback()` the feedback message is shown/hidden based on whether the `show` argument to `feedback()` is "truthy".  "truthiness" is determined by `shiny::isTruthy()`.  `feedback()` works nicely with reactive expressions that use `shiny::req()` to check the validity of Shiny inputs.  e.g.

```{r, eval=FALSE}
library(shiny)
library(shinyFeedback)

ui <- fluidPage(
  useShinyFeedback(), # include shinyFeedback
  
  selectInput(
    "dataset",
    "Dataset",
    choices = c(
      "airquality",
      "Unknown dataset"
    )
  ),
  
  tableOutput('data_table')
)

server <- function(input, output, session) {
  
  data_out <- reactive({
    req(input$dataset)
    
    dataset_exists <- exists(input$dataset, "package:datasets")
    feedbackWarning("dataset", !dataset_exists, "Unknown dataset")
    req(dataset_exists, cancelOutput = TRUE)

    get(input$dataset, "package:datasets")
  })
  
  output$data_table <- renderTable({
    head(data_out())
  })
}

shinyApp(ui, server)
```


## Usage Inside Shiny Modules

`shinyFeedback` works inside [shiny modules](https://shiny.rstudio.com/articles/modules.html).

As for all modules, input and output IDs in the module UI code must be wrapped in `shiny::ns()` calls.
Inside your module server code, you need *not* wrap input or output IDs inside `shiny::ns()`; shinyFeedback will automatically prepend the namespace of your current module.

Here is a simple example using shinyFeedback inside a module:

```{r, eval=FALSE}
library(shiny)
library(shinyFeedback)

numberInput <- function(id) {
  ns <- NS(id)
  tagList(
    useShinyFeedback(),
    numericInput(
      ns("warningInput"),
      "Warn if Negative",
      value = 0
    )
  )
}

number <- function(input, output, session) {
  observeEvent(input$warningInput, {
    req(input$warningInput)
    if (input$warningInput < 0) {
      showFeedbackWarning(inputId = "warningInput")
    } else {
      hideFeedback("warningInput")
    }
  })
}

ui <- fluidPage(
  numberInput(id = "numberFoo")
)

server <- function(input, output) {
  callModule(module = number, id = "numberFoo")
}

shinyApp(ui, server)
```
