knotckout js foreach appending rows automatically in IE 7 and 8 - javascript

I have a "mini-cart" in a eCommerce site that I am bulding. For the "mini-cart" i use foreach to add items to a table.
The "mini-cart" is a dropdown and it is rendered from a razor file. When i close the "mini-cart" and opens it again it automatically appends all the rows one more time. So everytime I close the "mini-cart" and opens it again. All the rows gets appended one more time.
When the cart is opened this code is run.
showCart = function () {
WebService.PostJson("/services/CartService.svc/GetCart", {},
function (cartDto) {
updateCart(cartDto);
postman.deliver('cartShown', 1);
},
function () {
});
};
The table looks like this.
<table width="100%" >
<thead data-bind="foreach: Items, stripe: Items, evenClass: 'even', oddClass: 'odd'">
<tr>
<td data-bind="text: ArticleNo">
</td>
<td data-bind="text: Name" style="width:390px;">
</td>
<td>
<input type="text" data-bind="value: Quantity" class="quantity"/>
</td>
<td class="cart-price-column">
<span class="cartRowSubTotal" style="display: none;" data-bind="text: SubTotal"></span>
</td>
<td>
Ta bort
</td>
</tr>
</thead>
</table>
updateCart does a Items.splice(0); so it should reload it self each time. But it does not seem to work i internet explorer 7 and 8.
Is there anyway to "clear" the table everytime? Or can the viewmodel figure this out on its own?
Thanks.
UPDATE:
It seemed that the splice method did not empty the array for some reason. When changed to cart.Items([]) it started to work.

Set cart.items(null);
Nothing to do with IE!

In IE, the length parameter is required. Leaving it empty causes the function to do nothing. Call it like this: Items.splice(0, Items().length).

Related

jQuery - .each() over dynamically removed rows - numbering is off

JSFiddle: https://jsfiddle.net/dxhen3ve/4/
Hey guys,
I've been trying to figure out the issue here for some time.
Essentially, I have a table with rows. You can add new rows (works fine). However, on the deletion of rows, I would like to re-number all of the rows below it (including all of their input names/ids within).
This works fine as I have it on the first time you click "remove" for any row.. say, if you have rows 0-4 and delete row 1, you will now have rows 0-3 and they will be numbered correctly--however, after that if you click remove again on another row, the numbers do not update
The indexes are getting mixed up some how and it almost seems like it's not recognizing that I've removed an element from the DOM.. when I console.log the indexes everything looks fine.
As an example:
- Add 5 rows (0-4)
- Remove row #1 (the rows below get updated as they should).
- Remove the new row #1, and you will see that row #2 takes its place instead of changing to row #1.
- In the function 'renumber_budget_rows', the if statement seems to get skipped for that row #2, even though I feel like it should meet the conditions (and is present if I console.log(item)
What am I missing? https://jsfiddle.net/dxhen3ve/4/
** Update: Just wanted to update that I have a true resolution that works, which is great! However, I am more interested in knowing WHY my solution is failing. At the moment, the best I have, from the correct answer, was that my indexes were misaligned. I'm going to take a new look at them.
HTML
<script type="text/template" id="budget_row-template">
<tr id="budget_row-{{index}}" class="budget-row" data-budget-index="{{index}}">
<td class="budget-line">{{index}}</td>
<td><input type="text" name="budget_description-{{index}}" id="budget_description-{{index}}" class="budget-description" /></td>
<td><input type="text" name="budget_amount-{{index}}" id="budget_amount-{{index}}" class="budget-amount" /></td>
<td>
<select name="budget_costcode-{{index}}" id="budget_costcode-{{index}}" class="budget-costcode">
<option>-- Select Cost Code</option>
</select>
</td>
<td><i class="fa fa-share"></i></td>
<td>remove</td>
</tr>
</script>
<div class="table-scroll-container">
<table class="table table-striped table-bordered table-hover tablesorter" id="budget-display">
<thead>
<tr>
<th>Line #</th>
<th>Description</th>
<th>Amount</th>
<th>Cost Code</th>
<th data-sorter="false"></th>
<th data-sorter="false"></th>
</tr>
</thead>
<tbody id="test">
<tr id="budget_row-0" class="budget-row" data-budget-index="0">
<td class="budget-line">0</td>
<td><input type="text" name="budget_description-0" id="budget_description-0" class="budget-description" /></td>
<td><input type="text" name="budget_amount-0" id="budget_amount-0" class="budget-amount" /></td>
<td>
<select name="budget_costcode-0" id="budget_costcode-0" class="budget-costcode">
<option>-- Select Cost Code</option>
</select>
</td>
<td><i class="fa fa-share"></i></td>
<td></td>
</tr>
</tbody>
</table>
</div>
<div class="text-align-center">
<i class="icon icon-plus icon-white"></i> Add Line Item<br />
</div>
JS
function renumber_budget_rows(removed) {
$('#budget-display tbody .budget-row').each(function(indite, item) {
var ti = $(item).data('budget-index');
if( ti > removed ) {
ti--;
//console.log(item);
$(item).attr('id', 'budget_row-'+ti);
$(item).attr('data-budget-index', ti);
$(item).find('.budget-line').html(ti);
$(item).find('.budget-description').attr({ 'name': 'budget-description-'+ti, 'id': 'budget-description-'+ti });
$(item).find('.budget-amount').attr({ 'name': 'budget-amount-'+ti, 'id': 'budget-amount-'+ti });
$(item).find('.budget-costcode').attr({ 'name': 'budget-costcode-'+ti, 'id': 'budget-costcode-'+ti });
$(item).find('.add-budget-child').attr({ 'id': 'budget_row-addparent-'+ti, 'data-budget-index': ti });
$(item).find('.trash-budget-row').attr({ 'id': 'budget_row-'+ti+'-trash' });
$(item).find('.trash-budget-row').attr('data-budget-index', ti);
}
});
}
var budget_index = 0;
$('.add-budget-row').click(function(e) {
budget_index++;
e.preventDefault();
var budget_html = $('#budget_row-template').html();
budget_html = budget_html.replace(/{{index}}/g, budget_index);
$('#budget-display tbody').append(budget_html);
});
$('#budget-display').on('click', '.trash-budget-row', function(e) {
var removed = $(this).data('budget-index');
$(this).closest('tr').remove();
console.log(removed);
renumber_budget_rows(removed);
budget_index--;
});
While you are deleting the row, after a row deletion, you can iterate through every tr using .each() function and change the attributes based on the index i value.
$('#budget-display').on('click', '.trash-budget-row', function(e) {
var removed = $(this).data('budget-index');
$(this).closest('tr').remove();
$('tbody tr').each(function(i){
$(this).find('td').eq(0).text(i);
$(this).attr("data-budget-index",i);
$(this).attr("id","budget-row-" + i);
});
budget_index--;
});
Working example : https://jsfiddle.net/DinoMyte/dxhen3ve/5/

Remove invalid row from table

I've table with inline create (when user click on new button it create new empty row first in the table, which he can put data inside),when user click on save I go to the controller and check if this values are valid,if yes store them on the DB.if not I raise exception to the UI and not store the data.The problem is that the new row is not stored but I see it in the UI. just when I refresh the page in the browser the line was omitted..
My question is if there is a way by code search for the first line of the table and remove it explicitly from the UI?
I try like the following but its not refresh the table,any idea?
$("table:first").find("tr:first").remove();
The following should work with a general table.
HTML
<table>
<tbody>
<tr>
<td>row1</td>
</tr>
<tr>
<td>row2</td>
</tr>
<tr>
<td>row3</td>
</tr>
</tbody>
</table>
<br />
<br />
<input type="submit" id="btnAdd" value="add"></input>
<input type="submit" id="btnRemove" value="remove"></input>
jquery
$(function () {
$("#btnAdd").click(function () {
$("table:first > tbody").prepend('<tr><td>new row</td></tr>');
});
$("#btnRemove").click(function () {
$("table:first tr:first-child").remove();
});
});
Working example: http://jsfiddle.net/5NxL4/

need to click twice on an added element

I have jQuery to add a some content to a page:
$('#upper').on("click",'a',function(e){
e.preventDefault();
var url = $(this).attr('href');
if (!$(this).hasClass('new')) {
$('#lower').load(url);
}
});
It works fine but the added content includes some links. I.e.:
<table class="report">
<tr>
<td class="report">
PG010000001
</td>
<td class="report">società</td>
<td class="report">LSSF Consulting SRL</td>
<td class="report">
PF010000002
</td>
<td class="report">IC - Intestatario di un credito</td>
</tr>
</table>
First time I click on the href nothing happens. Second time it works fine . Looks like I miss something in the code I have posted to focus the added content. Any advice? Will try to create a fiddle in the meanwhile (never done before...)
Thanks in advance!

Can I call a jquery or javascript function in grails g:each element?

I want to call a jquery function in grails g:each element, i'm using a function call on page load to filter a table which has a loop as follows
<g:each in="${sampleTypes}" status="i" var="sampleType">
<div class="uniq">${sampleType}</div>
<table id="sampleTable">
<thead>
<tr>
<th class="no-sort"><g:message code="labWorkItem.testUnit.label"
default="CustomerId" /></th>
<th class="no-sort"><g:message code="labWorkItem.testUnit.label"
default="OrderNo" /></th>
<th class="no-sort"><g:message code="labWorkItem.testUnit.label"
default="DateCreated" /></th>
<th class="no-sort"><g:message code="labWorkItem.testUnit.label"
default="Test unit" /></th>
<th class="no-sort no-visible"><g:message
code="labWorkItem.sampleType.label" default="Sample Type" /></th>
</tr>
</thead>
<tbody>
<g:each in="${labWorkItemInstance}" status="a" var="labWorkItem">
<tr class="${(a % 2) == 0 ? 'even' : 'odd'}">
<td>
${labWorkItem?.order?.customer?.customerId}
</td>
<td>
${labWorkItem?.order?.orderNo}
</td>
<td>
${labWorkItem?.order?.dateCreated}
</td>
<td >
${labWorkItem?.testUnit}
</td>
<td id = "labSample">
${labWorkItem?.testUnit?.sampleType}
</td>
</tr>
</g:each>
</tbody>
</table>
<g:textField name="singleValue" value="Blood" id="someHiddenField"/>
</g:each>
i am using the class "uniq" to filter the table
function typeSampleCollected() {
jQuery.fn.dataTableExt.afnFiltering.push(function(oSettings, aData,
iDataIndex) {
if (oSettings.nTable.id != "sampleTable") {
return true;
}
var uniq = jQuery("div.uniq").html();
alert(uniq);
jQuery("#someHiddenField").val(uniq);
var someHiddenField = jQuery("#someHiddenField").val()
if(someHiddenField){
//var sampleValue = jQuery("#someHiddenField").val();
//alert(sampleValue.toString());
if (someHiddenField != aData[4]){
console.log("sampleType"+someHiddenField);
console.log("aData"+aData[4]);
return false;
}
}
else{
console.log("else condition");
}
return true;
});
}
The problem is, it executes at the first on page load, only the first data of the loop executed others remains the same, i want the remaining data also to execute.
jQuery + HTML answer.
Your generated HTML will be wrong because the id "someHiddenField" will be duplicated. An id has to be unique within the HTML document. View the source of the document to check. Copy into an IDE or use w3c validator to check.
Once you have unique ID's you need to iterate over them and run your filter.
I am not sure whether by filter you are sending information back to the server. i.e. text blood results in only content relating to blood being displayed. I don't see any events in your code. Is the code incomplete?
A similar thing - click on an icon to only display items relating to that class. View code:
<g:each in="${assetTypes}" status="i" var="assetType">
<li>
<span class="atext">${assetType.name?.encodeAsHTML()}</span>
</li>
</g:each>
I used delegate() to late-bind jQuery to the click - use go() after 1.7 jQuery. The javascript had to be in a grails view because I use the gsp tags. With delegate() it could be anywhere:
/* Change asset types */
jQuery('body').delegate('[name=assetTypeSelector]', 'click', function() {
var newAssetIconId = jQuery(this).attr("id").split("-").pop();
// set the asset type.
${remoteFunction(controller: 'assetType', action: 'selector', name: 'assetTypeSelector', update: 'assetTypeSelector', params:'\'currentAssetTypeIconId=\' + newAssetIconId')}
});
Alternatively we have had a lot of success with DataTables which is a more complete solution.

Combining Rows in Javascript

I'm looking for some help on the Javascript angle of this problem. I have a table that goes like...
<table>
<tbody>
<tr> (Row 1)
<td colspan="3">
<p>This Says Something</p>
</td>
</tr>
<tr> (Row 1a)
<td>
<select option>
</td>
</tr>
<tr> (Row 2)
<td colspan="3">
<p>This Says Something</p>
</td>
</tr>
<tr> (Row 2a)
<td>
<select option>
</td>
</tr>
<tr>
<td colspan="3">
<p>This Says Something</p>
</td>
</tr>
<tr>
<td>
<select option>
</td>
</tr>
<tbody>
</table>
There are actually more like 20 rows and row a's but I didn't think I'd want to copy them all.
I basically need to add a container row (a single row) around every two rows (# and #a). Something like:
<tr> (Container Row 1)
<td>
+<tr> (Row 1)
+<tr> (Row 1a)
</td>
</tr>
It needs to cycle through the whole table. Somehow it has to retain the HTML data inside since all of the "a"s have options.
I hope this makes sense...
Any help would be greatly appreciated, as I'm at a loss. I'm novice at best at javascript and am struggling my way through the DOM and TOM methods.
Thank you so much in advance for any help or headway.
[EDIT] For clarification, the table is already constructed from a third party database, I am editing it after it's constructed. I guess this clarifies why it would have to be javascript to be done through the DOM.
Embed another table:
<tr> (Container Row 1)
<td>
<table>
<tr><td>(Row 1a)</td></tr>
<tr><td>(Row 1b)</td></tr>
</table>
</td>
</tr>
Or if you are wanting to do that via Javascript, you can give the parent <td> an id and set it's innerHTML.
<tr> (Container Row 1)
<td id='rowX'>
</td>
</tr>
document.getElementById('rowX').innertHTML = "<table><tr><td>(Row 1a)</td></tr><tr><td>(Row 1b)</td></tr></table>";
As mentioned in another answer you can't add tr elements directly in td like you are trying.
You would first create an inner table.
If you were using jQuery you would do something like this:
//setup some click actions just to prove that they remain attached even after moving
$('#outterTable tr').click(function(){
alert('You clicked on row: '+$(this).text());
});
//update the table (group each even row with the one after it)
$('#outterTable tr:even').each(function() {
var $tr1 = $(this),
$tr2 = $tr1.next('tr'),
$t = $('<table></table>');
$('<tr></tr>').append($t).insertBefore($tr1);
//click actions will remain attached
//if that is not required, than use $tr1.remove()
$t.append($tr1).append($tr2);
});​
See this live jsFiddle example.
without jQuery it may look like that:
<script type="text/javascript">
<!--
function fx(table)
{
var tmp=document.createElement('table');
tmp.appendChild(document.createElement('tbody'))
while(table.rows.length)
{
if(table.rows.length%2==0)
{
var wrapper=tmp.lastChild.appendChild(document.createElement('tr'));
wrapper.appendChild(document.createElement('td'));
wrapper.getElementsByTagName('TD')[0].appendChild(document.createElement('table'));
wrapper.getElementsByTagName('TD')[0].lastChild.appendChild(document.createElement('tbody'));
}
wrapper.getElementsByTagName('TD')[0].lastChild.lastChild.appendChild(table.getElementsByTagName('TR')[0])
}
table.parentNode.replaceChild(tmp,table);
tmp.setAttribute('border',1);
}
window.onload=function(){fx(document.getElementsByTagName('table')[0]);}
//-->
</script>
Example#jsFiddle
But: why do you need this grouping?
If the only benefit is a visible grouping I would prefer to do this by setting the borders of the cells .
Give all cells a border and to the even a border-top:none / to the odd a border-bottom: none

Categories

Resources