TradingView javascript widget deleting the UI in Shiny - javascript

I would like to integrate a JS code to a Shiny application to load a TradingView JS widget.
The problem is when the app is loading, the selector input disappears and the TradingView widget replaces the whole UI, I do not know why.
library(shiny)
library(shinyjs)
jsCode <- 'shinyjs.pageCol = function(para){new TradingView.widget( {"width": 640,"height": 400,"symbol": para,"interval": "D","timezone": "Etc/UTC","theme": "light", "style": "1",
"locale": "en", "toolbar_bg": "#f1f3f6","enable_publishing": false, "allow_symbol_change": true,"container_id": "tradingview_e9634"} );}'
shinyApp(
ui = fluidPage(
div(selectInput("ticker", "Ticker:",
c('NASDAQ:AMD', 'NASDAQ:TSLA', 'NASDAQ:GE'))),
tags$head(HTML('<script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>')) ,
useShinyjs(),
div(extendShinyjs(text = jsCode, functions = c("pageCol")))
),
server = function(input, output) {
observeEvent(input$ticker, {
js$pageCol(input$ticker)
})
}
)

There are two ways of making it work, as described in this other post (related only to Javascript): TradingView widget replacing entire HTML body.
Either give the ID attribute name you chose in your jsCode to the div tag:
div(id="tradingview_e9634", extendShinyjs(text = jsCode, functions = c("pageCol")))
Or use an iframe: place the following chart.html file in a www subfolder of your app folder.
<html>
<head>
<script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>
<script>
function getParameterByName(name, url) {
if (!url) {
url = window.location.href;
}
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
var para = getParameterByName('value');
console.log(para);
var fxWidget = new TradingView.widget({
"width": 640,
"height": 400,
"symbol": para,
"interval": "1",
"timezone": "Etc/UTC",
"theme": "light",
"style": "1",
"locale": "en",
"toolbar_bg": "#f1f3f6",
"enable_publishing": false,
"allow_symbol_change": true,
"container_id": "tradingview_e9634"
});
</script>
</head>
<body>
</body>
</html>
and use this app.R (simpler than the other version, no need for shinyjs):
library(shiny)
shinyApp(
ui = fluidPage(
div(selectInput("ticker", "Ticker:",
c('NASDAQ:AMD', 'NASDAQ:TSLA', 'NYSE:GE'))),
htmlOutput("frame")
),
server = function(input, output) {
observeEvent(input$ticker, {
query <- paste0("chart.html?value=", input$ticker)
output$frame <- renderUI({
tags$iframe(src=query, width=660, height=450)
})
})
})

Related

Manage Shiny Sweetalert Size with Bootstrap

Below is code showing that the size of a sweetalert when bootstrap is used is very large. If you comment out the navbarpage() code chunk I note in the sample below the sweetalert is smaller.
I suppose I could overwrite with new .css which is fine. But, is there an alternative way to deal with that via arguments in the function itself? I don't see an argument in the help, but there is the ... which passes to .js. I'm not savvy enough with js to know if that's an option and if so what might that look like?
Thanks for any suggestions
library(shiny)
library(bslib)
library(shinyBS)
ui <- fluidPage(
### Code Chunk to comment out
navbarPage(
theme = bs_theme(bootswatch = "flatly", version = 4),
title = 'Methods',
tabPanel('One'),
),
### end BS code chunk
tags$h2("Sweet Alert examples"),
actionButton(
inputId = "success",
label = "Launch a success sweet alert",
icon = icon("check")
),
actionButton(
inputId = "error",
label = "Launch an error sweet alert",
icon = icon("remove")
),
actionButton(
inputId = "sw_html",
label = "Sweet alert with HTML",
icon = icon("thumbs-up")
)
)
server <- function(input, output, session) {
observeEvent(input$success, {
sendSweetAlert(
title = "Success !!",
text = "All in order",
type = "success"
)
})
observeEvent(input$error, {
sendSweetAlert(
title = "Error !!",
text = "It's broken...",
type = "error"
)
})
observeEvent(input$sw_html, {
sendSweetAlert(
title = NULL,
text = tags$span(
tags$h3("With HTML tags",
style = "color: steelblue;"),
"In", tags$b("bold"), "and", tags$em("italic"),
tags$br(),
"and",
tags$br(),
"line",
tags$br(),
"breaks",
tags$br(),
"and an icon", icon("thumbs-up")
),
html = TRUE
)
})
}
shinyApp(ui, server)

Tagify - show suggestions on focus not working

In an asp.net Core web application I'm using the Tagify component (Tagify home page & examples) for showing tags in input and textarea controls. Following an example from the link (see under: Same using custom suggestions) I have the following code:
<div class="col-auto" title="Start typing to show available tags ...">
<textarea name="tbSearch2" placeholder="Filter by tags" id="tbSearch2" class="form-control email-filter" rows="1"></textarea>
</div>
and javascript (ajax executes only once -> on page load):
var tagslist = '';
$.ajax({
'url': "Email/GetTags",
'success': function (data) {
tagslist = data;
// load tags for searching
var tagify1 = new Tagify(document.querySelector('textarea[name=tbSearch2]'), {
tagTextProp: 'name',
enforceWhitelist: true,
delimiters: null,
whitelist: tagslist,
editTags: false,
dropdown: {
mapValueTo: 'name',
searchKeys: ['name'],
maxItems: 20, // <- maximum allowed rendered suggestions
classname: 'tags-look', // <- custom classname for this dropdown, so it could be targeted
enabled: 0, // <- show suggestions on focus
closeOnSelect: false // <- do not hide the suggestions dropdown once an item has been selected
},
});
}
});
The tags work, but only when the user starts typing some text into the textarea. The dropdown does not appear immediately when Tagify control has focus (as in the example in the link in the beginning of this post).
Any ideas what I'm doing wrong?
*note: there are no errors in the browser console.
You can try to check the versions of tagifycss and js, Here is a working demo:
GetTags action:
public List<string> GetTags()
{
return new List<string>{ "A# .NET", "A# (Axiom)", "A-0 System", "A+", "A++", "ABAP", "ABC", "ABC ALGOL", "ABSET", "ABSYS", "ACC", "Accent", "Ace DASL", "ACL2", "Avicsoft", "ACT-III", "Action!", "ActionScript"};
}
js:
<script src="https://cdnjs.cloudflare.com/ajax/libs/tagify/4.3.0/tagify.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tagify/4.3.0/tagify.min.css" crossorigin="anonymous" referrerpolicy="no-referrer" />
<script>
var tagslist = '';
$(function () {
$.ajax({
'url': "GetTags",
'success': function (data) {
tagslist = data;
// load tags for searching
var tagify1 = new Tagify(document.querySelector('textarea[name=tbSearch2]'), {
tagTextProp: 'name',
enforceWhitelist: true,
delimiters: null,
whitelist: tagslist,
editTags: false,
dropdown: {
mapValueTo: 'name',
searchKeys: ['name'],
maxItems: 20, // <- maximum allowed rendered suggestions
classname: 'tags-look', // <- custom classname for this dropdown, so it could be targeted
enabled: 0, // <- show suggestions on focus
closeOnSelect: false // <- do not hide the suggestions dropdown once an item has been selected
},
});
}
});
})
</script>
result:

Using values selected from a javascript based select menu in shiny

I am using the jQuery plugin ComboTree to display a tree-like select menu in my shiny app.
I am having trouble retrieving those values (e.g. c("Item 2", "Item 2-1")) to use in some output. So the issue here is to retrieve whatever values are selected from the select menu ($("example").val();).
ui.r:
ui <- function(){
fluidPage(
tags$head(
tags$script(src = "comboTreePlugin.js"),
tags$script(src = "icontains.js"),
tags$link(rel = "stylesheet", type = "text/css", href = "comboTreeStyle.css")
),
includeScript("myData.json"),
# layouy content ----
sidebarLayout(
sidebarPanel(width = 3,
tags$input(type = "text", id = "example", placeholder = "Select"),
uiOutput("comboTreeMenu")
),
mainPanel(width = 9)
)
)
}
server.r:
server <- function(input, output, session){
output$comboTreeMenu <- renderUI({
includeScript("www/example.js")
})
# want to do some manipulation with the resulting selections from the
# combo tree. Something along the lines of:
# selections <- eventReactive(input$click, {
# return(input$comboTreeSelections)
# })
}
example.js:
comboTree1 = $('#example').comboTree({
source: myData,
isMultiple: true
});
myData.json:
var myData = [
{
id: 0,
title: 'Item 1 '
}, {
id: 1,
title: 'Item 2',
subs: [
{
id: 10,
title: 'Item 2-1'
}, {
id: 11,
title: 'Item 2-2'
}, {
id: 12,
title: 'Item 2-3'
}
]
}, {
id: 2,
title: 'Item 3'
}
];
I've tried to add an extra piece of js script like so:
selectedValues = $("#example").val();
Shiny.onInputChange("comboTreeSelections", selectedValues);
Thank you!
This is just a quick fix, as I don't really recommend using a pure jQuery plugin, since you will have to write all the interaction between combotree and Shiny yourself. But when you're only interested in the actual selected items, you could do this:
In comboTreePlugin.js change the function at line 129 to:
this._elemItemsTitle.on('click', function(e){
e.stopPropagation();
if (_this.options.isMultiple)
_this.multiItemClick(this);
else
_this.singleItemClick(this);
var selItem = comboTree1.getSelectedItemsTitle();
Shiny.onInputChange('selTitle', selItem);
});
This example will only work, when you really click on an item, it wont fire when you select an item by hitting Enter. You would have to copy/paste the last 2 lines above in the keydown-event handler (code 13).
Then you can access the variable selTitle with input$selTitle in Shiny.
Here's a small ShinyApp which prints out the selected titles:
library(shiny)
ui <- {fluidPage(
tags$head(
tags$script(src = "comboTreePlugin.js"),
tags$script(src = "icontains.js"),
tags$link(rel = "stylesheet", type = "text/css", href = "comboTreeStyle.css")
),
includeScript("www/myData.json"),
sidebarLayout(
sidebarPanel(width = 3,
tags$input(type = "text", id = "example", placeholder = "Select"),
uiOutput("comboTreeMenu"),
verbatimTextOutput("selected")
),
mainPanel(width = 9)
)
)}
server <- function(input, output, session){
output$comboTreeMenu <- renderUI({
includeScript("www/example.js")
})
output$selected <- renderPrint({
req(input$selTitle)
print(input$selTitle)
})
}
shinyApp(ui, server)
I found another method, where you dont have to mess with the source code and just inject some javascript.
This will trigger a setInterval function, when the dropdown is visible/openend and will re-run every 500ms.
library(shiny)
js <- HTML("
$(function() {
var selection = setInterval(function() {
if($('.comboTreeDropDownContainer').is(':visible')) {
var selItem = comboTree1.getSelectedItemsTitle();
Shiny.onInputChange('selTitle', selItem)
}
}, 500);
});
")
ui <- {fluidPage(
tags$head(
tags$script(src = "comboTreePlugin.js"),
tags$script(src = "icontains.js"),
tags$script(js),
tags$link(rel = "stylesheet", type = "text/css", href = "comboTreeStyle.css")
),
includeScript("www/myData.json"),
sidebarLayout(
sidebarPanel(width = 3,
tags$input(type = "text", id = "example", placeholder = "Select"),
uiOutput("comboTreeMenu"),
verbatimTextOutput("selected")
),
mainPanel(width = 9)
)
)}
server <- function(input, output, session){
output$comboTreeMenu <- renderUI({
includeScript("www/example.js")
})
output$selected <- renderPrint({
req(input$selTitle)
print(input$selTitle)
})
}
shinyApp(ui, server)

Code Prints To DOM When Dynamically Inserting Inline Scripts With Javascript

I'm trying to insert a chart dynamically with javascript. I found an example of how to do such a thing and it almost works. The chart loads but then underneath the chart, part of the Javascript used to display the chart actually shows as text on the page. It otherwise works fine.
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<body>
<div id="tvTest"></div>
<script>
/* helpers
*/
// runs an array of async functions in sequential order
function seq (arr, callback, index) {
// first call, without an index
if (typeof index === 'undefined') {
index = 0
}
arr[index](function () {
index++
if (index === arr.length) {
callback()
} else {
seq(arr, callback, index)
}
})
}
// trigger DOMContentLoaded
function scriptsDone () {
var DOMContentLoadedEvent = document.createEvent('Event')
DOMContentLoadedEvent.initEvent('DOMContentLoaded', true, true)
document.dispatchEvent(DOMContentLoadedEvent)
}
/* script runner
*/
function insertScript ($script, callback) {
var s = document.createElement('script')
s.type = 'text/javascript'
if ($script.src) {
s.onload = callback
s.onerror = callback
s.src = $script.src
} else {
s.textContent = $script.innerText
}
// re-insert the script tag so it executes.
document.head.appendChild(s)
// clean-up
$script.parentNode.removeChild($script)
// run the callback immediately for inline scripts
if (!$script.src) {
callback()
}
}
// https://html.spec.whatwg.org/multipage/scripting.html
var runScriptTypes = [
'application/javascript',
'application/ecmascript',
'application/x-ecmascript',
'application/x-javascript',
'text/ecmascript',
'text/javascript',
'text/javascript1.0',
'text/javascript1.1',
'text/javascript1.2',
'text/javascript1.3',
'text/javascript1.4',
'text/javascript1.5',
'text/jscript',
'text/livescript',
'text/x-ecmascript',
'text/x-javascript'
]
function runScripts ($container) {
// get scripts tags from a node
var $scripts = $container.querySelectorAll('script')
var runList = []
var typeAttr
[].forEach.call($scripts, function ($script) {
typeAttr = $script.getAttribute('type')
// only run script tags without the type attribute
// or with a javascript mime attribute value
if (!typeAttr || runScriptTypes.indexOf(typeAttr) !== -1) {
runList.push(function (callback) {
insertScript($script, callback)
})
}
})
// insert the script tags sequentially
// to preserve execution order
seq(runList, scriptsDone)
}
$(document).ready(function()
{
var htmlContent = `<script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>
<script type="text/javascript">
new TradingView.widget({
"width": 500,
"height": 400,
"symbol": "BINANCE:AMBETH",
"interval": "60",
"timezone": "Etc/UTC",
"theme": "Dark",
"style": "1",
"locale": "en",
"toolbar_bg": "#f1f3f6",
"enable_publishing": false,
"allow_symbol_change": true,
"hideideas": true
});
</script>`;
var $container = document.querySelector('#tvTest');
$container.innerHTML = htmlContent;
runScripts($container);
});
</script>
</body>
</html>
If I run that, the chart displays and just underneath it, I see the code var $container = document.querySelector('#tvTest'); $container.innerHTML = htmlContent; runScripts($container); }); as text in the DOM. How can I get it to render the chart without printing any code to the DOM?
By default this trading view library appends to the body. You can override that by passing "container_id" property. Here is a simplified example of your code:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://s3.tradingview.com/tv.js"></script>
</head>
<body>
<div id="tvTest"></div>
<script type="text/javascript">
new TradingView.widget({
"container_id": "tvTest", // THIS IS THE LINE I ADDED
"width": 500,
"height": 400,
"symbol": "BINANCE:AMBETH",
"interval": "60",
"timezone": "Etc/UTC",
"theme": "Dark",
"style": "1",
"locale": "en",
"toolbar_bg": "#f1f3f6",
"enable_publishing": false,
"allow_symbol_change": true,
"hideideas": true
});
</script>
</body>
</html>
You need to escape the <\/script> inside of the string template

JSDOM not loading script files in Node

I am trying to do a test of loading up an html page in jsdom which will eventually generate graphs. I cannot overcome the first hurdle of just loading the html page and having the javascript execute.
Below is my html page which I am trying to load which doesnt take any parameters and just renders a simple graph.
<html>
<head>
<script src="http://code.jquery.com/jquery.min.js"/>
<script src="http://static.fusioncharts.com/code/latest/fusioncharts.js"/>
<script src="http://static.fusioncharts.com/code/latest/fusioncharts.charts.js"/>
<script src="http://static.fusioncharts.com/code/latest/themes/fusioncharts.theme.fint.js"/>
<script>
var testVar = true;
function test(){
testVar = false;
};
</script>
<script>
$(document).ready(function(){
FusionCharts.ready(function () {
var revenueChart = new FusionCharts({
type: 'column2d',
renderAt: 'container',
width: '400',
height: '200',
dataFormat: 'json',
dataSource: {
"chart": {
"caption": "Split of Revenue by Product Categories",
"subCaption": "2014",
"numberPrefix": "$",
"theme": "fint",
"captionFontSize": "13",
"subcaptionFontSize": "12",
"subcaptionFontBold": "0",
"showValues": "0"
},
"data": [{
"label": "Food",
"value": "28504"
}, {
"label": "Apparels",
"value": "14633"
}, {
"label": "Electronics",
"value": "10507"
}, {
"label": "Household",
"value": "4910"
}]
}
}).render();
});
var svg = $('#container').html();
});
</script>
<head>
<body>
<div id="container">Charts will render here</div>
</body>
Here is the code in node where I am trying to load this page..
var config = {
file: path.join(__dirname, "chart.html"),
features:{
FetchExternalResources: ["script"],
ProcessExternalResources: ["script"],
MutationEvents: '2.0'
},
scripts:[
"http://code.jquery.com/jquery.min.js",
"http://static.fusioncharts.com/code/latest/fusioncharts.js",
'http://static.fusioncharts.com/code/latest/fusioncharts.charts.js',
"http://static.fusioncharts.com/code/latest/themes/fusioncharts.theme.fint.js"
],
onload: function(err, window) {
console.log('*******onload')
},
created: function(err, window) {
console.log('*******created')
},
done: function(err, window) {
console.log('*******done')
if(err){
console.log('*****got err ' + err.message);
callback(err);
}
global.window = window;
console.log('inside done ----------')
var $ = window.jQuery || window.$,
FusionCharts = window.FusionCharts,
document = window.document;
if(typeof FusionCharts == 'undefined')
console.log('FusionCharts NOT LOADED')
else
console.log('FusionCharts LOADED')
if(typeof $ == 'undefined')
console.log('JQUERY NOT LOADED')
else
console.log('JQUERY LOADED')
console.log('testVar ' + window.testVar)
console.log(window.test())
console.log('testVar ' + window.testVar)
console.log('svg is ' + window.svg);
console.log($('#container').html());
window.close();
}
}
jsdom.env(config);
The strange thing here is that if i do not include scripts in the config object, it will not load them and make them available in the done callback even thought it is there on the html page.
Also, testVar is never defined in the callback even thought it is present in the page, the same with window.test(), even though its in the html page it just does not seem to be available in the callback.
I have tried all different variations of creating the jsdom object, but none of them allows the page to load the scripts rather than me passing it into the config object, and none of the different versions allow me to access variables and functions defined in the script tags.
Is there something I am missing ?
Change your script elements so that they are correct HTML:
<script src="http://code.jquery.com/jquery.min.js"></script>
If you do that everything will load fine and you won't have to use scripts in your configuration.

Categories