Skip to contents

In this vignette, we show how to create a new field, that is the slider_field, similar to the range field with only 1 value.

Introduction

To create a field, we first need to design a constructor. In our case, we call it new_slider_field. Since fields are mirroring shiny inputs, we can naturally provide the corresponding input parameters that are the value, minimum, maximum and step. We pass all of these to blockr::new_field() and give it a custom class slider_field.

new_slider_field <- function(
    value = numeric(),
    min = numeric(),
    max = numeric(),
    step = numeric(),
    ...) {
  blockr::new_field(
    value = value,
    min = min,
    max = max,
    step = step,
    ...,
    class = "slider_field"
  )
}

On the UI side, this is fairly straightforward as all we have to do is call shiny::sliderInput with the same parameters given in the above constructor.

ui_input.slider_field <- function(x, id, name) {
  shiny::sliderInput(
    blockr::input_ids(x, id),
    name,
    value = blockr::value(x, "value"),
    min = blockr::value(x, "min"),
    max = blockr::value(x, "max"),
    step = blockr::value(x, "step")
  )
}

The next step consists in validating the field to ensure provided values are correct. For instance, we want to make sure all passed elements are numbers. This is done within the validate_field.slider_field method. We extract all the field element values with blockr::value(x, "element") and use the suitable validate function, that is validate_number(). Note that if nothing is provided, the validation falls back to validate_field.field(), which does nothing.

validate_field.slider_field <- function(x) {
  val <- value(x)
  min <- value(x, "min")
  max <- value(x, "max")
  step <- value(x, "step")

  validate_number(val)

  if (length(min)) {
    validate_number(min, "min")
  }

  if (length(max)) {
    validate_number(max, "max")
  }

  if (length(step)) {
    validate_number(step, "step")
  }

  NextMethod()
}

Finally, we want to make sure that we can update the field with the ui_update.slider_field() method. This is needed whenever another field would have to update the slider field, because of a possible dependency between these 2 fields. As an example, this is what currently happens in the filter block where the selected column field updates the value field (the field type changes depending on the column type).

ui_update.slider_field <- function(x, session, id, name) {
  updateSliderInput(
    session,
    blockr::input_ids(x, id),
    blockr::get_field_name(x, name),
    blockr::value(x),
    blockr::value(x, "min"),
    blockr::value(x, "max"),
    blockr::value(x, "step")
  )
}

Example

We can test our newly created field in a custom block.

library(blockr)

new_slider_field <- function(
    value = numeric(),
    min = numeric(),
    max = numeric(),
    step = numeric(),
    ...) {
  blockr::new_field(
    value = value,
    min = min,
    max = max,
    step = step,
    ...,
    class = "slider_field"
  )
}

ui_input.slider_field <- function(x, id, name) {
  shiny::sliderInput(
    blockr::input_ids(x, id),
    name,
    value = blockr::value(x, "value"),
    min = blockr::value(x, "min"),
    max = blockr::value(x, "max"),
    step = blockr::value(x, "step")
  )
}

validate_field.slider_field <- function(x) {
  val <- value(x)
  min <- value(x, "min")
  max <- value(x, "max")
  step <- value(x, "step")

  validate_number(val)

  if (length(min)) {
    validate_number(min, "min")
  }

  if (length(max)) {
    validate_number(max, "max")
  }

  if (length(step)) {
    validate_number(step, "step")
  }

  NextMethod()
}

ui_update.slider_field <- function(x, session, id, name) {
  updateSliderInput(
    session,
    blockr::input_ids(x, id),
    name,
    blockr::value(x),
    blockr::value(x, "min"),
    blockr::value(x, "max"),
    blockr::value(x, "step")
  )
}

registerS3method("ui_input", "slider_field", ui_input.slider_field)
registerS3method("ui_update", "slider_field", ui_update.slider_field)

new_slice_block <- function(from = 0, ...) {

  n_rows <- \(data) nrow(data)

  fields <- list(
    rows = new_slider_field(
      value = from,
      min = 0,
      max = n_rows,
      step = 1,
      title = "Select rows"
    )
  )

  new_block(
    fields = fields,
    expr = quote(dplyr::slice(seq_len(.(rows)))),
    name = "Slider slice block",
    ...,
    class = c("slice_block", "transform_block")
  )
}

serve_stack(
  new_stack(
    new_dataset_block("iris"),
    new_slice_block(5)
  )
)