blockr-org / blockr

Composable, extensible no-code UI
https://blockr-org.github.io/blockr/
GNU General Public License v3.0
11 stars 2 forks source link

Generate Server method for block #378

Open JohnCoene opened 1 month ago

JohnCoene commented 1 month ago

We have to create a lot of custom blocks with custom renderers, e.g.: for plotly, ggiraph, etc. (an example below).

When we create a new custom block that does not inherit from plot_block, transform_block, or data_block we have to reimplement the generate_server method because there is no method for block.

I've never had to change that method, it would be a lot of work anyways, the function generate_server_block() should work fine for most (probrably all) blocks.

my_block <- function(...){
  all_cols <- function(data) colnames(data)

  first_col <- \(data) colnames(data)[1]
  second_col <- \(data) colnames(data)[2]

  fields <- list(
    x_var = new_select_field(first_col, all_cols),
    y_var = new_select_field(second_col, all_cols)
  )

  new_block(
    fields = fields,
    expr = quote({
      ggplot2::ggplot(
        data,
        mapping = ggplot2::aes_string(
          x = .(x_var),
          y = .(y_var)
        )
      ) +
        ggplot2::geom_point() +
        ggplot2::geom_line()
    }),
    ...,
    class = "my_block"
  )
}

.S3method("server_output", "my_block", function(x, result, output){
  renderPlot(result())
})

.S3method("uiOutputBlock", "my_block", function(x, ns){
  plotOutput(ns("res"))
})

# we have to implement this
.S3method("generate_server", "my_block", function(...){
  blockr:::generate_server_block(...)
})

stack <-  new_stack(
  new_dataset_block,
  my_block
)

ui <- fluidPage(
  theme = bslib::bs_theme(5L),
  generate_ui(stack)
)

server <- function(input, output, session) {
  generate_server(stack)
}

shinyApp(ui, server)
DivadNojnarg commented 1 month ago

We have written a note about this in the documentation that it would be easier to inherit from plot_block (https://blockr-org.github.io/blockr/articles/plot-block.html#to-do-list):

On the Shiny side, we have to handle the plot output element. Remember that the server_output() generic is defined in the {blockr} server.R script. It supports tables with server_output.block() and plots with server_output.plot_block(). Therefore, we don’t need to create another S3 method and have to make sure our new plot block inherits from the plot_block class to dispatch to the correct method.

That does not solve the rendering issue for {ggiraph} for which you'd have to add extra methods for the output:

uiOutputBlock.ggiraph_block <- function(x, ns) {
  ggiraph::girafeOutput(ns("plot"))
}

server_output.ggiraph_block <- function(x, result, output) {
  ggiraph::renderGirafe(result())
}
JohnCoene commented 1 month ago

Sorry if I was not clear.

Creating the methods for uiOutputBlock and server_output is precisely what I want to do, what I'm saying here is that I don't think there is a need to re-implement generate_server. Moreover it's currently not possible today to create such blocks without knowing we have to use the unexported generate_server_block function.

We should have a method for block on generate_server so that we don't need to implement this or export generate_server_block.