HTML structure in Jelly Scripting

HameetK
Tera Contributor

Hi 
I am working on a requirement where i want to show/hide a UI formatter from on a UI macro based on the reference field on the form.

<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
    <style>
        tr:nth-child(even) {
            background-color: #DCDCDC;
        }

        td,
        th {
            padding: 5px;
            border: 1px solid black;
            border-collapse: collapse;
        }

        table {
            margin-left: auto;
            margin-right: auto;
            border: 1px solid black;
            border-collapse: collapse;
        }
    </style>
	<script>

        function saveCostPlanData() {
            var data = {};

            // Get all inputs for carry_over (this gives us all years)
            var carryInputs = document.querySelectorAll("input[id^='carry_over_']");

            carryInputs.forEach(function(input) {
                // Extract year from id → carry_over_FY25 → FY25
                // var year = input.id.replace("carry_over_", "").toLowerCase();
                var year = input.id.replace("carry_over_", "");

                data[year] = {
                    carry_over: input.value || "",
                    supplemental: document.getElementById("supplemental_" + year)?.value || "",
                    release: document.getElementById("release_" + year)?.value || ""
                };
            });

            g_form.setValue('u_cost_plan_changes', JSON.stringify(data));

        }
    </script>

    <g2:evaluate var="jvar_savedData" jelly="true">
        var data = '';
        var gr = new GlideRecord('project_change_request');
        gr.addQuery('sys_id', RP.getParameterValue('sys_id'));
        gr.query();

        if (gr.next()) {
        data = gr.getValue('u_cost_plan_changes');
        }
        data;

    </g2:evaluate>

    <div id='cost_plan_baseline'>
        <div class="section-title" style="width:80%"> <b>Baseline Cost Plan</b></div>
        <table style="width:80%">
            <g2:evaluate>
                var baselineConfig = {
                dmn_demand: 'dmn_demand_baseline',
                pm_project: 'pm_project_baseline'
                };
                var totals1 = {};
                var allResourceTypes1 = {};
                var query = '';
                var className = current.parent.sys_class_name;
                var category = current.category.getDisplayValue();
                query ='^ORbaseline_nameLIKE' + current.parent.getDisplayValue() + ' ' + category;
                var baselineGR=new GlideRecord(baselineConfig[className]);
                baselineGR.addEncodedQuery(' baseline_name=Baseline on Approved State-' + current.parent.getDisplayValue() + query);
                baselineGR.addQuery(className, current.parent.sys_id);
                baselineGR.orderByDesc('sys_created_on');
                baselineGR.setLimit(1);
                baselineGR.query();
                if (baselineGR.next()) {
                var costPlanBaselineGR=new GlideRecord('cost_plan_baseline');
                costPlanBaselineGR.addQuery('parent', baselineGR.getValue('sys_id'));
                costPlanBaselineGR.query();
                while (costPlanBaselineGR.next()) {
                var costPlanCategory=costPlanBaselineGR.resource_type.getDisplayValue();
                allResourceTypes1[costPlanCategory]=true;
                var costBreakdownGR=new GlideRecord('cost_plan_breakdown_baseline');
                costBreakdownGR.addQuery('parent', costPlanBaselineGR.getValue('sys_id'));
                costBreakdownGR.query();
                while (costBreakdownGR.next()) {
                var fiscalPeriod=costBreakdownGR.getDisplayValue('fiscal_period') || '' ;
                var fiscalYear=fiscalPeriod.split(':')[0].trim();
                var cost=parseFloat(costBreakdownGR.getValue('cost_default_currency')) || 0;
                if (!totals1[fiscalYear]) {
                totals1[fiscalYear]={ Total: 0 };
                }
                if (!totals1[fiscalYear][costPlanCategory]) {
                totals1[fiscalYear][costPlanCategory]=0;
                }
                totals1[fiscalYear][costPlanCategory] +=cost;
                totals1[fiscalYear].Total +=cost;
                }
                }
                }

                for (var fyKey1 in totals1) {
                totals1[fyKey1].Total=parseFloat(totals1[fyKey1].Total.toFixed(2));
                for (var rt1 in allResourceTypes1) {
                if (totals1[fyKey1][rt1]===undefined) {
                totals1[fyKey1][rt1]=0;
                }
                totals1[fyKey1][rt1]=parseFloat(totals1[fyKey1][rt1].toFixed(2));
                }
                }

                var orderedTotals1 = {};
                var orderedRT1 = Object.keys(allResourceTypes1).sort();
                var sortedFY1 = Object.keys(totals1).sort(function(a, b) {
                return parseInt(a.replace('FY','')) - parseInt(b.replace('FY',''));
                });

                for (var m in sortedFY1) {
                var fyName1 = sortedFY1[m];
                orderedTotals1[fyName1] = { Total: totals1[fyName1].Total };

                for (var idx1 in orderedRT1) {
                var resTypeName1=orderedRT1[idx1];
                orderedTotals1[fyName1][resTypeName1]=totals1[fyName1][resTypeName1];
                }
                }
             
            </g2:evaluate>
            <j2:forEach items="$[Object.keys(orderedTotals1)]" var="jvar_fy_key2" varStatus="jvar_index1">
                <g2:evaluate jelly="true">
                    var fyData2 = orderedTotals1[jelly.jvar_fy_key2];
                </g2:evaluate>
                <j2:if test="$[jvar_index1 == 0]">
                    <tr>
                        <th>Year</th>
                        <j2:forEach items="$[Object.keys(fyData2)]" var="jvar_key3">
                            <th>$[jvar_key3]</th>
                        </j2:forEach>
                    </tr>
                </j2:if>
                <tr>
                    <td>$[jvar_fy_key2]</td>
                    <j2:forEach items="$[Object.keys(fyData2)]" var="jvar_key4">
                        <g2:evaluate jelly="true">
                            var fyData3 = fyData2[jelly.jvar_key4];
                        </g2:evaluate>
                        <td>$[fyData3]</td>
                    </j2:forEach>
                </tr>
            </j2:forEach>
        </table>
		
		<div class="section-title" style="width:80%"> <b>Draft Cost Plan</b></div>
        <table style="width:80%">

            <g2:evaluate jelly="true">
                var allResourceTypes = {};
                var costPlanMap = {};
                var costPlanGR = new GlideRecord('cost_plan');
                costPlanGR.addQuery('top_task', current.getValue('parent'));
                costPlanGR.query();
                while (costPlanGR.next()) {
                var costPlanId = costPlanGR.getUniqueValue();
                var costPlanType = (costPlanGR.getDisplayValue('resource_type') || '').trim();
                costPlanMap[costPlanId] = costPlanType;
                }
                var totals = {};

                var costBreakdownGR = new GlideRecord('cost_plan_breakdown');
                costBreakdownGR.addQuery('task', current.getValue('parent'));
                costBreakdownGR.addNotNullQuery('cost_plan');
                costBreakdownGR.query();
                while (costBreakdownGR.next()) {
                var parentCostPlan = costBreakdownGR.getValue('cost_plan');
                if (!costPlanMap[parentCostPlan]) continue;
                var mappedResourceType = costPlanMap[parentCostPlan];
                allResourceTypes[mappedResourceType] = true;
                var fp = costBreakdownGR.getDisplayValue('fiscal_period') || '';
                var fy = fp.split(':')[0].trim();
                var cost = parseFloat(costBreakdownGR.getValue('cost_default_currency')) || 0;
                if (!totals[fy]) {
                totals[fy] = { Total: 0 };
                }
                if (!totals[fy][mappedResourceType]) {
                totals[fy][mappedResourceType] = 0;
                }
                totals[fy][mappedResourceType] += cost;
                totals[fy].Total += cost;
                }
                for (var fyKey in totals) {
                totals[fyKey].Total = parseFloat(totals[fyKey].Total.toFixed(2));
                for (var rt in allResourceTypes) {
                if (totals[fyKey][rt] === undefined) {
                totals[fyKey][rt] = 0;
                }
                totals[fyKey][rt] = parseFloat(totals[fyKey][rt].toFixed(2));
                }
                }
                var orderedTotals = {};
                var orderedRT = Object.keys(allResourceTypes).sort();
                var sortedFY = Object.keys(totals).sort(function(a, b) {
                return parseInt(a.replace('FY','')) - parseInt(b.replace('FY',''));
                });

                for (var n in sortedFY) {
                var fyName = sortedFY[n];
                orderedTotals[fyName] = { Total: totals[fyName].Total };

                for (var idx in orderedRT) {
                var resTypeName=orderedRT[idx];
                orderedTotals[fyName][resTypeName]=totals[fyName][resTypeName];
                }
                }
            </g2:evaluate>

            <j2:forEach items="$[Object.keys(orderedTotals)]" var="jvar_fy_key" varStatus="jvar_index">
                <g2:evaluate jelly="true">
                    var fyData = orderedTotals[jelly.jvar_fy_key];
                </g2:evaluate>
                <j2:if test="$[jvar_index == 0]">
                    <tr>
                        <th>Year</th>
                        <j2:forEach items="$[Object.keys(fyData)]" var="jvar_key1">
                            <th>$[jvar_key1]</th>
                        </j2:forEach>
                    </tr>
                </j2:if>
                <tr>
                    <td>$[jvar_fy_key]</td>
                    <j2:forEach items="$[Object.keys(fyData)]" var="jvar_key">
                        <g2:evaluate jelly="true">
                            var fyData1 = fyData[jelly.jvar_key];
                        </g2:evaluate>
                        <td>$[fyData1]</td>
                    </j2:forEach>
                </tr>
            </j2:forEach>
        </table>

        <div class="section-title" style="width:80%"> <b>Cost plan change classification</b></div>
        <table style="width:80%">
            <tr>
                <th>Year</th>
                <th>Baselined Cost plan</th>
                <th>Draft cost plan</th>
                <th>Carry over/Forward</th>
                <th>Supplemental</th>
                <th>Release</th>
                <th>New cost plan</th>
                <th>Change to be Claaified</th>
            </tr>

            <j2:forEach items="$[Object.keys(orderedTotals)]" var="jvar_fy_key1">

                <g2:evaluate jelly="true">
                    var year = jelly.jvar_fy_key1;
                    var fyData1 = orderedTotals[year];
                    var fyData4 = orderedTotals1[year];
                    var data = jelly.jvar_savedData;

                    var data1 = JSON.parse(data);

                    var carry = '';
                    var supp = '';
                    var rel = '';

                    if (data1[year]) {
                    carry = data1[year].carry_over || '';
                    supp = data1[year].supplemental || '';
                    rel = data1[year].release || '';
                    }

                </g2:evaluate>

                <tr>
                    <td>$[jvar_fy_key1]</td>
                    <td>$[fyData4.Total]</td>
                    <td>$[fyData1.Total]</td>

                    <td>
                        <input type="number" id="carry_over_$[jvar_fy_key1]" name="carry_over_$[jvar_fy_key1]" value="$[carry]" />
                    </td>

                    <td>
                        <input type="number" id="supplemental_$[jvar_fy_key1]" name="supplemental_$[jvar_fy_key1]" value="$[supp]" />
                    </td>

                    <td>
                        <input type="number" id="release_$[jvar_fy_key1]" name="release_$[jvar_fy_key1]" value="$[rel]" />
                    </td>

                    <td>Germany</td>
                    <td>Germany</td>
                </tr>

            </j2:forEach>
        </table>
    </div>
</j:jelly>

I have designed the HTML Structure as below :

<div id='cost_plan_baseline'>
    <div class="section-title" style="width:80%"> 
        <b>Baseline Cost Plan</b>
    </div>
    <table style="width:80%">
        <!-- table content -->
    </table>

    <div class="section-title" style="width:80%"> 
        <b>Draft Cost Plan</b>
    </div>
    <table style="width:80%">
        <!-- table content -->
    </table>

    <div class="section-title" style="width:80%"> 
        <b>Cost plan change classification</b>
    </div>
    <table style="width:80%">
        <!-- table content -->
    </table>

</div>  <!-- THIS CLOSES THE cost_plan_baseline DIV -->

However , when it is being rendered by the browser the <div id='cost_plan_baseline> tag is being closed after the first <div> as shown in the screenshot below:

HameetK_0-1776789306230.png

What is the possible solution for this?

@Naveen20 Can you please help?

2 REPLIES 2

Naveen20
ServiceNow Employee

Try this possible fix

1. Move all `<g2:evaluate>` computation blocks out of the tables, above the outer div:
<g2:evaluate>
// all baseline + draft calculations here
</g2:evaluate>

<div id="cost_plan_baseline">
<table>
<tbody>
<j2:forEach ...>
<tr>...</tr>
</j2:forEach>
</tbody>
</table>
</div>
2. Wrap rows in an explicit `<tbody>` — gives the parser a valid container.
3. Replace per-row `<g2:evaluate jelly="true">` with inline `$[...]` expressions, or precompute a flat row array above the div.

Show/hide
Client script on the reference field:

function onChange(control, oldValue, newValue, isLoading) {
if (isLoading) return;
var el = document.getElementById('cost_plan_baseline');
if (el) el.style.display = newValue ? 'block' : 'none';
}

HameetK
Tera Contributor

Hi @Naveen20 

HameetK_0-1776870636507.png

I did the changes you suggested but it is still not working.

<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
    <style>
        tr:nth-child(even) {
            background-color: #DCDCDC;
        }

        td,
        th {
            padding: 5px;
            border: 1px solid black;
            border-collapse: collapse;
        }

        table {
            margin-left: auto;
            margin-right: auto;
            border: 1px solid black;
            border-collapse: collapse;
        }
    </style>
    <!-- <script>
        function saveCostPlanData() {
            var data = {};

            // Get all inputs for carry_over (this gives us all years)
            var carryInputs = document.querySelectorAll("input[id^='carry_over_']");

            carryInputs.forEach(function(input) {
                // Extract year from id → carry_over_FY25 → FY25
                // var year = input.id.replace("carry_over_", "").toLowerCase();
                var year = input.id.replace("carry_over_", "");

                data[year] = {
                    carry_over: input.value || "",
                    supplemental: document.getElementById("supplemental_" + year)?.value || "",
                    release: document.getElementById("release_" + year)?.value || ""
                };
            });

            g_form.setValue('u_cost_plan_changes', JSON.stringify(data));

        }
    </script> -->

    <!-- <g2:evaluate var="jvar_savedData" jelly="true">
        var data = '';
        var gr = new GlideRecord('project_change_request');
        gr.addQuery('sys_id', RP.getParameterValue('sys_id'));
        gr.query();

        if (gr.next()) {
        data = gr.getValue('u_cost_plan_changes');
        }
        data;

    </g2:evaluate> -->

    <g2:evaluate var='jvar_baseline_rows' object="true">
        var baselineConfig = {
        dmn_demand: 'dmn_demand_baseline',
        pm_project: 'pm_project_baseline'
        };
        var baselineRows = [];
        var baselineObj = {};
        var baselinedResourceTypes = {};
        var query = '';
        var className = current.parent.sys_class_name;
        var category = current.category.getDisplayValue();
        query ='^ORbaseline_nameLIKE' + current.parent.getDisplayValue() + ' ' + category;
        var baselineGR=new GlideRecord(baselineConfig[className]);
        baselineGR.addEncodedQuery(' baseline_name=Baseline on Approved State-' + current.parent.getDisplayValue() + query);
        baselineGR.addQuery(className, current.parent.sys_id);
        baselineGR.orderByDesc('sys_created_on');
        baselineGR.setLimit(1);
        baselineGR.query();
        if (baselineGR.next()) {
        var costPlanBaselineGR=new GlideRecord('cost_plan_baseline');
        costPlanBaselineGR.addQuery('parent', baselineGR.getValue('sys_id'));
        costPlanBaselineGR.query();
        while (costPlanBaselineGR.next()) {
        var costPlanCategory=costPlanBaselineGR.resource_type.getDisplayValue();
        baselinedResourceTypes[costPlanCategory]=true;
        var costBreakdownGR=new GlideRecord('cost_plan_breakdown_baseline');
        costBreakdownGR.addQuery('parent', costPlanBaselineGR.getValue('sys_id'));
        costBreakdownGR.query();
        while (costBreakdownGR.next()) {
        var fiscalPeriod=costBreakdownGR.getDisplayValue('fiscal_period') || '' ;
        var fiscalYear=fiscalPeriod.split(':')[0].trim();
        var baselinedCost=parseFloat(costBreakdownGR.getValue('cost_default_currency')) || 0;
        if (!baselineObj[fiscalYear]) {
        baselineObj[fiscalYear]={ Total: 0 };
        }
        if (!baselineObj[fiscalYear][costPlanCategory]) {
        baselineObj[fiscalYear][costPlanCategory]=0;
        }
        baselineObj[fiscalYear][costPlanCategory] +=baselinedCost;
        baselineObj[fiscalYear].Total +=baselinedCost;
        }
        }
        }

        for (var baselinedYear in baselineObj) {
        baselineObj[baselinedYear].Total=parseFloat(baselineObj[baselinedYear].Total.toFixed(2));
        for (var type in baselinedResourceTypes) {
        if (baselineObj[baselinedYear][type]===undefined) {
        baselineObj[baselinedYear][type]=0;
        }
        baselineObj[baselinedYear][type]=parseFloat(baselineObj[baselinedYear][type].toFixed(2));
        }
        }

        var sortedBaselineObj = {};
        var sortedbaselineResourceTypes = Object.keys(baselinedResourceTypes).sort();
        var sortedfiscalYear = Object.keys(baselineObj).sort(function(a, b) {
        return parseInt(a.replace('FY','')) - parseInt(b.replace('FY',''));
        });

        for (var m in sortedfiscalYear) {
        var fiscalYearName = sortedfiscalYear[m];
        sortedBaselineObj[fiscalYearName] = { Total: baselineObj[fiscalYearName].Total };

        for (var type1 in sortedbaselineResourceTypes) {
        var baselineTypeName=sortedbaselineResourceTypes[type1];
        sortedBaselineObj[fiscalYearName][baselineTypeName]=baselineObj[fiscalYearName][baselineTypeName];
        }
        }

        for (var fy1 in sortedBaselineObj) {
        baselineRows.push({
        year: fy1,
        cols: sortedBaselineObj[fy1]
        });
        }
        baselineRows;
    </g2:evaluate>
    <g2:evaluate var="jvar_baseline_headers" object="true" jelly="true">
        var headers = [];
        if (jelly.jvar_baseline_rows.length > 0) {
        headers = Object.keys(jelly.jvar_baseline_rows[0].cols);
        }
        headers;
    </g2:evaluate>

    <g2:evaluate var='jvar_rows' object="true">
        var rows = [];
        var allResourceTypes = {};
        var costPlanMap = {};
        var costPlanGR = new GlideRecord('cost_plan');
        costPlanGR.addQuery('top_task', current.getValue('parent'));
        costPlanGR.query();
        while (costPlanGR.next()) {
        var costPlanId = costPlanGR.getUniqueValue();
        var costPlanType = (costPlanGR.getDisplayValue('resource_type') || '').trim();
        costPlanMap[costPlanId] = costPlanType;
        }
        var totals = {};

        var costBreakdownGR = new GlideRecord('cost_plan_breakdown');
        costBreakdownGR.addQuery('task', current.getValue('parent'));
        costBreakdownGR.addNotNullQuery('cost_plan');
        costBreakdownGR.query();
        while (costBreakdownGR.next()) {
        var parentCostPlan = costBreakdownGR.getValue('cost_plan');
        if (!costPlanMap[parentCostPlan]) continue;
        var mappedResourceType = costPlanMap[parentCostPlan];
        allResourceTypes[mappedResourceType] = true;
        var fp = costBreakdownGR.getDisplayValue('fiscal_period') || '';
        var fy = fp.split(':')[0].trim();
        var cost = parseFloat(costBreakdownGR.getValue('cost_default_currency')) || 0;
        if (!totals[fy]) {
        totals[fy] = { Total: 0 };
        }
        if (!totals[fy][mappedResourceType]) {
        totals[fy][mappedResourceType] = 0;
        }
        totals[fy][mappedResourceType] += cost;
        totals[fy].Total += cost;
        }
        for (var fyKey in totals) {
        totals[fyKey].Total = parseFloat(totals[fyKey].Total.toFixed(2));
        for (var rt in allResourceTypes) {
        if (totals[fyKey][rt] === undefined) {
        totals[fyKey][rt] = 0;
        }
        totals[fyKey][rt] = parseFloat(totals[fyKey][rt].toFixed(2));
        }
        }
        var orderedTotals = {};
        var orderedRT = Object.keys(allResourceTypes).sort();
        var sortedFY = Object.keys(totals).sort(function(a, b) {
        return parseInt(a.replace('FY','')) - parseInt(b.replace('FY',''));
        });

        for (var n in sortedFY) {
        var fyName = sortedFY[n];
        orderedTotals[fyName] = { Total: totals[fyName].Total };

        for (var idx in orderedRT) {
        var resTypeName=orderedRT[idx];
        orderedTotals[fyName][resTypeName]=totals[fyName][resTypeName];
        }
        }
        for (var fy2 in orderedTotals) {
        rows.push({
        year: fy2,
        cols: orderedTotals[fy2]
        });
        }
        rows;
    </g2:evaluate>
    <g2:evaluate var="jvar_headers" object="true" jelly="true">
        var headers = [];
        if (jelly.jvar_rows.length > 0) {
        headers = Object.keys(jelly.jvar_rows[0].cols);
        }
        headers;
    </g2:evaluate>

    <g2:evaluate var="jvar_classification" object="true">
        var classData = [];

        for (var year1 in orderedTotals) {
        var fyDataDraft = orderedTotals[year1];
        var fyDataBaseline = sortedBaselineObj[year1];

        var baselineTotal = (fyDataBaseline ${AND} fyDataBaseline.Total) ? parseFloat(fyDataBaseline.Total) : 0;
        var draftTotal = (fyDataDraft ${AND} fyDataDraft.Total) ? parseFloat(fyDataDraft.Total) : 0;

        var carry = 0;
        var supp = 0;
        var rel = 0;

        var newCostPlan = (baselineTotal + carry + supp - rel);
        var changeClassified = (draftTotal - newCostPlan);
        var test1 = {
        baselineTotal: baselineTotal,
        draftTotal: draftTotal,
        carry: carry,
        supp: supp,
        rel: rel,
        newCostPlan: newCostPlan,
        changeClassified: changeClassified
        };
        classData.push({
        year: year1,
        cols: test1
        });

        }

        classData;
    </g2:evaluate>

    <div id='cost_plan_baseline'>
        <div class="section-title" style="width:80%"> <b>Baseline Cost Plan</b></div>
        <table style="width:80%">
            <!-- <j2:forEach items="$[Object.keys(sortedBaselineObj)]" var="jvar_fy_key2" varStatus="jvar_index1">
                <g2:evaluate jelly="true">
                    var fyData2 = sortedBaselineObj[jelly.jvar_fy_key2];
                </g2:evaluate>
                <j2:if test="$[jvar_index1 == 0]">
                    <tr>
                        <th>Year</th>
                        <j2:forEach items="$[Object.keys(fyData2)]" var="jvar_key3">
                            <th>$[jvar_key3]</th>
                        </j2:forEach>
                    </tr>
                </j2:if>
                <tr>
                    <td>$[jvar_fy_key2]</td>
                    <j2:forEach items="$[Object.keys(fyData2)]" var="jvar_key4">
                        <g2:evaluate jelly="true">
                            var fyData3 = fyData2[jelly.jvar_key4];
                        </g2:evaluate>
                        <td>$[fyData3]</td>
                    </j2:forEach>
                </tr>
            </j2:forEach> -->
            <thead>
                <tr>
                    <th>Year</th>

                    <j2:forEach items="$[jvar_baseline_headers]" var="jvar_baseline_col">
                        <th>$[jvar_baseline_col]</th>
                    </j2:forEach>
                </tr>
            </thead>

            <tbody>
                <j2:forEach items="$[jvar_baseline_rows]" var="jvar_baseline_row">
                    <tr>
                        <td>$[jvar_baseline_row.year]</td>

                        <j2:forEach items="$[jvar_baseline_headers]" var="jvar_baseline_col">
                            <td>$[jvar_baseline_row.cols.get(jvar_baseline_col)]</td>
                        </j2:forEach>

                    </tr>
                </j2:forEach>
            </tbody>
        </table>

        <div class="section-title" style="width:80%"> <b>Draft Cost Plan</b></div>
        <table style="width:80%">
            <thead>
                <tr>
                    <th>Year</th>
                    <j2:forEach items="$[jvar_headers]" var="jvar_col">
                        <th>$[jvar_col]</th>
                    </j2:forEach>
                </tr>
            </thead>

            <tbody>
                <j2:forEach items="$[jvar_rows]" var="jvar_row">
                    <tr>
                        <td>$[jvar_row.year]</td>

                        <j2:forEach items="$[jvar_headers]" var="jvar_col">
                            <td>$[jvar_row.cols.get(jvar_col)]</td>
                        </j2:forEach>

                    </tr>
                </j2:forEach>
            </tbody>
        </table>

        <div class="section-title" style="width:80%"> <b>Cost plan change classification</b></div>
        <table style="width:80%">
            <tr>
                <th>Year</th>
                <th>Baselined Cost plan</th>
                <th>Draft cost plan</th>
                <th>Carry over/Forward</th>
                <th>Supplemental</th>
                <th>Release</th>
                <th>New cost plan</th>
                <th>Change to be Claaified</th>
            </tr>

            <j2:forEach items="$[jvar_classification]" var="jvar_row">
                <tr>
                    <td>$[jvar_row.year]</td>

                    <td>$[jvar_row.cols.baselineTotal]</td>
                    <td>$[jvar_row.cols.draftTotal]</td>

                    <td>
                        <input type="number" id="carry_over_$[jvar_row.year]" name="carry_over_$[jvar_row.year]" value="$[jvar_row.cols.carry]" />
                    </td>

                    <td>
                        <input type="number" id="supplemental_$[jvar_row.year]" name="supplemental_$[jvar_row.year]" value="$[jvar_row.cols.supp]" />
                    </td>

                    <td>
                        <input type="number" id="release_$[jvar_row.year]" name="release_$[jvar_row.year]" value="$[jvar_row.cols.rel]" />
                    </td>

                    <td>$[jvar_row.cols.newCostPlan]</td>
                    <td>$[jvar_row.cols.changeClassified]</td>
                </tr>
            </j2:forEach>
        </table>
    </div>
</j:jelly>