appsilon / shiny.router Goto Github PK
View Code? Open in Web Editor NEWA minimalistic router for your Shiny apps.
Home Page: http://appsilon.github.io/shiny.router
License: Other
A minimalistic router for your Shiny apps.
Home Page: http://appsilon.github.io/shiny.router
License: Other
An important use case arises when you start with a set of html pages (e.g. a commercial theme for a cms, blog, shop, etc). As I see it, it would be preferable to use these html templates directly without rewriting the whole thing using tags$ wrappers. Any idea if the htmlTemplate approach is fundamentally incompatible with shiny.router, or is it possible for this to work?
Thanks again for your great effort on shiny.router and it worked perfect on our application. But we came across a problem after upgrading to R3.5.0. We also tested the following sample application and we observe the same issue.
Source code:
library(shiny)
#devtools::install_github("Appsilon/shiny.router")
library(shiny.router)
# This generates menu in user interface with links.
menu <- (
tags$ul(
tags$li(a(class = "item", href = "/", "Page")),
tags$li(a(class = "item", href = "/other", "Other"))
)
)
# This creates UI for each page.
page <- function(title, content) {
div(
menu,
titlePanel(title),
p(content)
)
}
# Both sample pages.
root_page <- page("Home page", "Welcome on sample routing page!")
other_page <- page("Some other page", "Lorem ipsum dolor sit amet")
# Creates router. We provide routing path and UI for this page.
router <- make_router(
route("/", root_page),
route("/other", other_page)
)
# Creat output for our router in main UI of Shiny app.
ui <- shinyUI(fluidPage(
router_ui()
))
# Plug router into Shiny server.
server <- shinyServer(function(input, output) {
router(input, output)
})
# Run server in a standard way.
shinyApp(ui, server)
R version: 3.5.0 or higher
Issues:
When we click the "Other", it shows the following error:
BTW, do you have plan to push it to CRAN?
Hello!
Lovely package! I am wondering if anyone else had encountered issues with internal links: https://way2tutorial.com/html/html_internal_links.php.
For example, while I am on page One, found at route /#!/page_one
I would like to click on a link which takes me to a different section of the same page.
<a href="/#!/page_one#anchorid">Go to section B</a>
<a id="anchorid">Section B</a>
This isn't working for me, however. Any thoughts on how to approach linking to different sections on the same page?
Thank you so much!
Jessica
hi there,
I have a reasonably large app using shiny.fluent and shiny.router (thanks for these packages they are really great), with a structure similar to the code below:
library(shiny)
library(shiny.fluent)
library(shiny.router)
# navigation pane objects
navigation_styles <- list(
root = list(height = "100%", width = "30%", boxSizing = "border-box",
border = "1px solid #eee", overflowY = "auto"))
link_groups <- list(
list(
links = list(
list(name = 'Home', url = '#!/', key = 'home'),
list(name = "Documents", key = "documents", url = '#!/documents'),
list(name = "Pages", key = "pages", url = '#!/pages')
)))
# page UIs
home_page_ui <- tagList(p("Homepage"))
documents_page_ui <- function(id){
ns <- NS(id)
tagList(textOutput(ns("title")))
}
pages_page_ui <- function(id){
ns <- NS(id)
tagList(textOutput(ns("title")))
}
# page servers
documents_page_server <- function(id = "documents1"){
moduleServer( id, function(input, output, session){
ns <- session$ns
output$title <- renderText("Documents Page")
})
}
pages_page_server <- function(id = "pages1"){
moduleServer( id, function(input, output, session){
ns <- session$ns
output$title <- renderText("Pages Page")
})
}
# Creates router. We provide routing path, a UI as
# well as a server-side callback for each page.
router <- make_router(
route("/", home_page_ui),
route("documents", documents_page_ui("documents1")),
route("pages", pages_page_ui("pages1"))
)
shinyApp(
ui = fluidPage(
Nav(
groups = link_groups,
selectedKey = "home",
styles = navigation_styles
),
fluidPage(
router$ui
)
),
server = function(input, output, session) {
router$server(input, output, session)
# page servers
documents_page_server("documents1")
pages_page_server("pages1")
}
)
Each of my pages are inside modules because they are quite similar and it helps me avoid namespace conflicts.
Notice that my page servers are just in the main server function, not in the router.
This works okay, but suffers from the issue of every page reloading when something changes, not just the current page ( #70). From what I understand from reading other issues, this issue has been fixed in the current version of shiny.router, but requires calling the page server from the router - ie. using route(path, ui, server = ACTUAL_SERVER_FUNCTION)
. Is that correct?
I can only get that to work if the pages are not modules that use moduleServer(). Is there any way to get route
to work with modules? And is there any way to pass parameters (e.g. a set of reactive values) to those server modules if it does work?
Cheers
There is need to pass GET parameters as lists.
For example shiny::parseQueryString allows parsing links as:
?foo[1]=3&foo[2]=2&val=bar
returning list with two objects: foo
and val
.
Hi! I'm not really sure where to post this as it's not quite an issue. Rather, it's an initial conversation to see if you might be interested in / have ideas for how one might build an open-source, markdown-based survey platform using {shiny} (and possibly RMarkdown / Quarto). I've had this idea in mind for years (see this blog post), but I've struggled to find an architecture that achieves the end goal. So I'm posting this in case anyone in the Appsilon team might be interested in the idea as I think a lot of your packages could be key components of making this work, and I figured you all probably would have the best ideas of how to set something like this up.
For example, I never knew about {shiny.router}, and having just discovered it I feel like this could be a key component for controlling navigation through a survey, with questions set on different pages. And packages like {rhino} are probably also super useful for something like this. Apologies that this isn't quite an issue - I just didn't know how else to reach out to the Appsilon team.
My end goal is to build a package such that users can define their survey content using mostly just markdown and code chunks. That is, if the goal is to design a survey, then all we really need is to define the UI. The server code could be generated with some logic based on what's in the UI as we're not using {shiny} in a conventional dashboard way but rather simply for data collection.
For example, in this discussion I describe an example of how I would want a simple survey to be able to be defined, something like this:
---
name: "surveydown demo"
author: "John Paul Helveston"
format: html
self-contained: true
execute:
echo: false
---
```{r}
#| include: false
library(surveydown)
```
# Welcome!
This is a demo survey! Click the button below to go to the next page.
---
This is a screener.
You must choose "Red" in the question below, otherwise you will be screened out (meaning you will be sent to the end of the survey)
```{r}
question(
name = 'color',
type = 'mc',
required = TRUE,
label = "Do you want to take the red pill or the blue pill?",
option = c('Red', 'Blue')
)
```
---
Here are some more questions:
```{r}
question(
name = 'animal',
type = 'text',
required = FALSE,
label = "What's your favorite animal?"
)
question(
name = 'education',
type = 'select',
required = TRUE,
label = "What is the highest level of education you have attained?",
option = c(
"Did not attend high school" = "hs_no",
"Some high school" = "hs_some",
"High school graduate" = "hs_grad",
"Some college" = "college_some",
"College" = "college_grad",
"Graduate Work" = "grad",
"Prefer not to say" = "no_response"
)
)
```
---
# Fine
Thank you for taking our survey!
There is no {surveydown} package (yet), but that's the idea. Define a survey in markdown, then render it to a shiny app. The data would be saved to any database - I'm thinking of just saving to a Google sheet for now. But if you defined all of the UI content in a .Rmd or .qmd file, we should be able to define some functions that generate the appropriate UI and server code for a shiny app that simply writes user responses to a database.
Anyway, if this sounds interesting I would appreciate any thoughts / suggestions you have.
Hi,
It seems that the argument 'position' of the multiple_radio
function does not have any effect when set to 'inline'.
Using the example provided in the package manual:
if (interactive()) {
# Checkbox
library(shiny)
library(shiny.semantic)
ui <- function() {
shinyUI(
semanticPage(
title = "Checkbox example",
h1("Radioboxes"),
multiple_radio("radioboxes", "Select Letter", LETTERS[1:6], value = "A", position = "inline"),
p("Selected letter:"),
textOutput("selected_letter")
)
)
}
server <- shinyServer(function(input, output) {
output$selected_letter <- renderText(input$radioboxes)
})
shinyApp(ui = ui(), server = server)
}
Looks like the problem is present with shiny.semantic slider_input as well as standard shiny sliderInput.
server <- shinyServer(function(input, output) {
router(input, output) slider_ui <- sliderInput("slider", "slider_label", 0 , 10 , 3, 1, 3)
output$slider <- renderUI(slider_ui)
}
Above makes whole ui refresh on routing ( even elements outside router_ui()
)
I've checked some additional components (numericInput, semantic date_input, actionButton) and it is working fine with them.
Prerequisites
Package version: 0.1.1
Shiny version: 1.3.2
System: Windows 7, google chrome Version 75.0.3770.100
Description
When I return to a previous page either by clicking on the browser´s back button or with the change_page()
function and then move again to the current page, observeEvent and eventReactive functions are triggered an additional time.
I include a minimal example, which consists of an example from the shiny.router tutorial page, that I extended to include a selectInput
control. If you go back to the first page and then to the side page again and select an element from the selectInput control, the observeEvent function gets triggered an additional time. If you do this again, the function get triggered three times, and so on.
library(shiny)
library(shiny.router)
# This generates menu in user interface with links.
menu <- (
tags$ul(
tags$li(a(class = "item", href = route_link("home"), "Home page")),
tags$li(a(class = "item", href = route_link("side"), "Side page"))
)
)
# This creates UI for each page.
page <- function(title, content) {
div(
menu,
titlePanel(title),
p(content),
actionButton("switch_page", "Click to switch page!")
)
}
my_page<-function(){
div(
uiOutput("my_dropdown_list")
)}
# Both sample pages.
home_page <- page("Home page", uiOutput("current_page"))
side_page <- my_page()
my_page_server <- function(input, output, session) {
observeEvent(input$my_dropdown_list,{
cat(paste0("selected element ", input$my_dropdown_list, "\n"))
}, ignoreInit=TRUE, ignoreNULL=TRUE)
output$my_dropdown_list <- renderUI({
selectInput("my_dropdown_list","Choices:",
choices= c("A","B", "C"),
multiple=FALSE,
selectize=TRUE
)
})
}
# Creates router. We provide routing path, a UI as
# well as a server-side callback for each page.
router <- make_router(
route("home", home_page, NA),
route("side", side_page, my_page_server)
)
# Create output for our router in main UI of Shiny app.
ui <- shinyUI(fluidPage(
router_ui()
))
# Plug router into Shiny server.
server <- shinyServer(function(input, output, session) {
router(input, output, session)
output$current_page <- renderText({
page <- get_page(session)
sprintf("Welcome on %s page!", page)
})
observeEvent(input$switch_page, {
if (is_page("home")) {
change_page("side")
} else if (is_page("side")) {
change_page("home")
}
})
})
# Run server in a standard way.
shinyApp(ui, server)
We have a workaround using UIOutput and renderUI combined with ssession$clientData we can call the module and only transfer the content that is required across to the client based on the url (this is in standard shiny using tabs).
Below I have implemented the same approach / workaround using shiny.router:
`
library(shiny)
library(shiny.router)
menu <- tags$ul(
tags$li(a(class = "item", href = route_link("/"), "Page")),
tags$li(a(class = "item", href = route_link("second"), "Second page")),
tags$li(a(class = "item", href = route_link("third"), "A third page"))
)
page <- function(title, content, id) {
ns <- NS(id)
div(
titlePanel(title),
p(content),
textOutput(ns("click_me"))
)
}
root_page <- page("Home page", "Converted number of clicks", "root")
second_page <- page("Second page", "Converted number of clicks", "second")
third_page <- page("Third page", "Converted number of clicks", "third")
server_module <- function(id, clicks, power = 1) {
moduleServer(id, function(input, output, session) {
output$click_me <- renderText({
as.numeric(clicks())^power
})
})
}
ui <- fluidPage(
menu,
actionButton("clicks", "Click me!"),
router_ui(
route("/", uiOutput("root_out")),
route("second", uiOutput("second_out")),
route("third", uiOutput("third_out"))
)
)
server <- function(input, output, session) {
router_server()
clicks <- reactive({
input$clicks
})
observe({
if (session$clientData$url_hash == "#!/") {
output$root_out <- renderUI(root_page)
server_module("second", clicks = clicks, power = 2)
}
if (session$clientData$url_hash == "#!/second") {
output$ui <- renderUI(second_page)
server_module("second", clicks = clicks, power = 2)
}
if (session$clientData$url_hash == "#!/third") {
output$ui <- renderUI(third_page)
server_module("second", clicks = clicks, power = 2)
}
})
server_module("root", clicks = clicks)
server_module("third", clicks = clicks, power = 3)
}
shinyApp(ui, server)
`
From the screenshot you can see that only the root_page content was transferred to the client.
This is really useful when you are dealing with large shiny apps that cater for different users and scale well because sessions only deal with the content that they require and not the full app. In certain instances the user rights restrict them to not use certain parts of the app but with the current way shiny.router works, they will still sacrifice load time based on the app and not the session.
Open to discussions about this if you are interested.
NO
Only transfer html for the current route.
Brochure is working in the desired way but only works with Golem. We would like to migrate our app to Rhino but this is preventing us from doing that.
It seems that shiny.router
package does not work when an app is deployed on RSConnect.
Using the code provided in the example semanticui
app, the URL of the pages remain the same.
Any workaround maybe?
Is it possible to disable routing when the shiny app is launched in an iframe? In this case, the window.location variables are wrong (I guess).
The fix in #57 does not fix my use case, which is running shiny.router in combination with shinyproxy.
The reason for continuing to use shiny.router (even though the differentiated http links cannot be seen from an embedded iframe) is because shiny.router is excellent for managing apps with a complicated navigation setup.
also, will this impact the bookmarkstate feature offered by Shiny?
Working perfectly in my local environment, however, the application URL will be stripped out.
For example, the initial FULL URL on shinyapps.io:
https://my_org.shinyapps.io/my_application_name_on_shinyapps.io/
Once I click one link to another page, it becomes:
https://my_org.shinyapps.io/#!/explore-indicators
The "my_application_name_on_shinyapps.io" is gone!!!
I deployed the sample application @https://appsilon.com/shiny-router-package/ on an nginx server with the following path:
https://www.rooturl.com/shinyproxy, when I click the "home" or "side", the root url (www.rooturl.com) is maintained but the path "shinyproxy" is disappeared. This only happens on the route_link. The "change_page" is still working as expected.
library(shiny)
library(shiny.router)
# This generates menu in user interface with links.
menu <- (
tags$ul(
tags$li(a(class = "item", href = route_link("home"), "Home page")),
tags$li(a(class = "item", href = route_link("side"), "Side page"))
)
)
# This creates UI for each page.
page <- function(title, content) {
div(
menu,
titlePanel(title),
p(content),
actionButton("switch_page", "Click to switch page!")
)
}
# Both sample pages.
home_page <- page("Home page", uiOutput("current_page"))
side_page <- page("Side page", uiOutput("current_page"))
# Creates router. We provide routing path, a UI as
# well as a server-side callback for each page.
router <- make_router(
route("home", home_page, NA),
route("side", side_page, NA)
)
# Create output for our router in main UI of Shiny app.
ui <- shinyUI(fluidPage(
router_ui()
))
# Plug router into Shiny server.
server <- shinyServer(function(input, output, session) {
router(input, output, session)
output$current_page <- renderText({
page <- get_page(session)
sprintf("Welcome on %s page!", page)
})
observeEvent(input$switch_page, {
if (is_page("home")) {
change_page("side")
} else if (is_page("side")) {
change_page("home")
}
})
})
# Run server in a standard way.
shinyApp(ui, server)
0.3.0
No response
No response
404 page does not work when a user opens a non-valid link without going to a valid one first
#Shiny Router 0.3.0 ----------
options(shiny.port = 3333)
install.packages("[email protected]") #Restart session if needed
library(shiny.router)
library(shiny)
router <- router_ui(
route("/", shiny::tags$div(shiny::tags$span("Hello world"))),
route("/main", shiny::tags$div(h1("Main page"), p("Lorem ipsum."))),
page_404 = page404(tags$div(tags$h1("This is the 404 Page")))
)
shinyApp(
router,
function(input, output, session) {router_server()}
)
# 1. Visit http://localhost:3333/#!/non-existing-page directly (without ever visiting homepage) from your browser
# 2. Visit http://localhost:3333/#!/ first and then visit http://localhost:3333/#!/non-existing-page
404 page is presented when a user opens a non-valid link.
No response
No response
No response
Only with the current master version of shiny.router
.
A bit puzzling error to me:
library(shiny)
library(shiny.router)
library(shiny.semantic)
tabs <- tabset(id = "tab1", list(list(menu = div("First link"),
content = div("First content")),
list(menu = div("Second link"),
content = div("Second content")),
list(menu = div("Third link"),
ontent = div("Third content"))))
router <- make_router(
route("index", tabs),
route("second", h1('second'))
)
ui <- semanticPage(
horizontal_menu(
list(
list(name = "One", link = route_link("index"), icon = "dog"),
list(name = "Two", link = route_link("second"), icon = "cat")
),
),
router_ui()
)
server <- function(input, output, session) {
router(input, output, session)
}
shinyApp(ui, server)
It looks like the last change broke that, as version of a router from CRAN (shiny.router_0.1.1
) works fine ( cc @krystian8207 )
Needed when wrong address given.
When clicking on a row, it is not highlighted.
if (interactive()){
library(shiny)
library(shiny.semantic)
ui <- semanticPage(
semantic_DTOutput("table"),
verbatimTextOutput("DT_selection")
)
server <- function(input, output, session) {
output$table <- DT::renderDataTable(
semantic_DT(iris)
)
output$DT_selection <- renderPrint({
print(input$table_rows_selected)
})
}
shinyApp(ui, server)
}
Also, when trying to change the table style (e.g. by adding style = 'default'
) , an error message appears.
Error: formal argument "style" matched by multiple actual arguments
Hello, thanks for your useful package!
I have run into an issue when it comes to changing pages. Here is a minimal example:
library(shiny)
library(shiny.router)
# This generates menu in user interface with links.
menu <- (
tags$ul(
tags$li(a(class = "item", href = route_link("home"), "Home page")),
tags$li(a(class = "item", href = route_link("side"), "Side page"))
)
)
# This creates UI for each page.
page <- function(title, content, button_name) {
div(
menu,
titlePanel(title),
p(content),
actionButton("switch_page", "Click to switch page!"),
actionButton(button_name, "Go!")
)
}
# Both sample pages.
home_page <- page("Home page", uiOutput("current_page"), "test1")
side_page <- page("Side page", uiOutput("current_page"), "test2")
home_server <- function(input, output, session) {
observeEvent(input$test1, {
print("Foo")
})
}
side_server <- function(input, output, session) {
observeEvent(input$test2, {
print("Bar")
})
}
# Creates router. We provide routing path, a UI as
# well as a server-side callback for each page.
router <- make_router(
route("home", ui = home_page, server = home_server),
route("side", ui = side_page, server = side_server)
)
# Create output for our router in main UI of Shiny app.
ui <- shinyUI(fluidPage(
router_ui()
))
# Plug router into Shiny server.
server <- shinyServer(function(input, output, session) {
router(input, output, session)
output$current_page <- renderText({
page <- get_page(session)
sprintf("Welcome on %s page!", page)
})
observeEvent(input$switch_page, {
if (is_page("home")) {
change_page("side")
} else if (is_page("side")) {
change_page("home")
}
})
})
# Run server in a standard way.
shinyApp(ui, server)
Expected behaviour:
Actual behaviour:
This seems to be related to issue #67. Any feedback is appreciated!
0.2.3
macOS 13.0.1
No response
Deployed a simple shiny app that uses shiny.router to display two pages. I deployed a docker container locally to test (because eventually want to deployed in Cloud Run). However, the app does not run.
docker build -t shinyrouter .
docker run -d -p 3838:3838 shinyrouter
Shiny app code:
library(shiny)
library(shiny.router)
library(DT)
# This is where the UI of page 1 is specified
home_page <- div(
titlePanel("Home page"),
p("This is the home page!"),
plotOutput("graph"),
fluidRow(
column(12,
align = "right",
actionButton("next_page", "Next")
)
)
)
# This is where the UI of page 2 is specified
another_page <- div(
titlePanel("Another page"),
p("This is the another page!"),
DTOutput("table"),
fluidRow(
column(12,
align = "right",
actionButton("prev_page", "Back to Main")
)
),
verbatimTextOutput("rows")
)
#this connects each page with an HTML address
router <- make_router(
route("/", home_page, NA),
route("another", another_page, NA)
)
# This is where it is all put together - only need to point to the router
ui <- fluidPage(
router$ui
)
server <- function(input, output, session) {
# important to add this line in the server function
router$server(input, output, session)
# When the button is clicked in page 1 go to page 2
observeEvent(input$next_page,
change_page("another")
)
# When the button is clicked on page 2 go back to page 1
observeEvent(input$prev_page,
change_page("/")
)
# other functions on the server side
output$graph <- renderPlot({
x <- runif(100)
y <- runif(100)
plot(x, y)
})
output$table <- renderDT({
iris
})
# get specific rows from the table and extract the specific value
output$rows = renderPrint({
s <- input$table_rows_selected
if (length(s)) {
print(iris[s, "Sepal.Width"], sep = '\n')
}
})
}
shinyApp(ui, server)
Dockerfile:
FROM rocker/shiny-verse:4.2.2
# system libraries of general use
RUN apt-get update && apt-get install -y \
libcurl4-gnutls-dev \
libssl-dev
RUN install2.r --deps TRUE --ncpus -1 shiny.router
# copy the app to the image
COPY app.R /srv/shiny-server/app.R
#RUN chmod +x /srv/shiny-server/shiny-server.sh
# select port
EXPOSE 3838
# allow permission
RUN sudo chown -R shiny:shiny /srv/shiny-server
# run app
CMD ["/usr/bin/shiny-server"]
No response
All of the examples use app.R and it's not clear how one could leverage shiny.router in an app design with ui.R and server.R especially with moduleServer components.
Not enough example material
More examples
DIY
If you have the development version of shiny (remotes::install_github("rstudio/shiny")
), then
shiny.router::disable_bootstrap_on_bookmark("test_bookmark")
results in the error
Error in `renderDependencies(resolveDependencies(list(func_bootstrapLib())),
srcType = "href")`: Dependency bootstrap 3.4.1 does not have a usable source
which is most likely a result of rstudio/shiny#3537
remotes::install_github("rstudio/shiny")
There is no possibility to send parameters via url on the main page.
Passing: app_url/#!/?val=1
redirects me to the main page app_url/#!/
without passing parameters to the app.
Could you validate this issue? Thanks a lot.
When app is run from docker, url is not copied correctly
I was very excited to discover shiny.router and incorporate it into my Shiny application. Managed to get it to work only to realise a caveat in my specific situation: I've built a Shiny app that uses a browser's cookie to determine the permissions a user has (allowing it to show certain tabs but not others). This functionality requires the tabs to be populated in server.R
, since the cookie must be passed using Shiny's input$
. However, make_router()
must be ran in Global.R
to be available to ui.R
.
Initially I fixed this by only displaying the buttons/links for the tabs the user was allowed to see, but then noticed that the way router
works is that it populates the entire ui, then toggles hide/show depending on the URL we're on (clever!). However, in my example this means the user can see tabs they shouldn't be able to see by simply going into their browser's console.
Another solution would be to dynamically populate make_router()
, but that's not possible since the permissions aren't known yet to Global.R, since it's ran before server.R.
It'd be great if 'make_router()' could somehow support a dynamic inclusion/exclusion (ran from Server.R). Might be quite a stretch, but thought I'd highlight it just in case!
Thanks for all the great work
Currently, when I click a link in the app and then go back to the previous page, the tables on it appear to be cached from when I was on that page previously. After a moment, the table will flash and reload. Initially I thought this might be due to some reactive dependencies, but even when I removed all dependencies and just loaded the table by itself, the behavior persisted. I would like to devise a solution that either clears that cached version of the table so that there is no double loading, or prevent the reloading if the cached table hasn't changed. Is there a known solution to this problem?
Shiny: 1.4.0.2
shiny.router: 0.1.1
R: 3.6.1
I've included code to reproduce this problem. To see the double load issue, run the app and wait for the table to load. Then click the link to the second page, and click the back button in the browser. You should see the table from before, then after 1 second, there should be a flash as the table is reloaded. I tested this on both Chrome and Firefox and was able to get the same results in both browsers.
library(shiny)
library(shiny.router)
# This generates menu in user interface with links.
menu <- (
tags$ul(
tags$li(a(class = "item", href = route_link("/"), "First page")),
tags$li(a(class = "item", href = route_link("second"), "Second page"))
)
)
# This creates UI for each page.
page <- function(title, content = "") {
div(
menu,
titlePanel(title),
p(content)
)
}
# Both sample pages.
root_page <- page("Home page", tagList(DT::dataTableOutput("table")))
other_page <- page("Second page")
# callbacks
root_callback <- function(input, output, session) {
output$table = DT::renderDataTable({
Sys.sleep(1)
DT::datatable(EuStockMarkets)
})
}
# Creates router. We provide routing path, a UI as
# well as a server-side callback for each page.
router <- make_router(
route("/", root_page, root_callback),
route("second", other_page)
)
# Creat output for our router in main UI of Shiny app.
ui <- shinyUI(fluidPage(
router_ui()
))
# Plug router into Shiny server.
server <- shinyServer(function(input, output, session) {
router(input, output, session)
})
# Run server in a standard way.
shinyApp(ui, server)
The package uses lots of shiny function recalls with shiny::
. We should import shiny within NAMESPACE and use raw shiny-function names.
I would like to use "shiny.router" to link URL address with "tabItem" of "dashboardBody" when using "shinydashboard" package. It will be great to see some examples how to do it. Thanks :)
Is it possible to set up a webhook so we can receive POST requests to specific routes?
So let's say that I have a shiny app deployed on https://itsme.shinyapps.io/myapp
I want to set up a webhook so the app 'wakes up' and the server does 'something' if a user sends a POST request with a json file to - let's say - https://itsme.shinyapps.io/myapp/myfeature
How would be the recommended way of addressing this with shiny.router
? Is it even possible?
Thank you! (I'm aware the question is a bit vague, let me know if you need more clarification)
Package version: latest github version (0.1.1) with shiny 1.3.1
System Details: Windows 10
Running platform: clean R session, testing app in Google chrome
Modifying query params ends Shiny session.
Our use case is that user authentication is done with reactiveValues in Shiny session and all the info is lost when we use links with query params.
Expected behavior:
Going from app_url/?foo=bar/#!/route to app_url/?foo=baz/#!/route should not end Shiny session.
Actual behavior:
When changing query strings, e.g. going from app_url/?foo=bar/#!/route to app_url/?foo=baz/#!/route the Shiny session ends (see MWE below).
Possible fix:
We worked around this by modifying parse_url_path in R/pages.R to support query strings of the form app_url/#!/route?foo=bar and the session apperas to be maintained between router calls.
MWE to reproduce hosted here: https://github.com/piotrbajger/query.mwe.
Run the app with RScript runapp.R, click on the links and inspect the console output to see that the session ends.
Is it possible to use shiny.router with separate ui and server files? I could not figure out how you would get the scoping right on that. Defining the router object in UI would make it not accessible in server and global.R did not seem to help that either.
When passing <path>
for main page other than "/" I expect that default url after app run is:
<app-url>/#!/<path>
. Currently <path>
displayed on url is always <app-url>/#!/
.
Anytime I source a .R page that I have created and then Run the app, the App will load
but only with have the screen visible and the word "TRUE" at the bottom left side. How
do I fix this annoyance. I have uploaded some minimal R code.
library(shiny)
#devtools::install_github("Appsilon/shiny.router")
library(shiny.router)
page2 <- source("/SamplingProject/7a_Create_Strata_Assing_Default_Single_Stratum.R")
router <- make_router(
route("/page2", page2)
)
ui <- shinyUI(fluidPage(
router_ui()
))
server <- shinyServer(function(input, output) {
router(input, output)
})
shinyApp(ui, server)
Hi, I just recently updated to shiny.router 0.2.2 and noticed that for some dashboards where data fetching takes 10-15 seconds behavior has changed. Previously I would see the root page while the data loads, but now I get the 404 page while data loads.
I modified the basic app.R example code in order to create a reproducible example, with the following two changes:
# Callbacks on the server side for
# the sample pages
root_callback <- function(input, output, session) {
ds <- reactive({
# Adding a 5 second wait
Sys.sleep(5)
data.frame(x = c(1, 2), y = c(3, 4))
})
output$table_one <- renderDataTable({
ds()
})
}
# Creates router. We provide routing path, a UI as
# well as a server-side callback for each page.
router <- make_router(
route("/", root_page, root_callback),
route("other", other_page, other_callback),
route("third", third_page, NA),
page_404 = page404("You opened non existing bookmark!")
)
Now when the app starts off, the 404 page displays for 5 seconds, then the root page appears. In the previous version the root page would show while the root_callback finishes.
Is this expected, or is there a way to get the previous behavior back?
Session info attached:
sessionInfo()
R version 3.6.3 (2020-02-29)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19042)
Matrix products: default
locale:
[1] LC_COLLATE=English_United States.1252 LC_CTYPE=English_United States.1252 LC_MONETARY=English_United States.1252
[4] LC_NUMERIC=C LC_TIME=English_United States.1252
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] shiny.router_0.2.2 shiny_1.5.0
loaded via a namespace (and not attached):
[1] Rcpp_1.0.4.6 digest_0.6.25 withr_2.2.0 later_1.0.0 mime_0.9 R6_2.4.1 xtable_1.8-4 jsonlite_1.6.1
[9] magrittr_1.5 rlang_0.4.11 promises_1.1.0 tools_3.6.3 httpuv_1.5.2 fastmap_1.0.1 compiler_3.6.3 htmltools_0.5.0
Currently there is no other option to name the main page other than "/".
Please check semanticui
example.
I am trying to use R/Shiny router to link .R pages that I have already coded but don't know how to get the router to work with my HTML links. I have a custom header Home.html, that I want to link to screen1.R using shiny router. Can someone help with this?
Home.html
<!DOCTYPE HTML>
<html>
<head>
<title>PPI Sampling System</title>
<style type="text/css">
body{background-color:white;}
#PPI-title{
font: 2em times, sans-serif;
color: white;
text-shadow: 1px 1px black;
}
.header{
padding: 0px 0px 0px 15px;
background: linear-gradient(#b4c9dd, white);
border: 1px solid black;
}
#nav-one{
background-color:white;
color: black;
font: 14 px arial, sans-serif;
border: 1px solid black;
width: 100%
}
#nav-two{
background-color: white;
font: 14px verdana;
font-weight: bold;
border: 1px solid black;
top-border: 0px;
color: blue;
width: 100%
}
#nav-three{
background-color: #b4c9dd;
border: 1px solid black;
font: 14px arial, sans-serif;
font-weight: regular;
width: 100%
}
.dropbtn1 {
background-color: transparent;
color: black;
font: 14px arial, sans-serif;
font-weight: bold;
border: none;
cursor: pointer;
display: inline-block;
z-index:1000;
}
.dropbtn2 {
background-color: #ffffff;
color: black;
font: 14px arial, sans-serif;
font-weight: bold;
border: none;
cursor: pointer;
display: inline-block;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
border-style: solid;
border-width: 1px;
min-width: 160px;
}
.dropdown-content a {
color: black;
font: 12px arial, sans-serif;
text-decoration: none;
display: block;
font-weight:regular;
padding: 2px;
z-index:1;
}
.dropdown a:hover {background-color: #a4a4a4}
.dropdown:hover .dropdown-content {display: block;}
.show {display:block;}
#first-box{
border-style: solid;
border-width: 1px;
}
.panes-container {
display: flex;
width: 100%;
overflow: hidden;
}
.left-pane {
width: 45%;
border-style: solid;
border-width: 1px;
}
.panes-separator {
width: 1px;
background: black;
position: relative;
cursor: col-resize;
}
.right-pane {
flex: auto;
border-style: solid;
border-width: 1px;
}
.panes-container,
.panes-separator,
.left-pane,
.right-pane {
margin: 5px;
padding: 5px;
}
.blue { color: blue; }
.tiny-button{
background-color: transparent;
color: black;
font: 12px arial, sans-serif;
font-weight: bold;
border: none;
cursor: pointer;
display: inline-block;
width:10px;
height:15px;
}
.white-button{
background-color: #ffffff;
color: black;
font: 12px arial, sans-serif;
border: none;
cursor: pointer;
display: inline-block;
}
.button-style{
background-color: #ffffff;
color: black;
border-style:solid;
font: 12px arial, sans-serif;
cursor: pointer;
display: inline-block;
padding:4px;
box-shadow: 0 1 2 0 black;
}
body {
font-size:12px;
}
.tit {
font-size:3em;
}
.sub {
font-size:2em;
}
.cont {
font-size:1em;
}
.button {
position:relative;
top:0;
font-size:15px;
cursor: pointer;
}
</style>
</head>
<!--Same as Previous Cycle Mockup 1, attempting to add split screen-->
<body onload="script();">
<!-- header of site -->
<div id="colorchange" class="header">
<div id="PPI-title">
<img src="http://www.conferenceharvester.com/uploads/harvester/photos/-ExhibitorLogo-45716.png" width="75" style="display:inline-block;">
<!-- image source: http://www.conferenceharvester.com/uploads/harvester/photos/-ExhibitorLogo-45716.png -->
<h2 id="header" style="display:inline-block; padding:0 0 0 10px;">PPI: Sampling System</h2>
</div>
<!-- Table for username, database, role-->
<table style="position:absolute; right:250px; top:20px;font:12px arial, sans-serif;" cellspacing="5px">
<tr>
<td><b>User:</b> Doe_j</td>
<td><b>DB:</b> ips-udb-samp</td>
</tr>
<tr>
<td><b>Role:</b> IA</td>
</tr>
</table >
<!-- Table for changing font size/header color -->
<table style="position:absolute; right:20px; top:20px; font:12px arial, sans-serif;">
<tr>
<td><b> Font:
<div class="button" style="display:inline-block; font:12px arial;"> <a class="inc" > + </a> </div>
<div class="button" style="display:inline-block; font:12px arial;"> <a class="dec" > - </a> </div>
<div class="button" style="display:inline-block; font:12px arial;"> <a class="reset"> Reset </a> </b> </div>
</td>
</tr>
<tr>
<td><b>Color Scheme:</b>
<button class="tiny-button" style="background:linear-gradient(to right,white,#b4c9dd,black)" onclick="document.getElementById('colorchange').style.background = 'linear-gradient(#b4c9dd,white)'"></button>
<button class="tiny-button" style="background:linear-gradient(to right,white,#b4ddb5,black)" onclick="document.getElementById('colorchange').style.background = 'linear-gradient(#b4ddb5,white)'"></button>
<button class="tiny-button" style="background:linear-gradient(to right,white,#f5ce6c,black)" onclick="document.getElementById('colorchange').style.background = 'linear-gradient(#f5ce6c,white)'"></button>
</td>
<td>
<button class="tiny-button" style="background:linear-gradient(to right,white,#b4c9dd,black)" onclick="document.getElementById('nav-three').style.background = '#b4c9dd'"></button>
<button class="tiny-button" style="background:linear-gradient(to right,white,#b4ddb5,black)" onclick="document.getElementById('nav-three').style.background = '#b4ddb5'"></button>
<button class="tiny-button" style="background:linear-gradient(to right,white,#f5ce6c,black)" onclick="document.getElementById('nav-three').style.background = '#f5ce6c'"></button>
</td>
</tr>
</table>
</div>
<!-- first navigation bar -->
<div>
<table id="nav-one" cellspacing="10">
<tr>
<th> Home </th>
<th>
<div class="dropdown">
<button class="dropbtn2">Sampling Process</button>
<div class="dropdown-content">
<a href="#">Browse Frame</a>
<a href="#">Frame Refinement</a>
<a href="#">Approve Sample Parameters</a>
<a href="#">Sample Refinement Round I</a>
<a href="#">Sample Refinement Round II</a>
<a href="#">Browse Sample</a>
</div>
</div>
</th>
<th> Tools</th>
<th> Help </th>
<th width="50%"></th>
<th><a style="font:12px arial, sans-serif; float:right;">Go To: <input type="text"></a></th>
<th><button class="white-button">Logout</button><th>
</tr>
</table>
</div>
<!--second naviagtion bar-->
<div>
<table id="nav-two" cellspacing="10">
<tr>
<th> Frame Refinement </th>
<th> Sample - 99 </th>
<th> Industry - 311311 </th>
<th> Cycle - F </th>
<th> Status Code - 2 </th>
<th width="30%"></th>
</tr>
</table>
</div>
<!--third navigation bar-->
<div class="ex1">
<table id="nav-three" cellspacing="10">
<tr>
<th> <a class="item1" href="#" target="main" >Search Frame </th>
<th> <a class="item2" href="#" target="main" > View Detail </th>
<!-- link not working -->
<th> <a class="item3" href="source(file://filer1/ppi/SamplingProject/2SampleSelection/screen1.R)" target="main" > Clustering </a> </th>
<th> <a class="item4" href="/page2" target="main"> Reports </a> </th>
<th> <a class="item5" href="" target="main" > View Changes </th>
<th> <a class="item6" href="#" target="main" > Search Universe </th>
<th>
<div class="dropdown">
<button class="dropbtn1"> Search Previous</button>
<div class="dropdown-content">
<a href="#" target="main"> Selected Industry</a>
<a href="#" target="main"> Any Industry</a>
</div>
</div>
</th>
<th> Industry Status </th>
<th width="25%"></th>
</tr>
</table>
</div>
<iframe name="main" src="http://www.yahoo.com" width="1570" height="560" scrolling="auto" > <p>Your browser does not support iframes.</p></iframe>
<script src="https://cdn.rawgit.com/lingtalfi/simpledrag/master/simpledrag.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
$(document).ready(function(){
var fontSize = parseInt($('body').css('font-size'),10);
$('.inc').on('click',function(){
fontSize++;
$('body').css('font-size',fontSize+'px');
})
$('.dec').on('click',function(){
fontSize--;
$('body').css('font-size',fontSize+'px');
})
$('.reset').on('click',function(){
$('body').css('font-size',"12px");
})
})
</script>
</body>
</html>
Router.R
library(shiny)
#devtools::install_github("Appsilon/shiny.router")
library(shiny.router)
Home <- includeHTML('//filer1/ppi/SamplingProject/Home.html')
router <- make_router(
route("/page2", page2))
ui <- shinyUI(fluidPage(
Home,
router_ui()
))
# Plug router into Shiny server.
server <- shinyServer( function(input, output) {
router(input,output)
})
# Run server in a standard way.
shinyApp(ui=ui, server=server)
screen1.R
library(shiny)
library(data.table)
library(DT)
library(dplyr)
library(shinyjs)
library(shinycssloaders)
# List of BLS 2017 Index items
codes <- read.csv("//filer1/ppi/SamplingProject/2017_index_items.csv", header = TRUE, sep = ",", fill=TRUE)
ui = fluidPage(
tags$div(class="flex-container",
# App title
titlePanel("Select NAICS code"),
sidebarLayout(
sidebarPanel(
# Drop down menu of NAICS codes. 337920 is a good example.
selectizeInput("industry", "Search", choices = codes, options = list(placeholder = "Search NAICS", onInitialize = I('function() { this.setValue(""); }')), width = "10em"),
h6("Click button below to Cluster by EIN"),
actionButton('action', 'Cluster by EIN')
),
# Display the datatable
mainPanel(DT::dataTableOutput('tbl1'), HTML(' ') ,DT::dataTableOutput('tbl2'), width = 9 )
)
)
)
#Start Server
server = function(input, output, session) {
# Universe file
universe <- fread("//filer1/ppi/SamplingProject/universe.csv", header = TRUE, sep = ",", fill = TRUE)
#Frame Universe
withProgress(message = "Reading file", {
frame_codes <- Reduce(function(x, y) full_join(x, y, by='NAICS'), list(codes,universe))
})
withProgress(message = "Reading file", {
Tail <- fread("//filer1/ppi/SamplingProject/Tail.csv", header = TRUE, sep = ",")
})
# Make a subset of the universe using the industry code that was inputted
datatableInput1 <- reactive(subset(frame_codes, NAICS == input$industry))
# Use the DataTables library and the buttons extension to create the datatable.
# Filter adds a search bar to each column. If the columns are numeric, its a range slider, needs to be characters to be a search bar.
# Add buttons and the ability to reorder the columns by drag and drop.
output$tbl1 = renderDataTable(
datatableInput1(),
filter = "top",
class = "cell-border stripe",
extensions = list("Buttons" = NULL, "ColReorder" = NULL),
options = list(dom = "Bfrt",
paging = FALSE,
scrollX = TRUE,
scrollY = "80vh",
colReorder = TRUE,
buttons = list(list(extend = "colvis", text = "Show/Hide Columns"), "copy", "csv", "print")),
server = TRUE
)
output$tbl2 <- DT::renderDataTable(
datatableInput2(),
filter = "top",
class = "cell-border stripe",
extensions = list("Buttons" = NULL, "ColReorder" = NULL),
options = list(dom = "Bfrt",
paging = FALSE,
scrollX = TRUE,
scrollY = "80vh",
colReorder = TRUE,
buttons = list(list(extend = "colvis", text = "Show/Hide Columns"), "copy", "csv", "print")),
server = TRUE
)
# Function to Cluster by EIN
datatableInput2 <- eventReactive(input$action, {
# Your arbitrary R code goes here
test<-subset(frame_codes[c("LDB_NUM", "EIN", "NAICS", "TOT_WAGES","TOT_EMP")], NAICS == input$industry )
#### Begin Clustering by EIN ####
newdata<-arrange(test, desc(TOT_EMP))
cluster <- arrange(newdata, EIN, desc(TOT_EMP), desc(TOT_WAGES))
# Seperate into Headers, Singles, and Members
myid.uni <- unique(cluster$EIN)
a<-length(myid.uni)
headers <- c()
members<-c()
singles<-c()
d<-c()
w<-c()
# Function to seperate the different Rec_Types
for (i in 1:a) {
temp<-subset(cluster, EIN==myid.uni[i])
if ( (dim(temp)[1] < 2 ) | myid.uni[i]==0 )
{
singles.temp<-temp
singles<-rbind(singles, singles.temp)
}
else
{
header.temp<-temp[1,]
headers<-rbind(headers, header.temp)
members.temp <-temp[1:dim(temp)[1],]
members<-rbind(members, members.temp)
d.temp<- sum(temp[1:dim(temp)[1],]$TOT_EMP)
d<-rbind(d,d.temp)
w.temp<- sum(temp[1:dim(temp)[1],]$TOT_WAGES)
w<-rbind(w,w.temp)
}
}
h<-headers[order(headers$EIN, -headers$TOT_EMP),]
m<-members[order(members$EIN, -members$TOT_EMP),]
s<-singles[order(singles$EIN, -singles$TOT_EMP, -singles$TOT_WAGES),]
h.chars<-replicate(dim(headers)[1],"H")
m.chars<-replicate(dim(members)[1],"M")
s.chars<-replicate(dim(singles)[1],"S")
# Make Headers with Total Header Employment
comb.headers<-data.frame(h,w,d,h.chars)
drops<-c("TOT_EMP", "TOT_WAGES")
comb.headers<-comb.headers[,!(names(comb.headers) %in% drops) ]
colnames(comb.headers)[4]<-"TOT_WAGES"
colnames(comb.headers)[5]<-"TOT_EMP"
colnames(comb.headers)[6]<-"Rec_Type"
# Make Members with their individual employment
comb.members<-data.frame(m,m.chars)
colnames(comb.members)[6]<-"Rec_Type"
# Make Singles with their individual employment
comb.singles<-data.frame(s,s.chars)
colnames(comb.singles)[6]<-"Rec_Type"
comb.singles<-arrange(comb.singles, desc(TOT_EMP), desc(TOT_WAGES))
hs.cluster<-rbind(comb.headers,comb.singles)
hs.cluster<-arrange(hs.cluster, desc(TOT_EMP))
# Add cluster IDs
# Actual clustering function
df<-hs.cluster
a<-dim(df)[1]
b<-dim(comb.headers)[1]
count<-1
temp4<-c()
for(i in 1:a)
{
temp.s<-c()
temp.h<-c()
temp2<-c()
temp3<-c()
if(grepl( 'S', df[i,6]))
{
temp.s<-rbind(temp.s,df[i,])
temp.s$Cluster_ID<-NA
temp.s<-temp.s[c(7,1:6)]
temp4<-rbind(temp4,temp.s)
}
else
{
temp.h<-df[i,]
temp2<-merge(temp.h, comb.members, by='EIN', all.x=TRUE)
temp2<-temp2[-c(2:6)]
colnames(temp2)[2]<-"LDB_NUM"
colnames(temp2)[3]<-"NAICS"
colnames(temp2)[4]<-"TOT_WAGES"
colnames(temp2)[5]<-"TOT_EMP"
colnames(temp2)[6]<-"Rec_Type"
temp2<-temp2[order(-temp2$TOT_EMP),]
temp3<-rbind(temp.h,temp2)
e<-c()
e<-dim(temp3)[1]
temp3$Cluster_ID<-rep(count,e)
temp3<-temp3[c(7,1:6)]
count<-count+1
temp4<-rbind(temp4,temp3)
}
}
temp5 <-c()
temp5 <-temp4
temp5$Rank<-c(1:dim(temp4)[1])
temp5 <- merge(temp5,Tail, by="LDB_NUM", all.x=TRUE)
temp5 <- arrange(temp5, Rank)
count<-1
for(i in 1:dim(temp5)[1])
{
if(grepl('H',temp5[i,7]))
{
temp5[i,1]=count
count<-count+1
}
}
final.cluster<-temp5 # Contains Clustered Frame
return(final.cluster) # Return Clustered Frame
}) # End Function to Cluster by EIN
} # End Server
shinyApp(ui, server)
0.2.2
MacOS Monterey 12.3.1 (Apple Chip, M1)
seems no
I just successfully installed shiny.router
with R 4.2.0 on a MacOS Monterey 12.3.1 (Apple Chip, M1):
> install.packages("shiny.router")
> packageVersion("shiny.router")
[1] ‘0.2.2’
I can access the man page of the function router_ui()
with and without double colon operators...
> ?router_ui
> ?shiny.router::router_ui
... as seen here in this screenshot from RStudio:
But, when I try to run, for example, this example from your tutorial/blog page...
library(shiny)
library(shiny.router)
# This creates UI for each page.
page <- function(title, content) {
div(
titlePanel(title),
p(content),
uiOutput("power_of_input")
)
}
# Part of both sample pages.
home_page <- page("Home page", "This is the home page!")
side_page <- page("Side page", "This is the side page!")
# Callbacks on the server side for the sample pages
home_server <- function(input, output, session) {
output$power_of_input <- renderUI({
HTML(paste(
"I display <strong>square</strong> of input and pass result to <code>output$power_of_input</code>: ",
as.numeric(input$int) ^ 2))
})
}
side_server <- function(input, output, session) {
output$power_of_input <- renderUI({
HTML(paste(
"I display <strong>cube</strong> of input and <strong>also</strong> pass result to <code>output$power_of_input</code>: ",
as.numeric(input$int) ^ 3))
})
}
# Create routing. We provide routing path, a UI as well as a server-side callback for each page.
router <- make_router(
route("home", home_page, home_server),
route("side", side_page, side_server)
)
# Create output for our router in main UI of Shiny app.
ui <- shinyUI(fluidPage(
shiny::sliderInput("int", "Choose integer:", -10, 10, 1, 1),
router_ui()
))
# Plug router into Shiny server.
server <- shinyServer(function(input, output, session) {
router(input, output, session)
})
# Run server in a standard way.
shinyApp(ui, server)
... R throws the error Error in router_ui() : could not find function "router_ui"
.
I tried to specify the package with double colons...
shiny.router::router_ui()
... what yields the error Error: 'router_ui' is not an exported object from 'namespace:shiny.router'
.
I verified that the package is loaded via adding packageVersion()
and getAnywhere()
right after reading the libraries (here, I outcommented router_ui()
to make it "run")...
> library(shiny)
> library(shiny.router)
> print(packageVersion("shiny.router"))
> print(getAnywhere(router_ui))
> ...
[1] ‘0.2.2’
A single object matching ‘router_ui’ was found
It was found in the following places
namespace:shiny.router
with value
function (router)
{
shiny::addResourcePath("shiny.router", system.file("www",
package = "shiny.router"))
js_file <- file.path("shiny.router", "shiny.router.js")
css_file <- file.path("shiny.router", "shiny.router.css")
list(shiny::singleton(shiny::withTags(shiny::tags$head(shiny::tags$script(type = "text/javascript",
src = js_file), shiny::tags$link(rel = "stylesheet",
href = css_file)))), shiny::tags$div(id = "router-page-wrapper",
lapply(router$routes, function(route) route$ui)))
}
<bytecode: 0x105b37c10>
<environment: namespace:shiny.router>
... what also showed that shiny.router
is properly installed and router_ui()
should be accessible.
Do you have any clue what's wrong here? Sorry, in case I might have overlooked something...
Thank you!
shiny.router
version 0.2.2 from CRAN (on a MacOS Monterey 12.3.1 (Apple Chip, M1?)I would expect R to find the function router_ui()
and open the app.
No response
No response
No response
I suppose there is no possibility to get back to the main page just removing some part of url.
When I move to: app_url/#!/other
and want to return to main page by passing url: app_url/#!/
it still returns to app_url/#!/other
.
Moreover passing app_url/#!/wrong_path
still returns to app_url/#!/other
(not to the default page). Is this target behaviour?
Could you verify above issues? Thank you.
The list of objects:
output$name <- NULL
see #69)A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.