图表是使用旧数据重新创建的



我使用JSF 2.2、Primefaces 6.0、CDI和Highcharts 4.2.3。我的一个页面包括一个图表(Highcharts)和一个干扰我的图表的简单表单。我已经剪切了我的代码(看下面),向您展示了我代码中最重要的部分。

我想实现这样的目标:

  • 当我在表单中按下commandButton时,图表应该使用新数据重新创建

到目前为止,我已经:

  • 声明了几个js变量,我在脚本中的函数中使用这些变量
  • 创建了一个createChart函数,该函数使用#{bean.getDataForChart1a(typeOfData)}从CDIBean(实际上是从数据库)下载数据(对于我的图表),并将这些数据放入js变量中。除了对数据进行下行编码外,此函数还可以创建图表
  • 创建了一个createNewChart函数,它破坏了我的当前图表,并调用createChart函数来创建应该包括新数据的新图表

问题是最后阶段。您可以看到以下内容:我已将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所写:

脚本标记在第一次加载后不会更新,因此不再计算#{visualizationController.getData…}表达式,它们引用的是旧数据。


我到底有什么问题

目前,当我知道自己做错了什么时,正确的问题是:

  • 如何将几个值从CDIBean传递到javascript
  • 如何在服务器或客户端调用javascript函数(将准备好的和需要的数据传递给该函数)

我的页面应该如何工作

这是最后一个问题,你必须知道答案,才能理解我在下一节中要做什么。我的页面应该是这样工作的:

  • 加载/打开页面时,应执行createChartjavascript函数(无需任何按钮或链接即可自动执行),并且该函数应使用我在CDIBean中设置的几个值。此操作的结果应该是出现图表
  • 每次按下按钮时,都应该执行changeDatajavascript函数,并且该函数应该使用我在CDIBean中设置的几个值。此操作的结果是,图表应更新其显示的数据

如果你想取得不同的成就,那不是问题所在。根据你在这里可以找到的解决方案,你将能够实现你想要的。


第一个解决方案

第一种解决方案基于:

  • 通过RequestContext将一些值从CDIBean传递给javascript。为了实现这一点,我基于素数面文档(第11.1章RequestContext)
  • 调用服务器侧的CCD_ 15函数
  • 调用客户端的CCD_ 16函数

首先,我为我的javascript函数添加了三个参数(每个值一个,我想从CDIBean传递给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()方法,该方法调用createChartjavascript函数并传递三个必需值:

@PostConstruct
public void init() {
//Creating the callback parameters.
initData();     
requestContext.execute(String.format("createChart('%s', '%s', '%s')", getProtos(),getData(),getColors()));
}

请注意,我已经使用''字符在javascript端实现了正确的表示。此时,浏览器会将数据解释为字符串。我在CDIBean的init()方法(有@PostConstruct注释)中调用execute()方法,所以我确信:

  • createChartjavascript函数在开始时只执行一次(CDIBean有@ViewScoped注释)
  • 将在脚本中下载的数据已准备好

我在客户端做的最后一件事是:更改<p:commandButton>组件的oncomplete属性的值。oncomplete属性调用changeDatajavascript函数。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将一些值从CDIBean传递给javascript
  • 调用客户端的CCD_ 41函数
  • 调用客户端的CCD_ 42函数

您可以看到,此解决方案与以前的解决方案相似,但与createChart函数调用的位置不同。在本例中,我在客户端调用了createChart,并使用EL提供CDIBean的值。

我不会重复代码,所以我只告诉你我做了什么来达到预期的结果。在服务器端,除了调用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>

请注意,我还从createChart函数中删除了JSON.parse()eval()方法,因为EL直接写入javascript代码,并且浏览器将数据正确解释为数组。


第三种解决方案

第三种解决方案基于:

  • 通过<h:inputHidden>组件将一些值从CDIBean传递给javascript
  • 调用客户端的CCD_ 51函数
  • 调用客户端的CCD_ 52函数

这个解决方案完全不同。一开始,我在表单中添加了三个<h:inputHidden>组件(每个值一个,我想传递给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>

如上所述,我添加了<h:inputHidden>idvalue属性,并在value属性中保存了所需的数据。

请注意,当您按下按钮时,表单会更新(查看<p:commandButton>组件的update属性),因此我确信每次按下按钮时都会下载输入的value属性中的数据。

最后,我在javascript代码中通过document.getElementById("filterForm:idOfInput").value引用了这些值。记住使用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(); 
}

请注意,由于有效,我为redraw参数设置了false。Highcharts API表示:

如果在图表上执行更多操作,最好将redraw设置为false,然后调用chart.redraw()。

第二个解决方案

您也可以销毁图表并重新创建它。在这种情况下,您还必须更改changeDatajavascript函数,如下所示:

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();
}

作为一种替代方案,您还可以使用highfaces JSF库来处理这些情况,包括将整个流集成到JSF流中。

因此,解决方案将变得像一样简单

<p:outputPanel id="something">
<hf:chart type="line" value="# {lineChartBean.mappedList}" var="birth" subTitle="Including sub title"
point="# {birth.amount}" tickLabel="# {birth.year}" title="Map with Lists of Pojos"/>
</p:outputPanel>
....
<p:commandButton id="filterButton" value="Filtruj" action="#{visualizationController.actionFilterButtonForChart1a()}"
disabled="#{!visualizationController.visibilityFilterButtonForChart1a}"
update="filterForm something"/>

HighFaces也适用于动态序列数,您可以查看例如Simple List、Map with POJO List示例,当然它与PrimeFaces很好地集成在一起。

相关内容

  • 没有找到相关文章

最新更新