Replies: 6 comments 5 replies
-
For the validation communication, we could call validate_inputs inside validate_block (like here) and only call validate_block within the corresponding observer. Validation happens on the Shiny side. It is fairly limited but it's ok for now, the most important is to check whether inputs have a value and prevent from evaluating invalid expressions. The goal was also to handle the case where we change upstream data and a column does not exist anymore: user should be notified about this. I don't store the valid status within the block structure. Maybe this is a missing piece and we could have a preliminary validation running before the shiny layer. The validate_field_* methods ensure the value stays in within the allowed bounds (for a range, numeric input, ...) and reset to factory settings if not. They should actually be renamed to |
Beta Was this translation helpful? Give feedback.
-
Considerations for the validation. Validation should happen at initialisation so inside validate_field makes sense because we do this: initialize_field.field <- function(x, env = list()) {
validate_field(
eval_set_field_value(x, env)
)
} Validation should refresh in real time (as long as the app is alive), which is currently the case (update_field runs each time a field has changed): update_field.field <- function(x, new, env = list()) {
x <- eval_set_field_value(x, env)
value(x) <- new
validate_field(x)
} When the app is running:
Question:
Example: new_numeric_field(n_rows, n_rows_min, nrow(data)) Min and max are defined by the block developer. While |
Beta Was this translation helpful? Give feedback.
-
Some extra thoughts discussed with @JohnCoene and @nbenn:
TBC |
Beta Was this translation helpful? Give feedback.
-
@nbenn: I started to write down some ideas we discussed on how the core could look like in the future (WIP) graph TD;
block1[Block1]
block2[Block2]
block3[Block3]
init_block[Initialize blocks]
block1 --> |construct block| init_block
block2 --> init_block
block3 --> init_block
init_block --> |set field values| evaluate_block
subgraph stack
evaluate_block[Evaluate blocks]
evaluate_block --> |data| validation
validation --> valid_block[Valid block]
validation --> invalid_block[Invalid block]
valid_block --> result[Stack result]
end
config[Validation config] --> |Custom acceptance| validation[Validation]
stack ---> |Render blocks| shiny
invalid_block --> |Notifications| ui
subgraph shiny
ui[User interface]
end
user[End user] --> |Input change| ui
ui ---> |real time update| evaluate_block
From this flowchart:
custom_data_block <- data_block(selected = "iris")
custom_select_block <- select_block(
columns = "Species",
fancy_param1 = TRUE,
fancy_param2 = FALSE,
submit = TRUE
) Currently, the code base prevents us to do so: new_select_block <- function(data, columns = colnames(data)[1], ...) {
all_cols <- function(data) colnames(data)
# hidden code
...
} Instead we could have a default for new_select_block <- function(data = data.frame(), columns = character(), ...) {
all_cols <- function(data) colnames(data)
fields <- list(
columns = new_select_field(columns, all_cols, multiple = TRUE, title = "Columns")
)
# hidden code
...
}
TBC |
Beta Was this translation helpful? Give feedback.
-
I agree with your dislike of block1 <- data_block(selected = "iris")
block2 <- select_block(data = iris, columns = "Species") what I was more hoping for is something along the lines of block1 <- data_block(selected = "iris")
block2 <- select_block(columns = "Species") being possible. |
Beta Was this translation helpful? Give feedback.
-
Next steps:
|
Beta Was this translation helpful? Give feedback.
-
Personally I feel we are still lacking clarity in this area, so I'd like to use this space to collect some thoughts.
It's important to note that some of the things that feel like requirements might simply result from design flaws that'd be better addressed in other ways. If you feel this way about something, please make this clear.
The way I see it currently, we might have up to three things that might have to happen until an object is "fully usable":
If for example a stack is created as
The involved fields and blocks can be created, but they are not yet initialized, as we don't have field values yet. Such "uninitialized" blocks should probably not be validated yet, as it does not much sense to do so. As soon as all required inputs for a field have been provided, the field can be validated and as soon as all fields for a block are "validatable", the block can be considered initialized and the validation result is something like
all(vapply(<blocks>, is_valid, logical(1L))
.We have generics
is_initialized()
currently forblock
andfield
validate_field()
for many different types of fieldsand functions
validate_inputs()
validate_block()
which communicate validation results to the user.
Currently (@DivadNojnarg please correct me if I'm wrong), the functions responsible for communicating validation status to the user by looking at shiny input values1. Some issues I see with this (much of this might be a lack of proper understanding on my side):
validate_inputs()
is doing too much or the wrong thing here. Each field should be able to report on its validity (or initialization) status (and block validity is an aggregation of that). And separately from that, there is the issue of communicating the result of this validation to the user.which(inputs_to_validate %in% c("expression", "submit"))
is a (solvable) problem. How these fields are named is arbitrary and entirely up to the block implementer. We cannot hard-code such validation behavior to field names. If certain fields are to be excluded from this, it should be based on field type or some field attributevalidatable
or so.length(val) == 0 || (length(val) > 0 && all(nchar(val)) == 0)
you fix a global criterion for what is a valid field value. I don't think this scales well. It might very well be that""
in some field is a valid value (e.g. if a filter expressioncol == ""
is represented as separate valuescol
andval
,val
would have to be settable to""
, which in this way would globally be considered an "invalid input".Footnotes
@christophsax such things are important to keep an eye on if we want to provide an abstraction for how to look up user inputs (e.g. when having a module based field). ↩
Beta Was this translation helpful? Give feedback.
All reactions