State management with jQuery access - r

How can a user defined app state be stored using R Shiny so that it is both accessible in R code and in jQuery code?
For example, my app has two states black and white. Options for storing the current state include
as hidden text in the DOM
as a data attribute of an DOM element
use local storage on the browser
Is there a better approach that is built in to Shiny for this purpose? Something like having a global variable defined in R that can be read in jQuery without having to send a message.

The canonical approach to communicate with JavaScript is indeed via sending a message.
Of course you can rely on pure HTML <-> JS communication means as well:
library(shiny)
read_js <- "$(function() {
$('#output').append(`Text from data: ${$('#constant_data').data('state')}`,
$('<br>'),
`Text from hidden: ${$('#constant_hidden').text()}`,
$('<br>'),
`Text from session: ${sessionStorage.getItem('constant_session')}`,
$('<br>'),
`Text from window: ${window.state}`);
});
"
ui <- fluidPage(
tags$head(tags$script("sessionStorage.setItem('constant_session', 'black');
window.state = 'black';")),
tags$head(tags$script(HTML(read_js))),
div(id = "constant_data", `data-state` = "black"),
div(id = "constant_hidden", "black", style = "display: none"),
div(id = "output"),
)
server <- function(input, output, session) {
}
shinyApp(ui, server)
And the choice of the method is up to you.
Some questions which may guide your choice:
Should the state by visible in the DOM?
Does the state change over time?
Should the state be persistent over sessions?

Related

How do I use {polished} package with {brochure} framework?

This was a complicating in logic execution i came across using {polished} and {brochure}.
When placing secure_ui/secure_server inside of a brochure::Page() in the same order of the example given by the {polished} dev team, there are changes to how a Shiny App is deploy on the {brochure} infrastructure. I was not sure where to relocate the polsiehd logic to.
Differences
no global.R file in a brochureApp()
multiple calls to different module_ui/server functions since each brochure::page() is its owns shiny session
single page shinyApp vs true multipage shinyApp
When needing to merge the two logics you must:
move polished_config() in globals.R --> golem::runApp() [initiate global setting for brochureApp()]
run_app <- function(
onStart = NULL,
options = list(),
enableBookmarking = NULL,
...
) {
# old 'globals.R' logic
polished_config(
app_name = "humblFinance",
api_key = "xxxx"
)
with_golem_options(
app = brochureApp(
# Putting the resources here
golem_add_external_resources(),
page1(),
),
golem_opts = list(...)
)
}
wrap each brochure::page() ui/server with polished::secure_ui/server()`
# an example login page
login <- function(id = "login", href = "/login") {
page(
href = href,
ui = secure_ui(
mod_login_ui(id = id),
sign_in_page_ui = sign_in_custom()
),
server = secure_server(
function(input, output, session) {
mod_login_server(id = id)
}
)
)
}
NOTE
sign_in_custom() is a function that returns a customized UI object from polished::sign_in_default() to create personal business webpages.
I would recommend wrapping polished::sign_in_default() in a custom global function since you will need to define this on ever brochure::page() that you want to have protected behind polished auth.
once you authenticate one page through polished, you will be able to access all other protected pages while you are still logged in. After loggign out and attempting to access any one of the protected pages will result in a custom login page

R Shiny - Trigger a shinyalert popup directly from the UI using javascript

With the following piece of code I'm able to trigger a pure javascript alert by clicking on the question-mark of the fileInput:
fileInput('peptides',
span("Peptides file ",
id="peptidesSpan",
tags$a(
tags$i(class='fa fa-question-circle'),
href = "#",
onclick = "alert('Oops!'); return false;")
),
multiple=FALSE,
accept = c('text/csv','text/comma-separated-values',
)
)
I was wondering if I could trigger a shinyalert popup (https://github.com/daattali/shinyalert/) instead of a simple javascript alert directly form the UI without any observer in the server side.
Something like:
shinyalert("Oops!", "Something went wrong.", type = "error")
If there is not a workaround to do that, any other suggestion with an observer would be welcome as well.
I think using an observer is not at all inconvenient.
Instead of alert(), invoke Shiny.setInputValue(id, value);, and then on your server side you can observeEvent(input[id], { shinyalert() }).
Read this article for details: https://shiny.rstudio.com/articles/communicating-with-js.html
You only need to use one observe code block to achieve this.
An example
Define a customized function in your UI Javascript code and call it in your onclick.
You can put this function say in helper.js in the 'www' folder in your project folder, that will be www/helper.js. Include this file in your Shiny UI code by tags$head(tags$script(src = "helper.js"))
function showAlert(message, type = "info") {
Shiny.setInputValue(
'alertMessage',
{
message: message,
type: type
},
{ priority: 'event' }
);
}
Then on the Shiny server side define the observer once
observeEvent(input$alertMessage, {
alertData <- input$alertMessage
req(alertData)
shinyalert("title", alertData$message, type = alertData$type)
})
It's one of the few times that I answer my own post, but after searching a bit more on stack-overflow I found a workaround inspired by this post.
I downloaded the sweetAlert.js file of the Sweet Alert library directly from here
I create a www folder in the root of my Shiny application
I added the sweetAlert.js file in the www directory and in the dashboardBody() of the ui.R I added the following line:
tags$head(tags$script(src="sweetAlert.js"))
Now I'm able to call directly the Swal.fire function with any argument as I would normally do in any other framework which runs javascript.

Deploy a shiny app - reactiveValues not found

I am trying to deploy my shiny app on shinyapps.io. The app runs fine in my console but when I deploy my app I get errors for reactiveValues, such as:
object 'Logged' not found
OR
Error in reactiveValues(Logged = Logged, registed = registed, Foget = Foget, : object 'Logged' not found
My script is more than 1000 lines, so, I was not sure that it is a good way to upload the whole script, that's why I decided to put the first few lines from the server that are about the reactiveValues.
Appriciate!
server = (function(input, output,session) {
Logged = FALSE;
registed = FALSE;
Foget = FALSE;
Started = FALSE;
tested = FALSE;
Saved = FALSE;
USER <- reactiveValues(Logged = Logged,registed = registed, Foget=Foget, Started=Started,tested=tested,Saved=Saved)
...
...
...
Declare it as global variable using "<<-" I would suggest you to declare all variable as global variable.
The code should end by the following line:
shinyApp(ui = ui, server = server)

Wijimo Autocomplete & AngularJS - setting initial value from loaded data

I am trying to set the initial value in a Wijimo Autocomplete control which has been loaded from an external data source. The scenario being a form is used to create some new data and then is saved. Subsequently the data needs to be edited so it is reloaded into the form.
I can successfully use the Autocomplete on the initial form - the source list is a JSON Array of objects which is loaded into the controller. The app is using UI Router so I resolve this first.
When I save the data I serialise the selected Object from the Autocomplete control and is then saved to a Mongo DB store. When loading this data back in it is converted back to an object.
This is what the control looks like:
<wj-auto-complete
selected-index="selectedIndexCombo"
selected-item="selectedAirline"
items-source="airlineCodes"
display-member-path="Title"
placeholder="Airline Code"
max-items="50"/>
An example of the source list looks like this:
{
"#href":"\/airline.nsf\/api\/data\/collections\/name\/(LUAirlines)\/unid\/8DCD734E7BCDA24D80257C99003770C4",
"#link":
{
"rel":"document",
"href":"\/airline.nsf\/api\/data\/documents\/unid\/8DCD734E7BCDA24D80257C99003770C4"
},
"#entryid":"98-8DCD734E7BCDA24D80257C99003770C4",
"#unid":"8DCD734E7BCDA24D80257C99003770C4",
"#noteid":"FB2",
"#position":"98",
"#siblings":100,
"#form":"Airline",
"AirlineCode":"WN",
"Airline":"Southwest Airlines",
"Title":"WN - Southwest Airlines"
}
So when the form is initially created the controller property selectedAirline is correctly set with the selected Object.
So this works fine in the save function:
$scope.formData.selectedAirline = JSON.stringify($scope.selectedAirline);
But when reloading in the data:
AirlineInfoFactory.loadAirlineInfo($scope.reference).then(function success(response) {
$scope.selectedAirline = eval('(' + response.data.selectedAirline + ')');
$scope.information = response.data.information;
$scope.dataLoaded = true;
console.log($scope.selectedAirline)
$scope.selectedIndexCombo=11;
})
The autocomplete control does not bind to the selectedAirline property.
I tried using the selected-index attribute on the directive so see if I could just change it to something when the data loads but it doesnt work either. I suspect its to do with the digest loop but I am not sure.
Any ideas?
Thanks
I tried to replicate the scenario by reloading the data and setting the selectedAirline property and it works well withe latest version 32. Here is the fiddle:
http://jsfiddle.net/n1kpkcud/2/
` $scope.countries = initialList;
$scope.selectedAirline = '';
$scope.setItem = function () {
$scope.countries = reloading;
$scope.selectedAirline = 'Yemen';
}`
I would suggest you to update this fiddle so that it replicates the issue and I can suggest you accordingly.

Unable to get value of the property 'transports' - SignalR

I'm using signalr in my asp.net web forms application, all seems to work good, except that I'm getting exception on the line
$.connection.hub.start();
However, if I click no on "Do you want to Debug" popup, signalr functionality works pretty well. The full error message is this
Unable to get value of the property 'transports': object is null or undefined
And here is complete js code
var docStatusUpdate = $.connection.docstatus;
docStatusUpdate.statusUpdate = function (msg, session) {
var sessionId = $('input#sessionValue').val();
if (sessionId == session) {
$("#statusUpdateMsgContainer").text(msg);
}
};
docStatusUpdate.endProcessing = function (session) {
var sessionId = $('input#sessionValue').val();
if (sessionId == session) {
$('input[id*=btnRefresh]').click();
}
};
$.connection.hub.start();
function assignValues() {}
I'm using Asp.net 4.0, signalr is installed via nuget.
Any ideas how can I solve this ?
I had a discussion on http://jabbr.net regarding to this issue, and it turned out, that none of the suggestions work. The main thing was to make sure that jQuery library isn't referenced more than once. Anyways, I just deleted browser data, and it started to work as expected.

Resources