Slide 1

Slide 1 text

BUILDING MULTI-PAGE {SHINY} APPS WITH {BROCHURE} NHS-R / 2022-02-16 COLIN FAY

Slide 2

Slide 2 text

> whoami Colin FAY - Data Science Engineering & R-Hacker at ThinkR. Hyperactive open source developer, lead developer of the {golem} project, author of the Engineering Production Grade Shiny Apps book. https://rtask.thinkr.fr https://twitter.com/_colinfay https://github.com/colinfay https://colinfay.me Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 2 / 23

Slide 3

Slide 3 text

Data Science engineering, focused on R  Training  Software Engineering  R in production  Consulting Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 3 / 23

Slide 4

Slide 4 text

BUILDING MULTI-PAGE {SHINY} APPS WITH {BROCHURE}

Slide 5

Slide 5 text

Multiple Page Shiny Apps with {brochure} Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 5 / 23

Slide 6

Slide 6 text

I know what you're thinking 🤔 Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 6 / 23

Slide 7

Slide 7 text

YASE (Yet Another Shiny Extension) Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 7 / 23

Slide 8

Slide 8 text

Ok let me explain 🗣 Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 8 / 23

Slide 9

Slide 9 text

(Because yes, there's a rational explaination) Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 9 / 23

Slide 10

Slide 10 text

10(*) types of webapps Multi-page apps The "traditionnal" way of building content on the web. Each time you move from one page to another, the whole content is reloaded. This is how most websites work. Single page apps More "modern" way to build apps. The HTML/CSS/JS is uploaded one time, and its content is updated via JavaScript requests to the server. This is how {shiny} works. (*) Sorry for everyone not getting the joke, I just read a book about binary numbers and wanted to sound smart. Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 10 / 23

Slide 11

Slide 11 text

What's wrong with the way {shiny} works? Nothing, but let's take a step back What do I need when I build a web app? Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 11 / 23

Slide 12

Slide 12 text

What do I need when building a web app ?  Multiple paths (real ones): /, /analysis, /results, /contact...  Not load the whole app everytime  Cookies  Healtcheck  HTTP method support (POST, PUT, DELETE, etc.)  HTTP code support (200, 300, 400...)  Tweak http requests and responses And many others things Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 12 / 23

Slide 13

Slide 13 text

But why would I want that? Serving static, pure HTML landing pages Being able to point to specific part of the app (not 'open the app, click there and there and see') Lower the load on the server by serving static HTML elements Monitor your app via healthcheck Identify the user when they come back to the app POST elements to the server Add a 404 page for unreachable pages Redirect pages to others Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 13 / 23

Slide 14

Slide 14 text

HERE COMES {BROCHURE}!

Slide 15

Slide 15 text

Previous works {blaze} https://github.com/nteetor/blaze {shiny.router} https://github.com/Appsilon/shiny.router => Not really multi-page, but "hack" your browser path by using URL fragments A Fragment URL Specifies A Location Within A Page Url fragments create history in your browser but don't reload the full page Are not sent as part of the http request (so it can be harder to log what's happening in the app) => Don't have all the features from the last slide Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 15 / 23

Slide 16

Slide 16 text

Basic brochure library(shiny) library(brochure) brochureApp( page( href = "/", ui = fluidPage( plotOutput("plot") ), server = function(input, output, session) { output$plot <- renderPlot({ plot(iris) }) } ), page( href = "/page2", ui = fluidPage( h1("This is my second page") ) ) ) Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 16 / 23

Slide 17

Slide 17 text

About HTTP

Slide 18

Slide 18 text

log_where <- function(req) { cli::cat_rule( sprintf( "%s - %s", Sys.time(), req$PATH_INFO ) ) req } brochureApp( req_handlers = list( log_where ) ) Cookies, request and response handlers Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 18 / 23

Slide 19

Slide 19 text

Cookies, request and response handlers login <- function() { page( href = "/login", ui = tagList( h1("You've just logged!") ), server = function(input, output, session) { }, res_handlers = list( ~ set_cookie(.x, "BROCHURECOOKIE", 12) ) ) } Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 19 / 23

Slide 20

Slide 20 text

brochureApp( # Pages page_1(), page_2(), page_contact(), page( href = "/check", ui = tagList(), # This req_handler will returns # an httpResponse, it will be # returned directly to the browser req_handlers = list( # Exported since shiny 1.6.0 ~ shiny::httpResponse( 200, content = "OK" ) ) ) ) > httr::GET('http://url/check') Response [http://url/check] Date: 2022-02-16 12:19 Status: 200 Content-Type: text/html; charset=UTF-8 Size: 2 B Simple healthcheck Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 20 / 23

Slide 21

Slide 21 text

Demo time Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 21 / 23

Slide 22

Slide 22 text

📖 engineering-shiny.org/ Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 22 / 23

Slide 23

Slide 23 text

Thx! Questions? Find me Online [email protected] https://twitter.com/_colinfay https://twitter.com/thinkr_fr https://github.com/ColinFay https://thinkr.fr/ https://rtask.thinkr.fr/ https://colinfay.me/ Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 23 / 23