我使用了JSF 2.2,Primefaces 6.0,CDI和Highcharts 4.2.3。我的页面之一包含一张图表(Highcharts)和一个干扰我图表的简单表格。我剪切了代码(请看下面),向您展示代码中最重要的部分。
我想实现这样的目标:
commandButton
时,图表应使用新数据重新创建。 到目前为止,我已经:
createChart
函数,该函数使用#{bean.getDataForChart1a(typeOfData)}
从CDI bean(实际上是从数据库)下载数据(对于我的图表),并将这些数据放入js变量中。除了下移数据外,该函数还创建图表。 createNewChart
函数,该函数销毁了我当前的图表,并调用createChart
函数来创建应包含新数据的新图表。 问题是最后阶段。在下面的 View 中,我已经将
oncomplete="createNewChart();"
属性添加到了commandButton
中,此刻,我的页面如下所示:commandButton
时,将重新创建图表,但它使用的是旧数据。我注意到createChart
函数中没有再次下载数据,js没有执行#{bean.getDataForChart1a(typeOfData)}
。因此,#{bean.getDataForChart1a(typeOfData)}
在开始时仅执行一次。我不明白为什么。 如何解决此问题?
我的xhtml页面:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:pt="http://xmlns.jcp.org/jsf/passthrough"
xmlns:pe="http://primefaces.org/ui/extensions">
<h:head>
<!-- loading css -->
</h:head>
<h:body>
<div id="container" style="height: 750px; width: 100%; margin: 0 auto;"/>
<script type="text/javascript">
//<![CDATA[
var chart;
var protos;
var dataChart;
var color;
var seriesChart;
var i, len;
function createChart() {
//Downloading data for the chart
protos = #{visualizationController.getDataForChart1a("protos")};
dataChart = #{visualizationController.getDataForChart1a("data")};
color = #{visualizationController.getDataForChart1a("color")};
seriesChart = [];
//Creating several series of the data
for (i = 0, len = protos.length; i < len; i++) {
seriesChart.push({
color: color[i],
name: protos[i],
data: dataChart[i]
//other options
});
}
console.time('scatter');
console.time('asyncRender');
Highcharts.setOptions({
lang: {
//translate
}
});
// Create the chart
chart = new Highcharts.Chart({
//other options
chart: {
//other options
renderTo: "container"
},
series : seriesChart
});
console.timeEnd('scatter');
}
function createNewChart(){
chart.destroy();
createChart();
}
createChart();
//]]>
</script>
<h:form id="filterForm">
<p:selectManyCheckbox id="filter" value="#{visualizationController.selectedKindOfNetwork}" layout="responsive" columns="4">
<p:ajax update="filterButton" />
<f:selectItems value="#{visualizationController.kindOfNetwork}" var="kindOfNetwork" itemLabel="#{kindOfNetwork}" itemValue="#{kindOfNetwork}" />
</p:selectManyCheckbox>
<br/>
<p:commandButton id="filterButton" value="Filtruj" action="#{visualizationController.actionFilterButtonForChart1a()}"
disabled="#{!visualizationController.visibilityFilterButtonForChart1a}"
update="filterForm"
oncomplete="createNewChart();"/>
</h:form>
</h:body>
</html>
最佳答案
我已经达到了预期的结果。基于@SiMag的评论,我发布了答案,其中包括几种可能的解决方案。帖子分为几个部分,使您可以了解我做错了什么以及为解决此问题所做的事情。
我做错什么了?
就像@SiMag写道:
The script tag isn't updated after the first load so the #{visualizationController.getData...} expressions aren't evaluated anymore and they are referring to the old data.
我到底是什么问题?
此刻,当我知道自己做错了什么时,正确的问题是:
我的页面应该如何工作?
这是您必须知道的最后一个问题,以了解我将在下一部分中进行的操作。我的页面应如下所示:
createChart
javascript函数(自动执行,无需任何按钮或链接),并且该函数应使用我在CDI bean中设置的多个值。作为此操作的结果,应显示该图表。 changeData
javascript函数,并且该函数应使用在CDI bean中设置的几个值。作为此操作的结果,图表应更新其显示的数据。 如果您想实现其他目标,那不是问题。根据您在这里可以找到的解决方案,您将能够实现所需的解决方案。
第一个解决方案
第一种解决方案基于:
RequestContext
将一些值从CDI bean传递到javascript。为了实现这一点,我基于素语表documentation (第11.1章RequestContext); createChart
函数; changeChart
函数。 首先,我在javascript函数中添加了三个参数(对于每个要从CDI bean传递到javascript代码的值,每个参数一个)。此刻,脚本应如下所示:
<script type="text/javascript">
//<![CDATA[
var chart;
var protos;
var dataChart;
var color;
var seriesChart;
var i, len;
//Creating the chart.
function createChart(protosArg, dataArg, colorsArg) {
$(function() {
//The data are parsed
protos = JSON.parse(protosArg);
dataChart = eval(dataArg);
colors = JSON.parse(colorsArg);
seriesChart = [];
//Creating several series of the data
for (i = 0, len = protos.length; i < len; i++) {
seriesChart.push({
color: color[i],
name: protos[i],
data: dataChart[i]
//other options
});
}
console.time('scatter');
console.time('asyncRender');
Highcharts.setOptions({
lang: {
//translate
}
});
// Create the chart
chart = new Highcharts.Chart({
//other options
chart: {
//other options
renderTo: "container"
},
series : seriesChart
});
console.timeEnd('scatter');
});
}
//Updating the chart using the new supplied data.
function changeData(protosArg, dataArg, colorsArg){
$(function() {
protos = JSON.parse(protosArg);
dataChart = eval(dataArg);
colors = JSON.parse(colorsArg);
seriesChart = [];
//Creating several series of the data using the new supplied data.
for (i = 0, len = protos.length; i < len; i++) {
seriesChart.push({
color: color[i],
name: protos[i],
data: dataChart[i]
//other options
});
}
//Removing the old data from the chart.
for (i = 0, len = chart.series.length; i < len; i++) {
chart.series[0].remove(false);
}
//Inserting the new data to the chart.
for (i = 0, len = protos.length; i < len; i++) {
chart.addSeries(seriesChart[i],false);
}
chart.redraw();
});
}
//]]>
</script>
让我们继续到服务器。在这里,我向您展示我的数据的样子。在服务器端,我创建了三个
String
变量。在服务器端,此变量的输出如下所示:["tcp","udp",...]
[[[date,23],[date,1234]],[[date,1234]]]
["black","white",...]
上面的样子,我使用
JSON.parse()
和eval()
方法保存传递的数据。为什么?由于浏览器将传递的数据解释为字符串,因此我们必须将数据转换为数组。在服务器端,我还使用
RequestContext#addCallbackParam()
方法创建了几个回调参数。 getProtos()
,getData()
和getColors()
方法返回带有我准备好的数据的字符串。requestContext = RequestContext.getCurrentInstance();
requestContext.addCallbackParam("protosArg", getProtos());
requestContext.addCallbackParam("dataArg", getData());
requestContext.addCallbackParam("colorsArg", getColors());
最后,我调用
RequestContext#execute()
方法,该方法调用createChart
javascript函数并传递三个必需值: @PostConstruct
public void init() {
//Creating the callback parameters.
initData();
requestContext.execute(String.format("createChart('%s', '%s', '%s')", getProtos(),getData(),getColors()));
}
请注意,,我已使用
''
字符在javascript端实现了正确的表示。目前,浏览器会将数据解释为字符串。我在CDI bean的execute()
方法(具有init()
批注)中调用@PostConstruct
方法,因此,我确定:createChart
javascript函数在开始时仅执行一次(CDI bean具有@ViewScoped
批注); 我在客户端完成的最后一件事是:更改
oncomplete
组件的<p:commandButton>
属性的值。 oncomplete
属性调用changeData
javascript函数。 args
参数引用我在服务器端设置的回调参数。<p:commandButton id="filterButton" value="Filter" action="#{chart2Controller.actionFilterButton()}"
disabled="#{!chart2Controller.visibilityFilterButton}"
update="filterForm"
oncomplete="changeData(args.protosArg, args.dataArg, args.colorsArg);"/>
我的CDI bean的一部分:
@Named
@ViewScoped
public class Chart2Controller implements Serializable {
/**
* Start method.
*/
@PostConstruct
public void init() {
//Creating the callback parameters.
initData();
requestContext.execute(String.format("createChart('%s', '%s', '%s')", getProtos(),getData(),getColors()));
}
/**
* Downloading the data from the database.
*/
private void initData(){
/*
* Sending the query to the database including the filter options of the form,
* saving the result and preparing the data basing on the result.
*/
//Preparing the callback parameters.
requestContext = RequestContext.getCurrentInstance();
requestContext.addCallbackParam("protos", protos);
requestContext.addCallbackParam("data", data);
requestContext.addCallbackParam("colors", colors);
}
/**
* Action for the filter button.
*/
public void actionFilterButton(){
initData();
}
/**
* Visibility for filter button.
*/
public boolean getVisibilityFilterButton(){
//return true or false.
}
//Getter and Setter
private static final long serialVersionUID = -8128862377479499815L;
@Inject
private VisualizationService visualizationService;
private RequestContext requestContext;
private List<String> kindOfNetwork;
private List<String> selectedKindOfNetwork;
private String protos;
private String data;
private String colors;
}
第二解决方案
第二种解决方案基于:
RequestContext
将一些值从CDI bean传递到javascript; createChart
函数; changeChart
函数。 看起来,该解决方案与先前的解决方案相似,但是
createChart
函数正在调用的位置不同。在这种情况下,我在客户端调用了createChart
,并使用EL从CDI bean提供值。我不会重复代码,因此仅告诉您为实现预期结果所做的工作。在服务器端,除了调用
RequestContext#execute()
方法外,我已经完成了与以前的解决方案相同的操作。取而代之的是,我在脚本结尾处在客户端调用了createChart
函数。该脚本应如下所示:<script type="text/javascript">
//<![CDATA[
var protos;
var dataChart;
var color;
//other code
//Creating the chart.
function createChart(protosArg, dataArg, colorsArg) {
$(function() {
protos = protosArg;
dataChart = dataArg;
colors = colorsArg;
//other code
});
}
//Updating the chart using the new supplied data.
function changeData(protosArg, dataArg, colorsArg){
$(function() {
//The data are parsed
protos = JSON.parse(protosArg);
dataChart = eval(dataArg);
colors = JSON.parse(colorsArg);
//other code
});
}
createChart(#{chart2Controller.protos}, #{chart2Controller.data}, #{chart2Controller.colors});
//]]>
</script>
请注意,,我还从
JSON.parse()
函数中删除了eval()
和createChart
方法,因为EL已直接写入javascript代码,并且浏览器将数据正确解释为数组。第三种解决方案
第三种解决方案基于:
<h:inputHidden>
组件将一些值从CDI bean传递到javascript; createChart
函数; changeChart
函数。 此解决方案完全不同。一开始,我在表单中添加了三个
<h:inputHidden>
componenet(每个要传递给javascript的值一个)。该表格应如下所示:<h:form id="filterForm">
<h:inputHidden id="protos" value="#{chart2Controller.protos}" />
<h:inputHidden id="data" value="#{chart2Controller.data}" />
<h:inputHidden id="colors" value="#{chart2Controller.colors}" />
<p:selectManyCheckbox id="filter" value="#{chart2Controller.selectedKindOfNetwork}" layout="responsive" columns="4">
<p:ajax update="filterButton" />
<f:selectItems value="#{chart2Controller.kindOfNetwork}" var="kindOfNetwork" itemLabel="#{kindOfNetwork}" itemValue="#{kindOfNetwork}" />
</p:selectManyCheckbox>
<br/>
<p:commandButton id="filterButton" value="Filtruj" action="#{chart2Controller.actionFilterButton()}"
disabled="#{!chart2Controller.visibilityFilterButton}"
update="filterForm"
oncomplete="changeData();"/>
</h:form>
上面的样子,我添加了
id
的value
和<h:inputHidden>
属性,并将所需的数据保存在value
属性中。请注意,,当您按下按钮时,表单会更新(请查看
update
组件的<p:commandButton>
属性),因此,我确定每次按下时,都会下载输入的value
属性中的数据按钮。最后,我已经通过
document.getElementById("filterForm:idOfInput").value
在javascript代码中引用了这些值。请记住使用JSON.parse()
或eval()
,因为数据被解释为字符串。该脚本应如下所示:<script type="text/javascript">
//<![CDATA[
var chart;
var protos;
var dataChart;
var colors;
function createChart() {
$(function() {
protos = JSON.parse(document.getElementById("filterForm:protos").value);
dataChart = eval(document.getElementById("filterForm:data").value);
colors = JSON.parse(document.getElementById("filterForm:colors").value);
//other code
});
}
function changeData(){
$(function() {
protos = JSON.parse(document.getElementById("filterForm:protos").value);
dataChart = eval(document.getElementById("filterForm:data").value);
colors = JSON.parse(document.getElementById("filterForm:colors").value);
//other code
});
}
//]]>
</script>
高图并更改图表数据
就我而言,我不知道每次按下按钮后会得到多少系列数据,因此有可能每次我都会获得不同系列的数据。 Highcharts API不支持这种情况。您可以在循环中结合使用Chart.addSeries(),Series.setData()和Series.remove()方法。如果还有更多时间,我会做,然后更新此答案。您可以在下面找到一些重新创建/更新图表的方法。
如果您不知道数据序列号。
第一个解决方案
如果您不知道要获得多少数据序列(例如在我的情况下),则可以使用Series.remove()删除当前数据序列,然后通过Chart.addSeries()添加新的数据序列:
function changeData(...){
//Preparation of the received data.
//Removing the old data from the chart.
for (i = 0, len = chart.series.length; i < len; i++) {
chart.series[0].remove(false);
}
//Inserting the new data to the chart.
for (i = 0, len = protos.length; i < len; i++) {
chart.addSeries(seriesChart[i],false);
}
chart.redraw();
}
注意,由于效率高,我为
false
参数设置了redraw
。 Highcharts API说:If doing more operations on the chart, it is a good idea to set redraw to false and call chart.redraw() after.
第二解决方案
您还可以销毁图表并重新创建它。在这种情况下,您还必须像这样更改
changeData
javascript函数:function changeData(...){
chart.destroy();
createChart(...);
}
如果您知道数据序列号。
这种情况比前一种情况容易。看下面的代码,不需要任何解释:
function changeData(...){
//Preparation of the received data.
for(var i = 0; i < chart.series.length; i++){
chart.series[i].setData(seriesData[i],false);
}
chart.redraw();
}
关于javascript - 使用旧数据重新创建图表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38463938/