Multiple child templates of same kind (HighCharts)



  • I'm trying to render several charts by calling the same child template multiple times. Only the first chart get rendered. The second div is rendered, but the chart is blank.

    What have I missed?
    The ids of the actual chart divs (inside the child) are unique.

    The child calls from my main template:

    <div class="chart-container">
        {#child child_chart @template.recipe=html @data.chartHeight=7cm @data.chartWidth=100% @data.dataGroup=projects_mtd}
    </div>
    
    <div class="chart-container">
        {#child child_chart @template.recipe=html @data.chartHeight=7cm @data.chartWidth=100% @data.dataGroup=projects_ytd}
    </div>
    

    The child template:

    <!DOCTYPE html><html>
    <head>
        <meta charset="utf-8">
        <script src="https://code.highcharts.com/highcharts.js"></script>
    </head>
    <body>
    
    <div id={{getChartId}} style="width:{{getChartWidth}}; height:{{getChartHeight}}"/>
    
    <script>
        const chartData = {{{getChartData}}};
    
        Highcharts.chart({{getChartId}}, {
            series: chartData.series,
            chart: {
                type: 'area'
            },
            ...// options omitted in this example
        });
    </script>
    
    </body>
    </html>
    
    function getChartId () {
        return this.dataGroup;
    }
    
    function getChartData() {
        const timeSeries = this.dataGroups[this.dataGroup].timeSeries;
        
        const data = ..shape the data object to suit HIghCharts
    
      return JSON.stringify(data);
    }
    


  • Try to debug it with html recipe.
    Some errors in F12 console?
    Does the div have really different Id?

    This has the most likely nothing to do with child templates.
    You can replicate the issue in playground if you want us to take a look.



  • Oh, found the problem. It would be great if someone could chime in and explain the scoping here.

    Everything works as it should if I use var instead of const in const chartData = {{{getChartData}}};
    Not even let works, so this has something with local scopes to do.

    I'm new to html and I would really like to understand how the scoping works when I have several separate <Script> tags. Where do the variables live, and from where are they accessed? Having to use var seems to indicate that the data (chartData) is accessed from outside of the script tag and needs to be hoisted.


  • administrators

    Everything works as it should if I use var instead of const in const chartData = {{{getChartData}}};
    Not even let works, so this has something with local scopes to do.

    when you use an html script any variable you define is put into the global scope, the variables are only in local scope if they are inside a function. for your case the const, let does not work because these declarations are more strict, they don't allow duplicates in the same scope, and since your variables are all in the global scope then you have a conflict. when you use var it works because that declaration is more permissive, it allows duplicates.

    also it is worth to mention that i think you are producing an invalid html document with your child template calls, i mean it can work right now but it is technically a bad html document what you are generating.

    this is what you are producing:

    <div class="chart-container">
       <!DOCTYPE html><html>
      <head>
          <meta charset="utf-8">
          <script src="https://code.highcharts.com/highcharts.js"></script>
      </head>
      <body>
    
      <div id={{getChartId}} style="width:{{getChartWidth}}; height:{{getChartHeight}}"/>
    
      <script>
          const chartData = {{{getChartData}}};
    
          Highcharts.chart({{getChartId}}, {
              series: chartData.series,
              chart: {
                  type: 'area'
              },
              ...// options omitted in this example
          });
      </script>
    
      </body>
      </html>
    </div>
    

    technically you can not have another html, body element in the same html document. as i said it can work just fine right now but it is not a best practice. if you want your child template to also work as a main template you usually need a bit of more code, you need to detect when the template is called as child or main, and based on that include the full html definition or just part of it that is designed for embedding into another document.

    FYI you can detect wheter the template is used as main or child template in a script, by checking the req.context.isChildRequest variable


Log in to reply
 

Looks like your connection to jsreport forum was lost, please wait while we try to reconnect.