1

I have a table that has some data on it and some of their cell must be sum into some other cells in the same row for each data row present. I'd like to make it dinamically at clientside to reduce some of the serverload since there are some millions of family rows paginated in some cases.

The table is generated as follows:

<rich:subTable id="tb" var="fila" value="#{AdministrarDMPermanente.listaDistribucionRecHum}" rowKeyVar="rowk">
    <rich:column style="text-align:center">
        <h:outputText value="#{fila.familiasP}"
        id="family1#{rowk+1}" />
    </rich:column>
    <rich:column style="text-align:center">
        <h:outputText value="#{fila.familiasPA}"
        id="family2#{rowk+1}" />
    </rich:column>
    <rich:column style="text-align:center">
        <h:outputText value="#{fila.familiasPAS}"
        id="family3#{rowk+1}" />
    </rich:column>
    <rich:column style="text-align:center">
        <h:outputText value="cargando..." id="totalfam#{rowk+1}" />
        <h:outputText value="#{fila.totalFamilias}" />
    </rich:column>
</rich:subTable>

And I want it to be generated in some way more like:

<rich:subTable id="tb" var="fila" value="#{AdministrarDMPermanente.listaDistribucionRecHum}" rowKeyVar="rowk">
    <rich:column style="text-align:center">
        <h:outputText value="#{fila.familiasP}"
        id="family1#{rowk+1}" />
    </rich:column>
    <rich:column style="text-align:center">
        <h:outputText value="#{fila.familiasPA}"
        id="family2#{rowk+1}" />
    </rich:column>
    <rich:column style="text-align:center">
        <h:outputText value="#{fila.familiasPAS}"
        id="family3#{rowk+1}" />
    </rich:column>
    <rich:column style="text-align:center">
        <h:outputText value="cargando..." id="totalfam#{rowk+1}" />
        <rich:jQuery
        selector="[id^='frmCalculoRHP'][id$='totalfam#{rowk+1}']"
        query="[id^='frmCalculoRHP'][id$='family1#{rowk+1}']+   
        [id^='frmCalculoRHP'][id$='family2#{rowk+1}']+
        [id^='frmCalculoRHP'][id$='family3#{rowk+1}']" />
        <ui:remove>
            <h:outputText value="#{fila.totalFamilias}" />
        </ui:remove>
    </rich:column>
</rich:subTable>

When I try this approach get some javascript code with brackets changed to entities:

<script type="text/javascript">//<![CDATA[
 {
    var selector = "[id^=\'frmCalculoRHP\'\x5D[id$=\'totalfam1\'\x5D";
    try {
        selector = eval("[id^=\'frmCalculoRHP\'\x5D[id$=\'totalfam1\'\x5D");
    } catch (e) {}
    jQuery(selector).[id^='frmCalculoRHP'][id$='family1']+[id^='frmCalculoRHP'][id$='family2']+[id^='frmCalculoRHP'][id$='family3'];
}
//]]>
</script>

On my research I found that it could be done in some similar way to:

function totalFrom() {
 var table = document.getElementById(document.querySelector('[id^="frmCalculoRHP:table"]').id);
 for (var i = 3; i < table.rows.length; i++) {
     var total = 0;
     var firstColumn = table.rows[i].cells[6]
     var input = firstColumn.getElementsByTagName('span')[0];
     var value = parseInt(input.textContent);
     if (!isNaN(value)) {
      total += value;
     }
     var secondColumn = table.rows[i].cells[7];
     var input = secondColumn.getElementsByTagName('span')[0];
     var value1 = parseInt(input.textContent);
     if (!isNaN(value1)) {
      total += value1;
     }
     var thirdColumn = table.rows[i].cells[8];
     var input = thirdColumn.getElementsByTagName('span')[0];
     var value2 = parseInt(input.textContent);
     if (!isNaN(value2)) {
      total += value2;
     }
     var qry="[id^='frmCalculoRHP'][id$='totalfam"+(i-2)+"']";
     var resu=document.getElementById(document.querySelector(qry).id);
     resu.textContent=total;
 }
}

content table row starts on 3rd row that's why in the script you can see var i = 3.

Ruslan López
  • 4,433
  • 2
  • 26
  • 37
  • 1
    If you really have millions of family rows I dont think that calculating the sum at client side gives some performance impact. Leave it at server side, othewise all that javascript-code will mess up your ui code. For pagination I would leave it to the database, see [https://wiki.eclipse.org/EclipseLink/Examples/JPA/Pagination]. – frifle Oct 16 '15 at 08:21
  • Trough all the system I think it will... specially since we show individual quantities and the sun for every location and the state sum. – Ruslan López Oct 16 '15 at 15:50

1 Answers1

1

Your concrete problem is caused by using EL in id attribute which depends on a variable which is only available during view render time and not during view build time.

<rich:subTable ... rowKeyVar="rowk">
    <rich:column>
        <h:outputText id="family1#{rowk+1}" ... />
    </rich:column>
    <rich:column>
        <h:outputText id="family2#{rowk+1}" ... />
    </rich:column>
    <rich:column>
        <h:outputText id="family3#{rowk+1}" ... />
    </rich:column>
</rich:subTable>

The id attribute is evaluated during view build time. If you had inspected the generated HTML output, you'd have noticed that <h:outputText id="family1#{rowk+1}"> etc all generate the ID without the evaluted value of the EL expression like so <span id="formId:tableId:0:family1">. Basically, the actual IDs in HTML DOM tree are not what your JavaScript code expected.

This is elaborated in below related questions:


As to the concrete functional requirement, summing a few integers is extremely cheap in modern hardware (done in less than a microsecond; thousand times would be still less than a millisecond). It doesn't make sense to delegate it to the client side. You'd better spend time on fixing performance holes in other areas, such as attempting to eagerly display millions of records unfiltered and/or unpaginated. Fixing that has a much bigger positive impact in order of magnitude.

Below should do good for you.

<rich:subTable ...>
    <rich:column>
        <h:outputText value="#{fila.familiasP}" />
    </rich:column>
    <rich:column>
        <h:outputText value="#{fila.familiasPA}" />
    </rich:column>
    <rich:column>
        <h:outputText value="#{fila.familiasPAS}" />
    </rich:column>
    <rich:column>
        <h:outputText value="#{fila.familiasP + fila.familiasPA + fila.familiasPAS}" />
    </rich:column>
</rich:subTable>

If you really insist in delegating it to the client side, then better do like below with help of "abstract" style classes instead of narrow-mindedly with IDs, given that the parent <rich:dataTable> has a client ID matching your attempted selector [id^="frmCalculoRHP:table"], and that you apparently can't use $ due to some awkward RichFaces-related jQuery conflict:

<rich:subTable ...>
    <rich:column>
        <h:outputText styleClass="family" value="#{fila.familiasP}" />
    </rich:column>
    <rich:column>
        <h:outputText styleClass="family" value="#{fila.familiasPA}" />
    </rich:column>
    <rich:column>
        <h:outputText styleClass="family" value="#{fila.familiasPAS}" />
    </rich:column>
    <rich:column>
        <h:outputText styleClass="family-total" />
    </rich:column>
</rich:subTable>
<script>
    jQuery("[id^='frmCalculoRHP:table'] .rich-subtable-row").each(function(index, row) {
        var total = 0;
        jQuery(".family", row).each(function(index, cell) {
            total += parseInt(jQuery(cell).text()) || 0;
        });
        jQuery(".family-total", row).text(total);
    });
</script>

The .rich-subtable-row is just the autogenerated <tr class> of a <rich:subTable>.

Obviously, run above script after the table in the DOM (e.g. inline, or on document ready, or on window load.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555