Rending HTML pages using Plumber in R - r
I understand that plumber is more suitable to build an API than a full fledged website, this said I am trying to display the dynamic data from the db (mongo) in HTML. All works fine but the way I use (heavily inspired from a titanic example) might not be the best one. Here is an example of my homepage:
#' Return result
#' #get /
#' #serializer html
function(ht) {
title <- "Title"
body_intro <- "Welcome to R.gift!"
body_model <- paste("This is just a test ...page from the db with name <b>", single[1], "</b>")
body_msg <- paste("the home page title is <b>", single[2] , "</b>",
"The home page content:<b>",
single[3], "</b>",
sep = "\n")
css <- ' <link href="https://cloud.typography.com/7/css/fonts.css" rel="stylesheet">'
about <- 'about page'
contact <- 'contact page'
result <- paste(
"<head>",
css,
"</head>",
"<html>",
'<div style="font-family: Giant Background A, Giant Background B;font-style: normal;font-weight: 400;font-size:20pt;">',
"<h1>", title, "</h1>",
"<body>",
body_intro, "</div>",
'<div style="font-family: Gotham A, Gotham B;font-style: normal;font-weight: 400;font-size:16pt;">',
"<p>", body_model, "</p>",
"<p>", body_msg, "</p>",
"<p>", about, "</p>",
"<p>", contact, "</p>",
"</div>",
"</body>",
"</html>",
collapse = "\n"
)
return(result)
}
so my question is if there is a more elegant way to achieve the same perhaps with a semi-templating system. The solution might be obvious (I am very new to R so bear with me). I know that plumber can server static files with
#* #assets ./files/static
list()
but I assume this wouldn't allow me to pass variables into index.html for example ?
The ideal scenario is having just tags like in any templating system.
Thanks in advance!
You can try something like this:
basicHTML <- "The home page title is <b> %s </b> The home page content: <b> %s </b>" # create a template in a file or in the script.
single <- c(title = "Hello World", bodyMsg = "lorem ipsum") # set up your parameters.
finalHTML <- sprintf(basicHTML, single[1], single[2])
Or maybe you feel more comfortable with this approach: https://shiny.rstudio.com/articles/templates.html
I hope this helps you.
My advice would be to do away with the server side rendering and do everything on the client side. You could use a frontend library like vue (if you understand HTML and basic javascript, you can use vue) and then do a GET request to the backend for the data you need. Then use that data to customize the UI. This approach let's R focus on data and the frontend focus on the presentation.
Related
R markdown: datable within collapsible section
Below is a R-markdown document with <details> tags to create collapsible sections. Can you help me to render the datatable from section 2 in the html output? Minimal reproducible example ### Section 1 <details> <summary>Click to expand</summary> ```{r, echo=FALSE} head(iris) ``` </details> ### Section 2 <details> <summary>Click to expand</summary> ```{r, echo=FALSE} DT::datatable(iris) ``` </details>
This is not really an answer, but slightly too long to be a comment, so I've included it here. Hopefuly someone can use it to work out an actual answer: The good new is that it is definitely "possible". The bad news is "it is not easy". With my limited knowledge of web-dev the problem seems to be, that DT::datatable (or more correctly htmlwidgets:::print.html_widget) creates an entire html-webpage in a temporary file, and this is the default method for visualizing DT::datatable. The file itself looks something like <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <style>body{background-color:white;}</style> <script src="lib/htmlwidgets-1.5.3/htmlwidgets.js"></script> <script src="lib/jquery-1.12.4/jquery.min.js"></script> <link href="lib/datatables-css-0.0.0/datatables-crosstalk.css" rel="stylesheet" /> <script src="lib/datatables-binding-0.16/datatables.js"></script> <link href="lib/dt-core-1.10.20/css/jquery.dataTables.min.css" rel="stylesheet" /> <link href="lib/dt-core-1.10.20/css/jquery.dataTables.extra.css" rel="stylesheet" /> <script src="lib/dt-core-1.10.20/js/jquery.dataTables.min.js"></script> <link href="lib/crosstalk-1.1.0.1/css/crosstalk.css" rel="stylesheet" /> <script src="lib/crosstalk-1.1.0.1/js/crosstalk.min.js"></script> </head> <body> <div id="htmlwidget_container"> <div id="htmlwidget-cd5f37d21433eb2088ae" style="width:960px;height:500px;" class="datatables html-widget"></div> </div> <script type="application/json" data-for="htmlwidget-cd5f37d21433eb2088ae">{"x":{"filter":"none","data":[["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35","36","37","38","39","40","41","42","43","44","45","46","47","48","49","50","51","52","53","54","55","56","57","58","59","60","61","62","63","64","65","66","67","68","69","70","71","72","73","74","75","76","77","78","79","80","81","82","83","84","85","86","87","88","89","90","91","92","93","94","95","96","97","98","99","100","101","102","103","104","105","106","107","108","109","110","111","112","113","114","115","116","117","118","119","120","121","122","123","124","125","126","127","128","129","130","131","132","133","134","135","136","137","138","139","140","141","142","143","144","145","146","147","148","149","150"],[5.1,4.9,4.7,4.6,5,5.4,4.6,5,4.4,4.9,5.4,4.8,4.8,4.3,5.8,5.7,5.4,5.1,5.7,5.1,5.4,5.1,4.6,5.1,4.8,5,5,5.2,5.2,4.7,4.8,5.4,5.2,5.5,4.9,5,5.5,4.9,4.4,5.1,5,4.5,4.4,5,5.1,4.8,5.1,4.6,5.3,5,7,6.4,6.9,5.5,6.5,5.7,6.3,4.9,6.6,5.2,5,5.9,6,6.1,5.6,6.7,5.6,5.8,6.2,5.6,5.9,6.1,6.3,6.1,6.4,6.6,6.8,6.7,6,5.7,5.5,5.5,5.8,6,5.4,6,6.7,6.3,5.6,5.5,5.5,6.1,5.8,5,5.6,5.7,5.7,6.2,5.1,5.7,6.3,5.8,7.1,6.3,6.5,7.6,4.9,7.3,6.7,7.2,6.5,6.4,6.8,5.7,5.8,6.4,6.5,7.7,7.7,6,6.9,5.6,7.7,6.3,6.7,7.2,6.2,6.1,6.4,7.2,7.4,7.9,6.4,6.3,6.1,7.7,6.3,6.4,6,6.9,6.7,6.9,5.8,6.8,6.7,6.7,6.3,6.5,6.2,5.9],[3.5,3,3.2,3.1,3.6,3.9,3.4,3.4,2.9,3.1,3.7,3.4,3,3,4,4.4,3.9,3.5,3.8,3.8,3.4,3.7,3.6,3.3,3.4,3,3.4,3.5,3.4,3.2,3.1,3.4,4.1,4.2,3.1,3.2,3.5,3.6,3,3.4,3.5,2.3,3.2,3.5,3.8,3,3.8,3.2,3.7,3.3,3.2,3.2,3.1,2.3,2.8,2.8,3.3,2.4,2.9,2.7,2,3,2.2,2.9,2.9,3.1,3,2.7,2.2,2.5,3.2,2.8,2.5,2.8,2.9,3,2.8,3,2.9,2.6,2.4,2.4,2.7,2.7,3,3.4,3.1,2.3,3,2.5,2.6,3,2.6,2.3,2.7,3,2.9,2.9,2.5,2.8,3.3,2.7,3,2.9,3,3,2.5,2.9,2.5,3.6,3.2,2.7,3,2.5,2.8,3.2,3,3.8,2.6,2.2,3.2,2.8,2.8,2.7,3.3,3.2,2.8,3,2.8,3,2.8,3.8,2.8,2.8,2.6,3,3.4,3.1,3,3.1,3.1,3.1,2.7,3.2,3.3,3,2.5,3,3.4,3],[1.4,1.4,1.3,1.5,1.4,1.7,1.4,1.5,1.4,1.5,1.5,1.6,1.4,1.1,1.2,1.5,1.3,1.4,1.7,1.5,1.7,1.5,1,1.7,1.9,1.6,1.6,1.5,1.4,1.6,1.6,1.5,1.5,1.4,1.5,1.2,1.3,1.4,1.3,1.5,1.3,1.3,1.3,1.6,1.9,1.4,1.6,1.4,1.5,1.4,4.7,4.5,4.9,4,4.6,4.5,4.7,3.3,4.6,3.9,3.5,4.2,4,4.7,3.6,4.4,4.5,4.1,4.5,3.9,4.8,4,4.9,4.7,4.3,4.4,4.8,5,4.5,3.5,3.8,3.7,3.9,5.1,4.5,4.5,4.7,4.4,4.1,4,4.4,4.6,4,3.3,4.2,4.2,4.2,4.3,3,4.1,6,5.1,5.9,5.6,5.8,6.6,4.5,6.3,5.8,6.1,5.1,5.3,5.5,5,5.1,5.3,5.5,6.7,6.9,5,5.7,4.9,6.7,4.9,5.7,6,4.8,4.9,5.6,5.8,6.1,6.4,5.6,5.1,5.6,6.1,5.6,5.5,4.8,5.4,5.6,5.1,5.1,5.9,5.7,5.2,5,5.2,5.4,5.1],[0.2,0.2,0.2,0.2,0.2,0.4,0.3,0.2,0.2,0.1,0.2,0.2,0.1,0.1,0.2,0.4,0.4,0.3,0.3,0.3,0.2,0.4,0.2,0.5,0.2,0.2,0.4,0.2,0.2,0.2,0.2,0.4,0.1,0.2,0.2,0.2,0.2,0.1,0.2,0.2,0.3,0.3,0.2,0.6,0.4,0.3,0.2,0.2,0.2,0.2,1.4,1.5,1.5,1.3,1.5,1.3,1.6,1,1.3,1.4,1,1.5,1,1.4,1.3,1.4,1.5,1,1.5,1.1,1.8,1.3,1.5,1.2,1.3,1.4,1.4,1.7,1.5,1,1.1,1,1.2,1.6,1.5,1.6,1.5,1.3,1.3,1.3,1.2,1.4,1.2,1,1.3,1.2,1.3,1.3,1.1,1.3,2.5,1.9,2.1,1.8,2.2,2.1,1.7,1.8,1.8,2.5,2,1.9,2.1,2,2.4,2.3,1.8,2.2,2.3,1.5,2.3,2,2,1.8,2.1,1.8,1.8,1.8,2.1,1.6,1.9,2,2.2,1.5,1.4,2.3,2.4,1.8,1.8,2.1,2.4,2.3,1.9,2.3,2.5,2.3,1.9,2,2.3,1.8],["setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","setosa","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","versicolor","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica","virginica"]],"container":"<table class=\"display\">\n <thead>\n <tr>\n <th> <\/th>\n <th>Sepal.Length<\/th>\n <th>Sepal.Width<\/th>\n <th>Petal.Length<\/th>\n <th>Petal.Width<\/th>\n <th>Species<\/th>\n <\/tr>\n <\/thead>\n<\/table>","options":{"columnDefs":[{"className":"dt-right","targets":[1,2,3,4]},{"orderable":false,"targets":0}],"order":[],"autoWidth":false,"orderClasses":false}},"evals":[],"jsHooks":[]}</script> <script type="application/htmlwidget-sizing" data-for="htmlwidget-cd5f37d21433eb2088ae">{"viewer":{"width":450,"height":350,"padding":15,"fill":true},"browser":{"width":960,"height":500,"padding":40,"fill":false}}</script> </body> </html> with data and header being obviously variable depending on the data. Now if we inspect and edit the page while it is active by In Rstudio call DT::datatable(iris) to show the table Click the "show in new window" to open it in your preferred browser Right-click anywhere on the page and click "Inspect element (Q)" Right-click on "body" and click "Edit as HTML" Finally add the <details> / </details> at the start and end of <body> Then we can actually see that it works as we would expect (closed first): (open now): So this is a clear conclusion that "it is possible". The problem is extracting the code. Walking down into DT::datatable you will eventually find that it calls htmlwidgets:::print.html_widget to open the actual html-page. This lets us recreate a script and extract the actual html code used in the widget: #' Generate html and make dependencies available in directory for a DT::datatable #' #' #param x a data.frame or DT::datatable #' #param dir the (root) directory for the project/dependencies. See details #' #param background background for the html widget #' #param libdir directory to export dependencies to #' #' #details This function generates the html that is usually generated when #' printing DT::datatable, and exports dependencies to a given directory, making #' it useful for embedding the html into a markdown file or shiny script, either #' running and saving this in the pre-amble/header or interactively. "Dir" #' can be used to specify the project root, with "libdir" specifying the path #' relative from "dir" to place dependencies. Note that this likely enforces #' the html file to be placed in the project root, and not a sub-folder of the #' project. datatable_html <- function(x, dir = getwd(), background = "white", libdir = 'lib'){ if(is.data.frame(x)) x <- DT::datatable(x) #from htmlwidgets:::print.html_widgets x <- htmltools::as.tags(x, standalone = TRUE) #from htmltools::save_html (called by print.html_widgets x <- htmltools::renderTags(x) deps <- lapply(x$dependencies, function(dep) { dep <- htmltools::copyDependencyToDir(dep, libdir, FALSE) dep <- htmltools::makeDependencyRelative(dep, dir, FALSE) dep }) bodyBegin <- if (!isTRUE(grepl("<body\\b", x$html[1], ignore.case = TRUE))) { "<body>" } bodyEnd <- if (!is.null(bodyBegin)) { "</body>" } c("<!DOCTYPE html>", "<html>", "<head>", "<meta charset=\"utf-8\"/>", sprintf("<style>body{background-color:%s;}</style>", htmltools::htmlEscape(background)), htmltools::renderDependencies(deps, c("href", "file")), x$head, "</head>", bodyBegin, # <=== body starts here, maybe remove? x$html, bodyEnd, # <=== Body ends here, maybe remove? "</html>") } dt_html <- datatable_html(DT::datatable(iris)) # print (very large output): cat(dt_html) now dt_html contains the html segments in a vector, and the dependencies are copied to {dir}/{libdir} which should be a folder under the root of the markdown project. A few things to note: The html vector has the dependencies in dt_html[6] (may have to be included in the markdown pre-amble?) and the htmlscript itself is in dt_html[10] with body tags in dt_html[9] and dt_html[11] respectively. I am not skilled enough with html embedding in Rmarkdown to be sure where to go from here, but I am certain that there will be some efficionados out there that has the proficiency to abuse this and providing the final part of the answer. I am assuming a combination of document dependencies for the header and then using the html segment in dt_html somehow should do the job.
Out of the box, reactable will work with similar output and configurations. Within an Rmd document: <details><summary>Click to expand</summary> ```{r} library(reactable) reactable(mtcars) ``` </details> Which renders to:
This should work: # Section 3 ```{r} library(shiny) library(DT) # make sure you load DT *after* shiny # Render renderDataTable({ datatable(iris) %>% formatStyle( 'Sepal.Width', backgroundColor = styleInterval(3.4, c('gray', 'yellow')) ) }) ``` It really bugged me that I couldn't figure it out, so I googled a bit and this should help you: https://blog.rstudio.com/2015/06/24/dt-an-r-interface-to-the-datatables-library/ The results is:
Is there a way to use a font from my website inside an iframe
I have a website where I accept payments, it is built with react I am using a credit card processor who provides an iframe to embed in my website to capture the credit card securely the code looks like this. <iframe data-field-id="card-number" src='URL_TO_PROCESSOR_DOMAIN...'/><.iframe> Which displays an input for the credit card number on the page, they also provide a way to pass custom styling to the input like this. let style = { "border-color": "red", "font-size": "20px" ... } Then you call a function they provide setFieldStype("card-number", style) to apply the style to the generated input. However I am looking to also pass the font-family which I use to the input but it doesn't apply it, I tried it like this. let style = { "border-color": "red", "font-size": "20px" "font-family": "my custom font" } But it doesn't work since the iframe doesn't have access to my custom imported font. Is there a way I can accomplish to use a font imported by my website in an iframe?
This is only work with the appropriate CORS header is set. We can append a <link/> element to your iframe and pass the font file for it. const applyStyle = () => { const linkTag = ele.createElement("link"); linkTag.id = "custom-link"; linkTag.href = " link to font... /fonts/custom-font.css"; linkTag.rel = "stylesheet"; ele.head.appendChild(linkTag); }
implementing font awesome in json
I'm having a little bit of trouble trying to show some font awesome icons. I have some text that is written in json through a rakefile, and i am trying to incorporate a font awesome icon within the text, but having troubles because the text is in json, I was wondering if someone could take a quick look at it and help me out. The icon is not appearing, however within the dev tools, there is extra blank space where it should be appearing. I am using haml, with angular on the front end. I don't think I am needed to show the controller for this. I also, did sub out what i really have with deez nuts. The area in my rakefile is so { "partial":"message", "params":{ "primary": true, "icon": "<i class='fa fa-odnoklassniki'></i>", "body":" With Deez Nuts as your president, you can have the full assurance of knowing that your country will be awesome " } }, and the haml file that should render this is here %div{ 'ng-if' => 'option.params.primary' && 'option.params.icon'} %div{ 'ng-include' => "'/assets/ng/features/politicians/satire/presidents.html'" }
Your html is probably stripped by angular. Inspect the output to make sure. You can add this filter to your app to render trusted html: .filter('trustedHtml', ['$sce', function($sce){ return function(text) { return $sce.trustAsHtml(text); }; }]) and than you can apply it to any data: {{foo | trustedHtml}}
Base64 Encoding for CSV with R Markdown
Base64 encoding of images is a really cool little feature in R Studio Markdown that creates all in one HTML pages that are easy to distribute or share. No need to worry about having the image as a seperate file. The browser knows what to do with it. I'd like to extend this functionality to encoding CSV files as well. Looking at how they are doing it now, it looks like they are passing the information to .Call and using C/C++ to encode the file information. From (line 177, and 192) : https://github.com/rstudio/markdown/blob/master/R/renderMarkdown.R .b64EncodeFile <- function(inFile) { fileSize <- file.info(inFile)$size if (fileSize > 0){ paste( "data:", .mimeType(inFile),";base64,", .Call(rmd_b64encode_data,readBin(inFile,'raw',n=fileSize)), sep='') } else { warning(inFile,'is empty!') inFile } } .b64EncodeImages <- function(html) { reg <- "<\\s*[Ii][Mm][Gg]\\s+[Ss][Rr][Cc]\\s*=\\s*[\"']([^\"']+)[\"']" m <- gregexpr(reg,html,perl=TRUE) if (m[[1]][1] != -1) { .b64EncodeImgSrc <- function(imgSrc) { inFile <- sub(reg,"\\1",imgSrc) if (length(inFile) && file.exists(inFile)) imgSrc <- sub(inFile,.b64EncodeFile(inFile),imgSrc,fixed=TRUE) imgSrc } regmatches(html,m) <- list(unlist(lapply(regmatches(html,m)[[1]],.b64EncodeImgSrc))) } html } Now, how do I accomplish the same thing, with a CSV file? Importantly, how do I get the browser to understand it.
If I read your intentions right, if you make an A element with the encoded data in the HREF attribute, then clicking on the link will get the file. Just tested this with an encoded image block I had lying around: Click Me So as long as you set the mime type (text/something?) all you need do is construct that element and stick it in your HTML file. User clicks link, file download starts, from its embedded base64 encoding. Sorted. Complete example with a missing mime-type which just lets the browser read it: <html> <head> </head> <body> <h1>Test</h1> click me </body> </html> Where that data string has come from: > markdown:::.b64EncodeFile("test.csv") [1] "data:;base64,aWQsbmFtZSxhZ2UKMSwiRnJlZCBGb28iLDk5CjIsIkpvZSBCbG9nZ3MiLDIyCg=="
Retrieve Image Source with XPath in R using XML-Package
I'd like to retrieve the image source within the below HTML code block, but can't find the right syntax. library(XML) library(RCurl) script <- getURL("http://www.floraweb.de/pflanzenarten/druck.xsql?suchnr=4346") (doc <- htmlParse(script)) <div class="divider"><hr></div> <div id="contentblock"><div id="content"> <h1>Alle Angaben</h1> <p>Zu der von Ihnen gewählten Pflanzenart liegen folgende Informationen vor:</p> <p>Wissenschaftlicher Name: Poa badensis agg. </p> <p>Deutscher Name: Artengruppe Badener Rispengras</p> <p>Familienzugehörigkeit: Poaceae, Süßgräser</p> <p>Status: keine Angaben </p> <p class="centeredcontent"><img border="0" src="../bilder/Arten/dummy.tmb.jpg"></p> Desired result: "../bilder/Arten/dummy.tmb.jpg" Any pointers are greatly appreciated!
Try the following: script <- getURL("http://www.floraweb.de/pflanzenarten/druck.xsql?suchnr=4346") doc <- htmlTreeParse(script,useInternalNodes=T) img<-xpathSApply(doc,'//*/p[#class="centeredcontent"]/img',xmlAttrs) > img[2] [1] "../bilder/Arten/dummy.tmb.jpg" The use of Internal representation maybe necessary EDIT: I just looked up htmlParse and its equivalent to htmlTreeParse(useInternalNodes=T) #Martin Morgan thanks have added below doc <- htmlParse("http://www.floraweb.de/pflanzenarten/druck.xsql?suchnr=4346") xpathSApply(doc, '//*/p[#class="centeredcontent"]/img/#src')
Use: //div[#id='contentblock'] /div/p[#class='centeredcontent'] /img/#src This selects the src attribute of any p element whose class attribute has the value "centeredcontent"and that (the p element) is a child of a div that is a child of a div whose id attribute has the value '"contentblock"'. If you want to get directly the value of this attribute, use: string(//div[#id='contentblock'] /div/p[#class='centeredcontent'] /img/#src)