2016-07-19 26 views
3

我使用JSF 2.2,Primefaces 6.0,CDI和Highcharts 4.2.3。我的一個頁面包含一個圖表(Highcharts)和一個干擾我的圖表的簡單表格。我已經削減了我的代碼(請看下面),以顯示我的代碼中最重要的部分。使用舊數據重新創建圖表

我想實現這樣的事情:

  • 當我按在我的形式commandButton,圖表應該使用新的數據重建本身。

到目前爲止,我:

  • 宣佈,我在我的我的腳本中的函數使用一些JS變量;
  • 創建了createChart函數,該函數使用#{bean.getDataForChart1a(typeOfData)}從CDI bean(實際上是從數據庫)下載數據(用於我的圖表),並將這些數據放入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> 
+1

腳本標記,以便語句#{visualizationController ...}不再被評估不先裝後更新,他們指的是舊數據。您可以在表單中使用三個h:inputHidden來存儲您需要在JavaScript中訪問的數據,並且將createChart函數 – SiMag

+0

中的這些輸入值作爲前一篇文章的補充,我不確定是否創建新圖表在你的情況下,時間是最好的主意。難道你不能只使用Series.update()或者Series.setData()方法嗎? –

+0

@SiMag,謝謝你的反饋。我試圖實現你的想法。我已經創建了三個'的部件(與'id'和'值= 「#{visualizationController.getData ...}」'屬性)在我'filterForm'形式。然後,我在我的'createChart'函數中引用了這些輸入的值,如下所示:'variable = document.getElementById(「filterForm:idInput」)。value;'。不幸的是,這是行不通的。目前,我的圖表根本沒有創建。它看起來像:Javascript代碼是在表單創建之前或數據下載之前執行的。可能嗎? – Robert

回答

0

我剛剛完成我預期的結果。基於@SiMag的評論,我發佈了包含幾個可能解決方案的答案。這篇文章分爲幾個部分,讓你瞭解我做錯了什麼,以及我做了什麼來解決這個問題。


我做錯了什麼?

由於@SiMag寫道:

沒有更新,腳本標籤先裝後使#{visualizationController.getData ...}表達式不再被評估,它們指的是老數據。


究竟是我的問題嗎?

此刻,當我知道我做錯了什麼,正確的問題是:

  • 如何從CDI bean來的JavaScript傳遞多個值?
  • 如何在服務器或客戶端調用javascript函數(將準備好的和所需的數據傳遞給此函數)?

應該如何在我的網頁作品?

這是一個你要知道答案,明白我將在接下來的章節中做的最後一個問題。我的頁面應該像這樣工作:

  • 當頁面加載/打開,JavaScript函數應(自動無任何按下按鈕或鏈接)執行和功能應該使用哪一個我設置幾個值createChart CDI bean。作爲這一行動的結果,圖表應該出現。
  • 每個當我按下該按鈕時,javascript函數應該被執行,並且功能應該使用哪個我在CDI豆設置幾個值changeData。作爲此操作的結果,圖表應更新其顯示的數據。

如果你想實現不同的東西,這是沒有問題的。基於你可以在這裏找到的解決方案,你將能夠實現你想要的。


首先溶液

第一解決方案是基於:

  • RequestContext的手段傳遞一些值從CDI豆給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變量。在服務器端這個變量的輸出是這樣的:

  • PROTOS:["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方面。目前,數據將被瀏覽器解釋爲字符串。我呼籲在init()方法​​方法(具有@PostConstruct註解)的CDI bean的,所以我敢肯定:

  • javascript函數將在最開始只執行一次createChart(對CDI bean有@ViewScoped註釋);
  • 將在腳本中下載的數據已準備就緒。

我已經在客戶端做的最後一件事是:改變<p:commandButton>組件的oncomplete屬性的值。 oncomplete屬性調用changeData javascript函數。 args參數指的是我在服務器端設置的回調參數。我的CDI bean的

<p:commandButton id="filterButton" value="Filter" action="#{chart2Controller.actionFilterButton()}" 
       disabled="#{!chart2Controller.visibilityFilterButton}" 
       update="filterForm" 
       oncomplete="changeData(args.protosArg, args.dataArg, args.colorsArg);"/> 

部分:

第二個解決方案是基於

@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; 
} 

解決方法二:

  • 傳遞一些值從CDI bean來javascript通過RequestContext ;
  • 在客戶端調用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> 

注意,我也刪除了從createChart功能JSON.parse()eval()方法,因爲埃爾斯已經直接寫入JavaScript代碼和瀏覽器中正確解釋數據作爲陣列。


第三溶液

第三種解決方案是基於:

  • <h:inputHidden>其它的組件來使從CDI豆給javascript一些值;
  • 在客戶端調用createChart函數;
  • 在客戶端調用changeChart函數。

這種解決方案是完全不同的。在開始時,我向表單中添加了三個<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屬性所需的數據。

注意,當你按下按鈕,形式更新(看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和更改圖表的數據

在我來說,我不知道有多少一系列數據中每個後,我會得到怎樣按下按鈕,所以我可以每次得到不同數量的系列。 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(); 
} 

注意,我已經設置falseredraw參數由於有效。 Highcharts API說:

如果在圖表上做了更多的操作,最好將重繪設置爲false並在之後調用chart.redraw()。

解決方法二

您也可以摧毀圖表並重新創建它。在這種情況下,你已經也改變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(); 
} 
0

作爲替代方案,你也可以使用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還與一系列的動態數量,可以檢查如簡單列表,地圖與POJO列表示例,它當然很好地集成PrimeFaces。

相關問題