我为我的r Shiny应用程序创建了一个HTMLWIDGET。在我的小部件中,我无法用新数据替换现有数据,每当窗口小部件渲染函数被多次调用时,这会导致一个错误,并且未删除旧数据,并且将新数据附加到旧数据中。
我创建了一个可复制的示例,该示例显示了我的问题。当您单击" UI2"按钮时,应用程序将转到另一个UI,然后从该UI单击"重置"按钮时,它将带您回到默认UI,您可以看到同一同一中会有两个直方图容器而不是一个。这是因为我无法用htmlwidget中的新数据替换旧数据。
可重现的闪亮应用程序:
#library(devtools)
#install_github('radhikesh/d3Viz')
library(shiny)
library(d3Viz)
ui <- shinyUI(fluidPage(
fluidRow(
column(12,uiOutput("page")))
))
ui1 <- function() {
fluidPage(
fluidRow( column(11,
div(
id = "ResetBtn5", actionButton("ui2", "ui2", style="color:
#fff; background-color: #337ab7; border-color: #2e6da4")
))),
br(),
fluidRow(
column(width = 6, d3HistogramOutput("d3Hist"))
),
br(),
br(),
br(),
br(),
br(),
br(),
fluidRow(
column(12, DT::dataTableOutput("DataAric"))
)
)}
ui2 <- function() {
fluidPage(
fluidRow( column(11,
div(
id = "ResetBtn5", actionButton("ResetButtonAric",
"Reset", style="color: #fff; background-color: #337ab7; border-color:
#2e6da4")
))),
br(),
fluidRow(
column(width = 6, d3HistogramOutput("d3Hist2"))
)
)}
server <- shinyServer(function(input, output) {
dataset <- data.frame(lpu = c('Apple','Banana','Apple'), amount =
c(20,10,10))
output$page <- renderUI({
div(class = "outer", do.call(bootstrapPage, c("", ui1())))
})
output$d3Hist <- renderD3Histogram({
k <- input$DataAric_rows_all
if (length(k) > 0)
{
trialAnnotate <- data.frame(table(dataset[k, "lpu"]))
dataset = data.frame(lpu = trialAnnotate$Var1,amount = trialAnnotate$Freq)
d3Histogram(dataset = dataset)
}
})
output$DataAric <-
DT::renderDataTable(
dataset,
options = list(
pageLength = nrow( dataset),
bLengthChange=F
),
rownames = FALSE,
escape = FALSE
)
output$d3Hist2 <- renderD3Histogram({
dataset <- data.frame(lpu = c('Apple','Banana','Orange'), amount =
c(30,20,25))
d3Histogram(dataset = dataset)
})
observeEvent(input$ui2, {
output$page <- renderUI({
div(class = "outer", do.call(bootstrapPage, c("", ui2())))
})
})
observeEvent(input$ResetButtonAric, {
output$page <- renderUI({
div(class = "outer", do.call(bootstrapPage, c("", ui1())))
})
})
})
# Run the application
shinyApp(ui = ui, server = server)
d3histogram.js代码
HTMLWidgets.widget({
name: 'd3Histogram',
type: 'output',
renderOnNullValue: true,
factory: function(el, width, height) {
return {
renderValue: function(x) {
var container = d3.select(el).append("div").attr("id", "container");
var barPadding = 1;
/*
var data=[
{"lpu":"lpu1","amount":"20"},
{"lpu":"lpu2","amount":"40"},
{"lpu":"lpu3","amount":"60"},
{"lpu":"lpu4","amount":"10"},
{"lpu":"lpu5","amount":"80"},
{"lpu":"lpu6","amount":"30"},
{"lpu":"lpu7","amount":"20"},
{"lpu":"lpu8","amount":"40"},
{"lpu":"lpu9","amount":"60"},
{"lpu":"lpu10","amount":"10"},
{"lpu":"lpu11","amount":"80"},
{"lpu":"lpu12","amount":"30"},
{"lpu":"lpu13","amount":"20"},
{"lpu":"lpu14","amount":"40"},
{"lpu":"lpu15","amount":"60"},
{"lpu":"lpu16","amount":"10"},
{"lpu":"lpu17","amount":"80"},
{"lpu":"lpu18","amount":"30"}
];
*/
var dataset = HTMLWidgets.dataframeToD3(x.dataset);
var width=2000,
height=300;
//radius=100,
// padding=100;
var margin = {top: 100, right: 50, bottom: 40, left: 50};
var xLPU=d3.scale.ordinal();
var yLPU=d3.scale.linear();
var xLPUAxis = d3.svg.axis()
.scale(xLPU)
.orient("bottom");
var yLPUAxis = d3.svg.axis()
.scale(yLPU)
.orient("left")
.ticks(20, "??????.");
var svg1 = d3.select("#container").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
xLPU.domain(dataset.map(function(d){return d.lpu;}))
.rangeBands([0, width]);
yLPU.domain([0,d3.max(dataset, function(d) { return d.amount; })])
.range([height,0]);
svg1.append("g")
.attr("class","x axis")
.attr("transform", "translate(0," + height + ")")
.call(xLPUAxis);
svg1.append('g')
.attr('class','y axis')
.call(yLPUAxis);
svg1.selectAll('rect').data(dataset).enter().append('rect')
.attr('x', function(d) {
return xLPU(d.lpu);
})
.attr('y',function(d) {
return yLPU(d.amount);
})
.attr('width',xLPU.rangeBand()-3)
.attr('height', function(d) {
return height - yLPU(d.amount);
})
.attr('fill','teal');
// .attr("fill", function(d) {
// return "rgb(0, 0, " + (d * 10) + ")";
// });
svg1.selectAll(".bartext")
.data(dataset)
.enter()
.append("text")
.attr("class", "bartext")
.attr("text-anchor", "middle")
.attr("fill", "black")
.attr("x", function(d,i) {
return xLPU(d.lpu)+xLPU.rangeBand()/2;
})
.attr("y", function(d,i) {
return yLPU(d.amount) + (-3);
})
.text(function(d){
return d.amount
});
svg1.selectAll('.axis line, .axis path')
.style({'stroke': 'Black', 'fill': 'none', 'stroke-width': '2px'});
svg1.append("text") // text label for the x axis
//.attr("x", 265 )
//.attr("y", 240 )
//.style("text-anchor", "middle")
.attr("transform", "translate(" + (width / 2) + " ," + (height + margin.bottom) + ")")
.text("Year")
.attr('font-family','sans-serif')
.attr('font-size','15px')
.attr('fill','black');
svg1.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Count")
.attr('font-family','sans-serif')
.attr('font-size','15px')
.attr('fill','black');
},
resize: function(width, height) {
}
};
}
});
我认为,我知道问题是,我必须用Java-Script绑定中的新数据替换旧数据,但我是Java-Script的新手,并且无法弄清楚。任何帮助将不胜感激。
谢谢
radhikesh
我不确定这是否是最有效的答案,但是我有相同的问题并通过在创建新孩子之前删除容器的子节点来解决它。
尝试在Rendervalue函数开头的循环使用以下循环
while (document.getElementById(el.id).hasChildNodes()) {
document.getElementById(el.id).removeChild(document.getElementById(el.id).lastChild);
}
您的'htmlwidget`定期将消息发送给浏览器,包括数据。这意味着它将将新数据绑定到DOM,JavaScript将再次运行。当数据用于绘制SVG时,它仍然独立于SVG。问题与新数据无关(r处理(,而是关于维护其状态的旧SVG。
在您的代码中,您无法在JS文件中删除现有的绘图/SVG(即使发送了新数据(。我通常使用以下内容:
this.element.innerHTML = '';
其中 this
是小部件的当前范围。element
是小部件中的el
。设置innerHTML = '';
清除DOM元素并允许您的脚本附加新图像。
您的小部件的工作流程应在进行任何进一步的图纸之前包括此类清洁。更复杂的小部件可以从以前的状态到D3中的新状态transition()
,但可能需要自定义绑定。