Consider the following shiny app:
library('shiny')
# User Interface/UI
ui <- fluidPage(
titlePanel(
'Slider and Text input update'
), # titlePanel
mainPanel(
# Slider input
sliderInput(
inputId = 'sliderValue',
label = 'Slider value',
min = 0,
max = 1000,
value = 500
), # sliderInput
# Text input
textInput(
inputId = 'textValue',
label = NULL
) # textInput
) # mainPanel
) # fluidPage
# Server logic
server <- function(input, output, session) {
observe({
# Update vertical depth text box with value of slider
updateTextInput(
session = session,
inputId = 'textValue',
value = input$sliderValue
) # updateTextInput
# updateSliderInput(
# session = session,
# inputId = 'sliderValue',
# value = input$textValue
# ) # updateSliderInput
}) # observe
}
# Run the application
shinyApp(ui = ui, server = server)
It allows the user to change the values of a slider (sliderInput), which updates the text in the text box (textInput):
I want these to work in sync. So, instead of just the above slider > text box interaction, I want the opposite as well: text box > slider.
If you uncomment the updateSliderInput component, the two widgets compete against one another; an update of the one leads to an update of the other which leads to an update of the other, ...
How can this be avoided while still making the two be in sync?
One way to do it would be using observeEvent for each input and adding a condition if(as.numeric(input$textValue) != input$sliderValue). This will help you from the inputs calling each others update functions recursively. Then your app would look something like this:
library('shiny')
# User Interface/UI
ui <- fluidPage(
titlePanel(
'Slider and Text input update'
), # titlePanel
mainPanel(
# Slider input
sliderInput(
inputId = 'sliderValue',
label = 'Slider value',
min = 0,
max = 1000,
value = 500
), # sliderInput
# Text input
textInput(
inputId = 'textValue',
value = 500,
label = NULL
) # textInput
) # mainPanel
) # fluidPage
# Server logic
server <- function(input, output, session)
{
observeEvent(input$textValue,{
if(as.numeric(input$textValue) != input$sliderValue)
{
updateSliderInput(
session = session,
inputId = 'sliderValue',
value = input$textValue
) # updateSliderInput
}#if
})
observeEvent(input$sliderValue,{
if(as.numeric(input$textValue) != input$sliderValue)
{
updateTextInput(
session = session,
inputId = 'textValue',
value = input$sliderValue
) # updateTextInput
}#if
})
}
# Run the application
shinyApp(ui = ui, server = server)
The above code can be modified a bit to fix the issue of application getting closed when the input in the test box is empty
library('shiny')
ui <- fluidPage(titlePanel('Slider and Text input update'),
mainPanel(
sliderInput(
inputId = 'sliderValue',
label = 'Slider value',
min = 0,
max = 1000,
value = 500
),
textInput(
inputId = 'textValue',
value = 500,
label = NULL
)
))
# Server logic
server <- function(input, output, session)
{
observeEvent(input$textValue, {
print(input$textValue)
if ((as.numeric(input$textValue) != input$sliderValue) &
input$textValue != "" & input$sliderValue != "")
{
updateSliderInput(
session = session,
inputId = 'sliderValue',
value = input$textValue
)
} else {
if (input$textValue == "") {
updateSliderInput(session = session,
inputId = 'sliderValue',
value = 0)
}
}
})
observeEvent(input$sliderValue, {
if ((as.numeric(input$textValue) != input$sliderValue) &
input$sliderValue != "" & input$textValue != "")
{
updateTextInput(
session = session,
inputId = 'textValue',
value = input$sliderValue
)
}
})
}
# Run the application
shinyApp(ui = ui, server = server)
I am a little late to this discussion, but I have recently been having similar trouble. Only I was wanting to sync numeric inputs with slider values... Using the latest example posted here, I still had issues with infinite loops. I think that I finally found a solution to the infinite loops. Following on from Tinku's answer, I add a time delay in the part of the code where the numeric input is updated by the slider value. I believe that the slider value updates slightly slower than the numeric input, so if changing the numeric input too fast, the lag can become too much and looping occurs. I add the code below…. I set the lag to 0.3 s, which is good enough to avoid the infinite loops on my computer….
library('shiny')
library('shinyvalidate')
ui <- fluidPage(titlePanel('Slider and numeric input'),
mainPanel(
sliderInput(
inputId = 'sliderValue',
label = 'Slider value',
min = 0,
max = 1000,
value = 500,
step = 5,
),
numericInput(
inputId = 'numericValue',
min = 0,
max = 1000,
value = 500,
step = 5,
label = "Numeric value"
),
actionButton("Set", "Apply"),
textOutput("value") #Paste the 'Set' value
))
# Server logic
server <- function(input, output, session)
{
v <- reactiveValues()
#Register the current time
v$now = Sys.time()
v$when = Sys.time()
#The saved numeric value to use in further functions/plots
v$num = 500 # Initial value
observeEvent(input$numericValue, {
v$when = Sys.time()
req(input$numericValue)
if (input$numericValue != input$sliderValue)
{
updateSliderInput(
session = session,
inputId = 'sliderValue',
value = input$numericValue
)
}
})
observeEvent(input$sliderValue, {
v$now = Sys.time()
req(input$numericValue)
if (input$numericValue != input$sliderValue & v$now - v$when > 0.3) #I found 0.3 s a good lag to give (I believe that the slidervalue updates ~0.25 s slower than numericinput)
{
updateNumericInput(
session = session,
inputId = 'numericValue',
value = input$sliderValue
)
}
})
#Only update the reactive value (v$num) if within the specified numeric range....
isolate(
observeEvent(input$Set, {
i <- InputValidator$new()
i$add_rule("numericValue", sv_required(message = "Number must be provided"))
i$add_rule("numericValue", sv_gte(0))
i$add_rule("numericValue", sv_lte(1000))
i$enable()
req(i$is_valid())
v$num <- input$numericValue #Use this reactive value in further functions/plots.....
output$value <- renderText({paste("The syncronised value is:", v$num)})
})
)
}
# Run the application
shinyApp(ui = ui, server = server)
Related
I am trying to build a shiny app in which users can provide a (numeric) parameter in different units. For this, I would like to have an input field to fill in the numeric value (e.g. like the one that the numericInput function provides) and two buttons (e.g. the buttons that the radioButtons function provides) in the same line, in which the user can specify which unit is used. It would look something like this example:
Ideally, the values of the min and max arguments in the numericInput depend on which unit is specified by the user. For example, the parameter creatinine can be provided in mg/dL and in umol/L where the conversion factor is 1 mg/dL = 88.42 umol/L and the allowed range would be 0.1 to 15 in mg/dL and 8.842 to 1326.3 in umol/L.
In addition to the dependency of min and max on the unit specified by the user, I would like to be able to make calculations with the numeric value (creat in the example below) that - depending on the unit specified by the user - takes the conversion factor into account (e.g. with an if else construction in the server function).
Is this possible in shiny and if yes how would you do it in the below example?
library(shiny)
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
numericInput(inputId="creat",label="Creatinine",value="",min=0,max=15,)),
mainPanel(p('Some calculation with creatinine'),
textOutput("creat"))
)
))
server <- function(input, output) {
output$creat <- renderText({input$creat*100})
}
shinyApp(ui = ui, server = server)
Many thanks in advance!
Perhaps this will meet your needs.
library(shiny)
ui <- shinyUI(fluidPage(
tags$head(
tags$style(HTML("
#creat {
width: 110px;
}
"))),
tags$head( tags$style(type="text/css","label{ display: table-cell; text-align: center;vertical-align: middle; } .form-group { display: table-row;}") ),
sidebarLayout(
sidebarPanel(
div(style= "display: inline;",
numericInput(inputId="creat",label="Creatinine ",value="",min=0.1,max=15,step=0.1, width = "80px")),
div(style= "display: inline;",
awesomeRadio(
inputId = "units",
label = "",
choices = list("mg/dl" = "mg", "umol/L" = "um"),
selected = "mg",
inline = TRUE,
status = "success",
width = "300px"
)
)
),
mainPanel(p('Some calculation with creatinine'), verbatimTextOutput("value"),
textOutput("creat"))
)
))
server <- function(input, output, session) {
output$creat <- renderText({input$creat})
numbers <- reactive({
if (input$units=="um"){
minval <- 8.842
maxval <- 1326.3
}else {
minval <- 0.1
maxval <- 15
}
validate(
need(is.numeric(input$creat) & input$creat >= minval & input$creat <= maxval , sprintf("Please input a number between %s to %s", minval,maxval))
)
})
output$value <- renderPrint({ numbers() })
observeEvent(input$units,{
print(input$units)
if (input$units=="um"){
if (is.null(input$creat)) value <- NULL
else value <- input$creat*88.42
updateNumericInput(
session = session, # getDefaultReactiveDomain(),
inputId = "creat",
value = value,
min = 8.842,
max = 1326.3,
step = 0.1
)
}else if (input$units=="mg") {
if (is.null(input$creat)) value <- NULL
else value <- input$creat/88.42
updateNumericInput(
session = session, # getDefaultReactiveDomain(),
inputId = "creat",
value = value,
min = 0.1,
max = 15,
step = 0.1
)
}
})
}
shinyApp(ui = ui, server = server)
I need a reactive variable (declared server-side) available after start-up. Using what I learned here How to create a conditional renderUI in Shiny dashboard I tried wrapping in reactive() before defining the UI but no luck. Moving topValuesSelector to the UI inside a conditionalPanel would work except conditional panels apparently do not like the %in% operator (a separate issue that I also tried to resolve w/o success).
if (interactive()) {
library(shiny)
library(shinydashboard)
library(shinydashboardPlus)
ui <-
dashboardPage(header = dashboardHeaderPlus(left_menu = tagList(
dropdownBlock(
id = "prefDropdown",
title = "Preferences",
icon = NULL,
badgeStatus = NULL,
checkboxGroupInput(
inputId = "prefDropdown",
label = NULL,
choices = c("Pareto",
"Legend on chart",
"Cases/1K uniques",
"Top 10 only"),
selected = c("Pareto", "Cases/1K uniques", "Top 10 only")
),
uiOutput("topValues")
)
)),
dashboardSidebar(),
dashboardBody(fluidRow(box(
title = "Top",
textOutput("topN")
))))
server <- function(input, output) {
topValuesSelector <- reactive({
if ("Top 10 only" %in% input$prefDropdown) {
numericInput(
inputId = "topValues",
label = NULL,
width = "25%",
value = 10,
min = 1,
max = 30,
step = 1
)
}
})
output$topValues <- renderUI({
topValuesSelector()
})
observe({
if ("Top 10 only" %in% input$prefDropdown) {
output$topN <- renderText(input$topValues)
} else{
output$topN <- renderText(100)
}
})
}
shinyApp(ui, server)
}
The intent is for the initial value of "topValues" to be 10 with this value immediately available. However, no value is available which causes an error. Using req() avoids the error by pausing execution but that is not a viable approach because "topValues" is needed for a plot. So no plot until selecting "prefDropdown".
It looks like the problem is that input$topValues does not exist until you click on the Preferences button. Since the UI element isn't needed it hasn't been created yet.
In order to work around that you can create a variable that detects whether or not the input is available and if not use a default value.
if (interactive()) {
library(shiny)
library(shinydashboard)
library(shinydashboardPlus)
ui <-
dashboardPage(header = dashboardHeaderPlus(left_menu = tagList(
dropdownBlock(
id = "prefDropdown",
title = "Preferences",
icon = NULL,
badgeStatus = NULL,
checkboxGroupInput(
inputId = "prefDropdown",
label = NULL,
choices = c("Pareto",
"Legend on chart",
"Cases/1K uniques",
"Top 10 only"),
selected = c("Pareto", "Cases/1K uniques", "Top 10 only")
),
uiOutput("topValues")
)
)),
dashboardSidebar(),
dashboardBody(fluidRow(box(
title = "Top",
textOutput("topN")
))))
server <- function(input, output) {
## We want to use the same default value in two places so create a var
default_value <- 10
topValuesSelector <- reactive({
if ("Top 10 only" %in% input$prefDropdown) {
numericInput(
inputId = "topValues",
label = NULL,
width = "25%",
value = default_value, ## Change to use the default value
min = 1,
max = 30,
step = 1
)
}
})
output$topValues <- renderUI({
topValuesSelector()
})
## Create a variable that is the default value unless the input is available
myTopN <- reactive({
if(length(input$topValues)>0){
return(input$topValues)
}
return(default_value)
})
observe({
if ("Top 10 only" %in% input$prefDropdown) {
# output$topN <- renderText(input$topValues)
output$topN <- renderText(myTopN()) ## Use our new variable instead of the input directly
} else{
output$topN <- renderText(100)
}
})
}
shinyApp(ui, server)
}
There are a couple of other things going on with your code. Notice that "Top 10 only" %in% input$prefDropdown will not do what you think it is doing. You have to check to see if "Top 10 only" is TRUE... I'll leave you there to start another question if you get stuck again.
I'm having trouble iteratively loading and filtering a datatable in Shiny. The ideal workflow would be as follows:
User pushes button to confirm loading data
Data is retrieved from MySql query. Note this should only occur once
(optional) filter buttons/sliders become visible/available
User interacts with buttons/sliders to filter datatable
1 and 2 work fine, but I am having particular issue with 4 (also any input for 3 would be appreciated as well).
The initial code that is not working is as follows:
get_data=function(){ # note that this is for sample purpose, real function is MySQL query
df=data.frame(x=1:10,Age=1:100)
print("loading data...")
return(df)
}
ui = bootstrapPage(
fluidPage(
fluidRow(
actionButton(
inputId = "confirm_button",
label = "Confirm"
)
)
,
fluidRow(
column(4,
sliderInput("slider_age", label = h4("Age"), min = 0,
max = 100, value = c(0, 100))
)
),
hr(),
fluidRow(
DT::dataTableOutput("all_background_table")
)
)
)
server = function(input, output){
observeEvent(input$confirm_button, {
req(input$confirm_button)
output$all_background_table <- DT::renderDataTable({
all_background=get_data() # <- MySQL function to laod data
# if all_background filter function put here:
#--> data is re-loaded by MySQL query
# if all_background filter function is put here surrounded by observeEvent(input$slider_age, {...:
#--> there is no change when input$slider_age is changed
datatable(all_background,
rownames = FALSE,
style = "bootstrap")
})
})
observeEvent(input$slider_age, {
## this will throw an error requiring all_background
#--> Error in observeEventHandler: object 'all_background' not found
req(input$confirmation_load_pts)
all_background=all_background[(all_background$Age > as.numeric(input$slider_age[1]) & all_background$Age < as.numeric(input$slider_age[2])),]
})
}
shinyApp(ui, server)
I am not sure about get_data(), but I will be using df to make it easier. With eventReactive you can create a new data frame after using the slider and only after clicking on the confirm button. Your observeEventwould not be necessary for this scenario.
library(shiny)
library(DT)
get_data=function(){ # note that this is for sample purpose, real function is MySQL query
df=data.frame(x=1:10,Age=1:100)
print("loading data...")
return(df)
}
ui = bootstrapPage(
fluidPage(
fluidRow(
actionButton(
inputId = "confirm_button",
label = "Confirm"
)
)
,
fluidRow(
column(4,
sliderInput("slider_age", label = h4("Age"), min = 0,
max = 100, value = c(0, 100))
)
),
hr(),
fluidRow(
DT::dataTableOutput("all_background_table")
)
)
)
server = function(input, output){
test <- eventReactive(input$confirm_button, {
df=get_data()
})
observeEvent(input$confirm_button, {
output$all_background_table <- DT::renderDataTable({
df=test()
all_background2=df[(df$Age > as.numeric(input$slider_age[1]) & df$Age < as.numeric(input$slider_age[2])),]
datatable(all_background2,
rownames = FALSE,
style = "bootstrap")
})
})
}
shinyApp(ui, server)
I was wondering if it was possible to set the value of a numeric input via updateNumericInput to be equal to 0 if a checkbox is not clicked. Below is how my code is set up at the moment to generate the check boxes and numeric inputs. I had to use loops to create a dynamic number of check boxes and inputs due to the nature of the app so I would really appreciate any help linking the two while keeping the functionality.
Server file:
shinyServer(function(input, output, session) {
output$inputs1 <- renderUI({
numSliders <- input$sources
lapply(1:numSliders, function(i) {
numericInput(
inputId = paste0('slider1', i),
label = df[i,2],
value = df[i,3]*(input$budget)/100)
})
})
output$checks1 <- renderUI({
numSliders <- input$sources
lapply(1:numSliders, function(i) {
checkboxInput(
inputId = paste0('check1', i),
label = df[i,2],
value = TRUE
)
})
})
}
UI:
shinyUI(fluidPage(fluidRow(
sidebarLayout(
sidebarPanel(
column(5,numericInput("budget", "Budget", value = 0),
uiOutput("checks1")),
column(5,uiOutput("inputs1"))),
mainPanel()
)
)
)
)
Please let me know if there is any sort of workaround for this.
Thanks in advance!
Since you can only generate a finite number of widgets the easiest way of creating an observer for each checkboxInput is to create a global variable, say, max_widgets which gives an upper bound on widgets. You then restrict the maximal value of numericInput which controls a number of widgets to max_widgets (so input$sources) and require within renderUIs that
req(numSliders > 0 & numSliders <= max_widgets)
(I would use validate and need to inform the user that the number of widgets has to be non negative and is bound to max_widgets but in my shiny version there is a bug and validate doesn't work as supposed.)
You then create observers for each checkboxInput on the server side:
lapply(1:max_widgets, function(i) {
observeEvent(input[[paste0('check', i)]], {
print(paste0("update of numeric", i))
updateNumericInput(session, inputId = paste0('numeric', i),
value = 0)
})
})
Note that this will create observers for all possible checkboxes (checkboxes may not even exist - shiny won't complain :) )
This may not be perfect but, as said, you will have only one observer for each checkbox.
If you dynamically generate observers in a following way (without a global variable max_widgets)
observe({
lapply(1:input$sources, function(i) {
observeEvent(input[[paste0('check', i)]], {
print(paste0("numeric", i, " = ", input[[paste0('numeric', i)]]))
updateNumericInput(session, inputId = paste0('numeric', i),
value = 0)
})
})
it will work too but each time you will generate new widgets you will also create an observer for it. So you may get multiple observers for each checkboxInput!
If your app is small then it won't matter much but in general it may lead to bugs. You can easy deal with it but it makes the code slightly more complicated - there was a question that touched on this problem.
Full example:
library(shiny)
rm(list = ls())
max_widgets <- 15
server <- shinyServer(function(input, output, session) {
output$inputs1 <- renderUI({
numSliders <- input$sources
# My shiny version has a bug and can't use validate(need(...)) because
# it doesn't work as suppossed
req(numSliders > 0 & numSliders <= max_widgets)
lapply(1:numSliders, function(i) {
numericInput(
inputId = paste0('numeric', i),
# label = df[i,2],
paste0("Input ", i),
# value = df[i,3] * (input$budget) / 100)
value = i * (input$budget) / 100)
})
})
output$checks1 <- renderUI({
numSliders <- input$sources
req(numSliders > 0 & numSliders <= max_widgets)
lapply(1:numSliders, function(i) {
list(
checkboxInput(
inputId = paste0('check', i),
# label = df[i,2],
label = paste0("Checkbox ", i),
value = TRUE
),
br()
)
})
})
lapply(1:max_widgets, function(i) {
observeEvent(input[[paste0('check', i)]], {
print(paste0("update of numeric", i))
updateNumericInput(session, inputId = paste0('numeric', i),
value = 0)
})
})
})
ui <- shinyUI(fluidPage(fluidRow(
sidebarLayout(
sidebarPanel(
column(5,
numericInput("budget", "Budget", value = 0),
hr(),
br(),
uiOutput("checks1")
),
column(5,
numericInput("sources", "Sources", value = 0, min = 0, max = max_widgets),
hr(),
uiOutput("inputs1")
)
),
mainPanel()
)
)))
shinyApp(ui, server)
I would like to know how to select all the check-boxes at once. In my code I have Five check-boxes.
server <- function(input, output) {
output$distPlot <- renderPlot({
hist(rnorm(input$obs), col = 'darkgray', border = 'white')
})
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("obs", "Number of observations:", min = 10, max = 500, value = 100),
checkboxInput("checkbox1", label = "meanSNR", value= FALSE),
checkboxInput("checkbox2", label = "t-statistics", value = FALSE),
checkboxInput("checkbox3", label = "adjusted p-value", value = FALSE),
checkboxInput("checkbox4", label = "log-odds", value = FALSE),
checkboxInput("checkbox5", label = "All", value = FALSE)),
mainPanel(plotOutput("distPlot"))
)
)
shinyApp(ui = ui, server = server)
I would like to know how to make it work
1) If the user selects the fifth check-box All, It should automatically select all the check-boxes. On uncheck, it should deselect all the Checkboxes.
2 ) If the user selects the first four check-boxes, it should select the fifth one All check-box too.
For condition 1) , the screen should like this
This isn't nearly as elegant as Jorel's answer, but it's a solution that uses pure shiny package code.
library(shiny)
#* make sure to include session as an argument in order to use the update functions
server <- function(input, output, session) {
output$distPlot <- renderPlot({
hist(rnorm(input$obs), col = 'darkgray', border = 'white')
})
#* This observer will update checkboxes 1 - 4 to TRUE whenever checkbox 5 is TRUE
observeEvent(
eventExpr = input$checkbox5,
handlerExpr =
{
if (input$checkbox5)
lapply(paste0("checkbox", 1:4),
function(x)
{
updateCheckboxInput(session, x, value = input$checkbox5)
}
)
}
)
#* This observer will set checkbox 5 to FALSE whenever any of checkbox 1-4 is FALSE
lapply(paste0("checkbox", 1:4),
function(x)
{
observeEvent(
eventExpr = input[[x]],
handlerExpr =
{
if (!input[[x]]) updateCheckboxInput(session, "checkbox5", value = FALSE)
}
)
}
)
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("obs", "Number of observations:", min = 10, max = 500, value = 100),
checkboxInput("checkbox1", label = "meanSNR", value= FALSE),
checkboxInput("checkbox2", label = "t-statistics", value = FALSE),
checkboxInput("checkbox3", label = "adjusted p-value", value = FALSE),
checkboxInput("checkbox4", label = "log-odds", value = FALSE),
checkboxInput("checkbox5", label = "All", value = FALSE)
),
mainPanel(plotOutput("distPlot"))
)
)
shinyApp(ui = ui, server = server)
Some follow up and recommendations
I spent a little time trying to get the application to do what you've specified, but honestly, it felt pretty unnatural (and wasn't working particularly well).
In a checkbox, if you check "All", it implies that you wish to check all the boxes, but I don't think unselecting "All" necessarily implies unselecting all of the boxes.
Stemming from 1), you're trying to have one control do two different things, which can open the door to confusion.
So here's my recommendation: User four checkboxes and two buttons. The two buttons control if you select all or unselect all of the boxes, and they act independently.
library(shiny)
#* make sure to include session as an argument in order to use the update functions
server <- function(input, output, session) {
output$distPlot <- renderPlot({
hist(rnorm(input$obs), col = 'darkgray', border = 'white')
})
#* This observer will update checkboxes 1 - 4 to TRUE whenever selectAll is clicked
observeEvent(
eventExpr = input$selectAll,
handlerExpr =
{
lapply(paste0("checkbox", 1:4),
function(x)
{
updateCheckboxInput(session = session,
inputId = x,
value = TRUE)
}
)
}
)
#* This observer will update checkboxes 1 - 4 to FALSE whenever deselectAll is clicked
observeEvent(
eventExpr = input$deselectAll,
handlerExpr =
{
lapply(paste0("checkbox", 1:4),
function(x)
{
updateCheckboxInput(session = session,
inputId = x,
value = FALSE)
}
)
}
)
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("obs", "Number of observations:", min = 10, max = 500, value = 100),
checkboxInput("checkbox1", label = "meanSNR", value= FALSE),
checkboxInput("checkbox2", label = "t-statistics", value = FALSE),
checkboxInput("checkbox3", label = "adjusted p-value", value = FALSE),
checkboxInput("checkbox4", label = "log-odds", value = FALSE),
actionButton("selectAll", label = "Select All"),
actionButton("deselectAll", label = "Deselect All")
),
mainPanel(plotOutput("distPlot"))
)
)
shinyApp(ui = ui, server = server)
I'd do this on JS side. I'd get every checkBox like this
var cbx1 = document.getElementById('checkbox1'); etc. and i'll store them in an array
i'll also have a function that will check everything :
checkEverything = function(){
cbx1.val = "true";
cbx2.val = "true";
// etc..
}
And i would bind this function on the 4th checkbox onclick event. I'd also have a function that check if every is checked like :
checkIfEverythingChecked = function(){
if(cbx1.val == true && cbx2.val == true)
cbx4.val = true;
}
And i'd bing this on the onclick event of every checkBox