Totalling an ExtJS editable grid's input fields - javascript

This question regards ExtJS 3 editable grids. I have a need to total the input fields of one column of an editable grid. There is the store's sum method but sometimes the data in the input fields is not necessarily in the store; for instance you might make an entry in the input field and then click a button on the toolbar.
The "traditional" way in this case seems to be to set up the appropriate listener and commit the content of the input field to the store, but this can get convoluted and seems downright unnecessary if as a result of clicking the toolbar button you will ultimately get re-directed elsewhere anyway. Is there another solution?

An alternative is to gather up all the input fields using the oft-overlooked Ext.query functionality; the following does this for the second column of a grid ('.x-grid-col-0' would be the first column):
var qtyFields = Ext.query('.x-grid3-col-1 input', myGrid.getView().mainBody.dom);
var total = 0;
var qty;
for (var i=0, n=qtyFields.length; i<n; i++) {
qty = parseFloat(qtyFields[i].value);
total += isNaN(qty) ? 0 : qty;
}

An alternative that doesn't rely on knowing classnames in the HTML is to get the information from the editing plugin.
var fields = grid.findPlugin('rowediting').editor.form.getFields();
var total = 0;
for (var i=0, n=fields.getCount(); i<n; i++) {
var qty = parseFloat( fields.getAt(i).getValue() );
total += isNaN(qty) ? 0 : qty;
}

Related

Is it possible to add an eventlistener on an object that is modified through javascript?

I have a table where the sum of the rows are updated with javascript when a user changes the quantity within a popup modal. I want to be able to update the grand total of all my rows every time the total of a row change.
I have tried to add an event listener onchange and oninput on the total of each of my row but it doesn't work. I have tried to find a workaround by adding an event listener on the quantity the user is changing but to no effect. I have tried the code in the console and it is working but the event-listener doesn't seem to trigger.
document.getElementById("work_quantity_<%= #work_detail.id %>").addEventListener("change", grandTotal)
var work_detail_quantity = document.getElementById("work_quantity_<%= #work_detail.id %>").value;
var work_detail_block = document.getElementById("work-<%= #work_detail.id %>");
var quant = work_detail_block.getElementsByTagName('td')[1];
quant.innerHTML = work_detail_quantity+" <%= #work_detail.work_resource.unit_measure%>";
function grandTotal() {
var sub_total = document.getElementById("subtotal_lign").innerHTML
var table = document.getElementById("invoice_summary"), sumVal = 0;
for(var i = 1; i < table.rows.length; i++)
{
sumVal = sumVal+ parseFloat(table.rows[i].cells[4].innerText.replace(/[^0-9-.]/g, ''));
}
document.getElementById("subtotal_lign").innerHTML = sumVal.toFixed(2) + ' €' ;
};
If someone could give me a hint it would be more than appreciated, I am a rookie in javascript. Thank you
It's not a good idea to parse the DOM like that to get the values.
Solution 1
You get the object that contains the values of your table form Ruby. Something like that:
var works = <%= #works.to_json %>;
Then, when a user updates his order, you first update the object and then you update the table based on the updated object.
Solution 2
When a user update his order, you send the update to your server and you update the table in the server side.
Solution 3 (Not recommended)
If you still want to do it by parsing the DOM, have a look at MutationObserver to listen the changes in your table.
I confess, I haven't tried this per say; but I did try in jQuery a similar thing, mainly because your example lacks the detail of the table;
but you need to trigger the event when a change occurs, I assume here your not using standard input fields, so are you using like <td contenteditable></td>, they can't be monitored for change, but keyup and blur can be monitored, so if you want to trigger the calc on blur, when that event occurs, trigger your table sum event...
I'd actually use jQuery for this but here goes ...
document.getElementById("work_quantity_<%= #work_detail.id %>").addEventListener("change", grandTotal)
...
function grandTotal() {
var sub_total = document.getElementById("subtotal_lign").innerHTML
var table = document.getElementById("invoice_summary"), sumVal = 0;
for(var i = 1; i < table.rows.length; i++) {
sumVal = sumVal+ parseFloat(table.rows[i].cells[4].innerText.replace(/[^0-9-.]/g, ''));
}
document.getElementById("subtotal_lign").innerHTML = sumVal.toFixed(2) + ' €' ;
};
// something like this
(function() {
// i assume it's cell[4] that is being edited, and it's like contenteditable
var table = document.getElementById("invoice_summary");
for(var i = 1; i < table.rows.length; i++) {
/* assign listener on the cell,
when it blurs, trigger the update,
you could also monitor keyup
*/
table.rows[i].cells[4].addEventListener("blur", function(e) {
document.getElementById("work_quantity_<%= #work_detail.id %>").dispatchEvent("change", grandTotal);
});
}
})();
Updated
I thought that what I wrote below was working but actually the grand total is always one computation behind as it is doing the sum of the rows before the total of the modified row has been updated after a change of quantity.
Ok it is working with a workaround, but I still believe MutationObservers could be a good way to resolve this kind of problem in the future
Good tutorial: for MutationObservers
To make my event listener work I add to define it before the change in the quantity is made by the user (stupid isn't it)
1) Add the event listener to the object you are tracking
document.getElementById("work_quantity_<%= #work_detail.id %>").addEventListener("input", grandTotal)
2) This is where my object is changed with javascript
var work_detail_block = document.getElementById("work-<%= #work_detail.id %>");
var quant = work_detail_block.getElementsByTagName('td')[1];
quant.innerHTML = work_detail_quantity+" <%= #work_detail.work_resource.unit_measure%>";
3) I define the function which is called in the step 1
function grandTotal() {
var sub_total = document.getElementById("subtotal_lign").innerHTML
var table = document.getElementById("invoice_summary"), sumVal = 0;
for(var i = 1; i < table.rows.length; i++)
{
sumVal = sumVal+ parseFloat(table.rows[i].cells[4].innerText.replace(/[^0-9-.]/g, ''));
}
document.getElementById("subtotal_lign").innerHTML = sumVal.toFixed(2) + ' €' ;
};

how to get previously selected rows of a bootstrap dataTable while searching

I have used a bootstrap DataTable to show data. At the same time i have a HTML checkbox as a column of that dataTable. I have a submit button to get the selected checkbox's value to do some stuff. I am fetching the following problem while select checkboxes under searching.
When i select all checkboxes and Click Save button I got all the values. It works as my expectation.
But Unexpected occures when i search via built-in search box of bootstrap dataTable.Here is the pictorial view
In this case when i press Save i got just the two value though all other values are still selected. How can i get all the selected values while searching
Here is my jquery code of Save button of getting the checkboxes value
$("#btnSave").click(function () {
var checkboxes = document.getElementsByName('foo');
var vals = "";
for (var i = 0, n = checkboxes.length; i < n; i++) {
if (checkboxes[i].checked) {
vals += "," + checkboxes[i].value;
}
}
....
//Other stuff
....
}
Please help me.
When you are doing a search, it is going to check only for the rows, that satisfy the given criteria. So your search logic works like this
if(searchCriteria==true)
{
showValue
}
Even if you had previously selected values it won't be showing all of them, because they don't satisfy the given criteria. Now in this case when you click on Save, it is taking only those rows in the datatable which are checked and satisfy the given criteria, as per your java script.
Now if you still wish, that all selected values should be taken in, there is one way you can try, put the selected values in some kind of List or Set, and place the list object in session. So everytime you click on Save, you can retrieve the List from the session. But I really don't see why you would want to do it, unless you need to retrieve the selected values every time.
I got my job done like this
var table = $('#tblReportList').dataTable();
When i was searching through dataTable, i was getting just the selected values matching the search criteria. But the previously selected values was disappeared. To get the previously selected value i used the following code...
table.$('input[type="checkbox"]').each(function () {
if (!$.contains(document, this)) {
if (this.checked) {
previousVal = previousVal + ',' + this.value;
}
}
});
Now, for getting the selected values of search I used the previous code posted before. And that was
var checkboxes = document.getElementsByName('foo');
var vals = "";
for (var i = 0, n = checkboxes.length; i < n; i++) {
if (checkboxes[i].checked) {
vals += "," + checkboxes[i].value;
}
}
So all selected checkbox's values are
var allValues=previousVal+vals;

In Javascript, when using an array.push method to add items to a list how do I get it to sort?

Edit: This is for use in PDF scripting. I use Acrobat pro but have been using Foxit as of late because it is much quicker than Acrobat.
Below is the exact script I have to add items to a dropdown list titled TalentList. The following is the script in a button titled "Add new Talent"
aray=[this.getField("TalentName").value];{aray.push(this.getField("TalentCost").value)}
var x = aray[0] + "," + aray[1];
this.getField("TalentList").insertItemAt(this.getField("TalentNa
me").value,x)
{this.getField("TalentName").value="";}
{this.getField("TalentCost").value="";}
{this.getField("TalentName1").value=" ";}
Where I have the fields TalentName and TalentCost and I input values into those then click the button to add them to the dropdown TalentList. The script works fine as is. But, the problem I am having is every time I add a new item it adds it to the top of the list and does not sort it when I want it to be sorted. Is there something I can add to the above script in the button to cause this to happen automatically every time?
Please note that I have the "Sort Items" box checked in the properties of the TalentList dropdown.
I believe I would use insertItemAt (as told by someone else) but I don't know the method or the correct parameter to use.
I think you are looking to sort the listbox on your PDF form? If so you could do it like this:
aray=[this.getField("TalentName").value];
aray.push(this.getField("TalentCost").value);
var x = aray[0] + "," + aray[1];
this.getField("TalentList").insertItemAt(this.getField("TalentName").value,x);
this.getField("TalentName").value="";
this.getField("TalentCost").value="";
count = this.getField("TalentList").numItems;
var l = this.getField("TalentList")
for (numsorted = 0; numsorted < count; numsorted ++){
max = l.getItemAt(numsorted, false)
maxindex = numsorted;
for (i=numsorted; i<count; i++){
item = l.getItemAt(i, false);
if (item > max) {
max = item;
maxindex = i;
}
}
item = l.getItemAt(maxindex, false);
l.deleteItemAt(maxindex);
l.insertItemAt(item, numsorted);
}

Updating Table Totals with Javascript

I recently added some functionality to a table using jQuery to allow users to add/delete table rows on the fly which is working well. However in doing so I broke the automatic calculation of totals based on the values enter into some cells in the table rows and for the life of me I can't work out how I broke it nor how to fix it.
I've also noticed that deleting a table row doesn't update the totals either (this was the new feature that I added).
Here's the script that runs that calculates the average number of hours per week for a single row - it should also update the total moderate or high field at the bottom of the table at the same time:
$(window).load(function(){
//Sum Table Cell and Map
$('#lastYear')
.on('change', 'select', calc)
.on('keyup', 'input', calc);
function calc(){
$('#lastYear tr:has(.risk)').each(function(i,v){
var $cel = $(v.cells);
var $risk = $cel.eq(1).find('option:selected').val();
var $numb = $cel.eq(2).find('input').val();
var $weeks = $cel.eq(3).find('input').val();
var $avg = ($numb * $weeks) / 52;
var $avgRounded = Math.round( $avg * 10 ) / 10;
$cel.eq(4).find('input').val($avgRounded);
});
var tot = {high:0,moderate:0};
$('#lastYear tr:has(.risk) option:selected')
.map(function(i){
var el = $(this).val();
var qty = parseFloat($('#lastYear tr:has(.risk)').eq(i).find('td:last').prev().find('input').val());
if (!tot.hasOwnProperty(el)) {
tot[el] = 0;
}
tot[el] += qty
return tot;
}).get();
// console.log(tot);
$('#textfield4').val(tot.moderate.toFixed(1));
$('#textfield5').val(tot.high.toFixed(1));
}
});//]]>
I've setup a jsFiddle that shows the current functionality - you can see if generates the averages but generates a 'NaN' result for the totals when entering data, and when you click the Delete button it should also update the totals too.
Update: I think problem is that the script is looking for the last table cell then going to the previous one to get the value. This used to work when the average cell was the 2nd last cell, but not it's the 3rd last cell and I don't know how to look for that cell by name/id etc. Any ideas?
I'm new to Javascript and have spent almost a day trying to work this out so really appreciate some assistance with this.
Working jsFiddle Demo
You have 2 td after your desired input. So you must call prev method twice:
var qty = parseFloat(
$('#lastYear tr:has(.risk)').eq(i).find('td:last')
.prev().prev() // call prev method twice
.find('input').val()
);
Mark your cells that store the average with eg a CSS class, than use that to find the cell within the row (rather than relying on td:last).
eg:
var row = $('#lastYear tr:has(.risk)').eq(i); // if this is the TR you want..
var avgCell = row.find('td.averageCell'); // find a <TD class="averageCell">.
var avgValue = parseFloat( avgCell.val());
In general, relying on positions to find things is brittle & fairly difficult to maintain; it's better to use IDs (where there is only one) or CSS classes (where there may be multiple.) Both of these are efficient, as well.
Hope this helps!

Calculate total of input text fields on a datatable in jsf

I have a JSF <h:datatable> with two columns.
column 1 : <h:outputText>, gets populated from bean data.
coulmn 2 : <h:inputText> boxes.
There is a "Total" field outside the table and I want to have it show the total of fields as entered in column2 in realtime. So I did searching around and found out that I need a JavaScript to do this. I am however quite new to JS.
Where I am confused is how to access the value of the input text box. What I have done so far:
function totalFrom() {
var element = document.getElementById('transferFundsForm:fundsFromTable:0:from_transferAmt');
if(element != null){
document.forms['transferFundsForm']['transferFundsForm:totalFrom'].value = document.forms['transferFundsForm']['transferFundsForm:totalFrom'].value+ element;
}
}
As far as I understand, the transferFundsForm:fundsFromTable:0, here 0 represents the first row. How do I refer to the element in column that is being edited?
I have called this function on onblur event of the textBox in column.
Also I read that I can use <f:ajax> for this as well, but I am using JSP instead of Facelets, so I can't use <f:ajax>.
The HTML DOM element representation of <table> element has a rows property which gives you an array of all <tr> elements. The HTML DOM representation of this <tr> element has a cells property which gives you an array of all <td> elements.
So, provided that the 2nd column of the table contains only one <input> element which holds the value you'd like to sum up, and that totalFrom is an <input> element (at least, you're attempting to set the value property and not innerHTML), you could achieve this as follows:
function totalFrom() {
var table = document.getElementById('transferFundsForm:fundsFromTable');
var total = 0;
for (var i = 0; i < table.rows.length; i++) {
var secondColumn = table.rows[i].cells[1];
var input = secondColumn.getElementsByTagName('input')[0];
var value = parseInt(input.value);
if (!isNaN(value)) {
total += value;
}
}
document.getElementById('transferFundsForm:totalFrom').value = total;
}
If the totalFrom is however a <h:outputText id="totalFrom">, then set it as follows instead:
document.getElementById('transferFundsForm:totalFrom').innerHTML = total;
Why don't you use the jsf server side event or user
http://livedemo.exadel.com/richfaces-demo/
it also provide you builtin ajax functionality

Categories

Resources