jQuery keeps number of cloned rows after reset - javascript

I have a form in a popup, that presents a simple table with header and one row, with a + button the user can click to add (clone) a row.
This works perfectly, the user submits the form, data is read - no problem.
The form is reset after submission with:
$('#entryTable').find("tr:gt(1)").remove(); // keep header and first row
$('#entryForm')[0].reset();
If the user calls the form immediately, the forms "seems" correctly reset, all fields look like new.
If the user clicks on the + (clone) button - it adds the number of rows added previously, and not 1, as if something (but what ?????) was not reset.
my clone function is
$(".cloneprod_add").on('click', function(e) {
e.preventDefault();
var $tr = $(this).closest('.tr_clone'); //only first row has +
var idtr= parseInt($tr.attr("id")); // checked it is always id of row1
var $clone = $tr.clone(true);
cindex++;
$clone.find(':text').val('');
$clone.attr('id', idtr+(cindex) );
$clone.find("*").each(function() {
var id = this.id || "";
var match = id.match(regex) || [];
if (match.length == 3) {
this.id = match[1] + (cindex);
}
});
$tr.after($clone);
$("#addline_"+cindex).addClass("uk-hidden"); // remove +
$("#delline_"+cindex).removeClass("uk-hidden"); // add -
});
cindex is correctly reset to 0 when the form is called again. What else should be reset ? where is my error ?
Thanks for your advices
(EDIT)
I am adding the form . I am using UIkit (www.getuikit.com), a node server and the form comes from a template in jade - I copied the HTML from the browser
<form id="entryForm" action="" class="uk-form uk-form-stacked" >
<div id="entryDialog" class="uk-modal-dialog uk-modal-dialog-large">
<fieldset data-uk-margin="">
<div class="uk-panel uk-panel-box uk-panel-box-primary uk-margin-small-top">
<div class="uk-form-row"><div class="uk-grid"><div class="uk-width-1-1">
<table id="entryTable" class="uk-table">
<thead><tr><th>lot</th><th>code</th><th>num</th>
<th>qte</th><th>unit</th><th>pos</th><th>cont</th>
<th></th></tr>
</thead>
<tbody><tr id="0" class="tr_productclone">
<td><input type="text" name="lot" id="lot_0" class="lot"></td>
<td><input type="text" name="code" id="code_0" class="code"></td>
<td><input type="text" name="num_0" id="num_0" class="num"></td>
<td><input type="text" name="qte_0" id="qte_0" class="qte"></td>
<td><select name="unit_0" id="unit_0" class="unit"><option value="items">pezzi</option></select></td>
<td><select name="pos_0" id="pos_0" class="pos"><option value="1">piece1</option></select></td>
<td><select name="cont_0" id="cont_0" class="cont"></select></td>
<td><button id="addline_0" class="uk-button cloneprod_add"></button>
<button id="delline_0" class="uk-button cloneprod_del"></button></td>
</tr>
</tbody></table></div></div></div></div>
<div class="uk-modal-footer"><button id="btnSave" type="submit" class="uk-button ">save</button></div>
</fieldset></div></form>

I found a solution to my problem. I don't know the why and hows, but at least it works.
I had the impression that the remove was keeping the information of the number of cloned rows and was thus putting that number back. I thought about reloading the table in the modal, so that it would be "fresh without memory".
In the end, I wrote the definition of the table in the js script and not in the Jade template. The table is rebuilt overtime the form is called, and no more memory ghosts.
It works, but I still don't understand why those ghosts.

Related

How to delete selected rows of an html table based on a checkbox column using Javascript

I have made a simple html table with a column of checkboxes, as well as some other columns. Above that, is a delete button that will call a service function I have already made, but I want it to delete every checked box that the column has. How would I send my delete function an array of checked "projects"?
Below is the delete button code:
<div class="panel-body">
<button class="btn pull-right btn-danger button-project-request form-group" value="Submit" ng-click=deleteCheckedProjects()>Delete</button>
</div>
Below is the table code:
<tbody>
<tr ng-repeat="project in projects">
<td><input type="checkbox" name="checkbox" value="project"/></td>
<td ng-click=editRequestModule(project)>{{project.project.projNm}}</td>
<td>{{convertMilliseconds(project.project.strtDt)}}</td>
<td>{{convertMilliseconds(project.project.endDt)}}</td>
<td>{{project.project.statusId.staDescription}}</td>
</tr>
</tbody>
Any advice on how to go about this? Maybe in Javascript, establish a two way bounded array that holds all the checked boxes?
I think the simplest implementation would be to replace your checkboxes with individual delete buttons:
<tr ng-repeat="proj in projects">
<td><button ng-click="remove(proj)">delete</button><td>
<td>{{proj.name}}</td>
<!-- ... -->
</tr>
Where remove is something to the effect of:
$scope.remove = function(proj){
$scope.projects.splice($scope.projects.indexOf(proj), 1);
}
If you're set on checkboxes, you can include some sort of "staged for delete" flag in your project model:
<tr ng-repeat="proj in projects">
<td><input type="checkbox" ng-model="proj.will_delete"></td>
<td>{{proj.name}}</td>
<!-- ... -->
</tr>
<button ng-click="remove_staged()">delete</button>
Where remove_staged is something like:
$scope.remove_staged = function(){
$scope.projects = $scope.projects.filter(proj=> !proj.will_delete);
}
You could add a 'project-checkbox' class to each checkbox, then use
var checkboxes=document.getElementsByClassName('project-checkbox');
to get every checkbox.
From there you can create an empty array, loop through the checkboxes array and check if it has been checked before adding it to the new array.
var checkboxes=document.getElementsByClassname('project-checkbox');
var checkedProjects=[];
for(var i=0;i<checkboxes.length;i++){
if (checkboxes[i].checked) {
checkedProjects.append(checkboxes[i]);
}
}
Now checkedProjects has every checked checkbox, and you can access the project it is contained in via checkedProjects[i].parentNode
A slightly more efficient way to do it would be to change the class name whenever it is checked or unchecked, then use document.getElementsByClassName() to get the checked class name only.
Here's how I would do it with JS (JQuery)
Add to every checkbox you want to track the class .myCheckboxClass
var deleteCheckedProjects = function() {
// this will return all checked checkboxes with class .myCheckboxClass
var checked = $('.myCheckboxClass:checkbox:checked')
// you can delete the input in the DOM
for (i = 0; i < checked.length; i++) {
checked[i].remove();
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input class="myCheckboxClass" type="checkbox" name="first">Hello Sir!<br>
<input class="myCheckboxClass" type="checkbox" name="first">I am not here</input><br>
<br>
<button onclick="deleteCheckedProjects()" type="button">Click Me!</button>
Alternatively you can just return the array:
var deleteCheckedProjects = function() {
// this will return all checked checkboxes with class .myCheckboxClass
return $('.myCheckboxClass:checkbox:checked')
}

Radio button selection is not working as expected

I am facing an issue with the existing code which is using html and JS. The page has a html table which has a radio button and 4 other fields. The table is created dynamically when we set values through controller. Also I can add new rows in the table which is done completly through JS. Now the problem is I inserted 2 rows from controller and added a new row through a button click. So I have 3 radio button now. If I select one radio button among the controller inserted row it selects only one, which is fine, If I select the 3rd one I can see two selected radio button, which is not correct. In any case it should select only one. when I take view source I am not seeing the entry for the javascript added new row, so I hope the problem is because of the Id generated ? please let me know how can I solve this issue?
Java controller
List <MyQuestion> fetchedQuestions = AppImpl.getMyQuestions(id);
for (int index=0; index< fetchedQuestions.size() ; index++){
MyQuestion questionsToAdd= fetchedQuestions.get(index);
questionList.add(questionsToAdd);
}
myqstntemplate.setMyQuestions(questionList);
templatebean.setQuestionTemplate(myqstntemplate);
}
JScode
function addRow(){
isDirty=true;
if(document.getElementById('value').value != 'value'){
rowNumber = document.getElementById('questionsSize').value;
document.getElementById('value').value='value';//other option is novalue
}
var questionId=document.getElementById('questionsSize').value;
questionId=parseInt(questionId)+1;
document.getElementById('questionsSize').value = questionId;
var newRow = $('<tr/>').attr('id', 'row' + rowNumber);
//Add some HTML to the row
newRow.html('<td><input type="hidden" id="newQuestion'+rowNumber+'" value="'+questionId+'"/><input id="question" name="question" type="radio" style="width:20px;" value="" onclick="setSelectedQuestion(\''+rowNumber+'\',\'new\')"/></td><td class="newRow" align="center" style="vertical-align: middle;">'+questionId+'<input type="hidden" value="newQuestion"/></td> <td colspan="2"><input style="width: 397px;" id="myQstnTemplate.questions['+rowNumber+'].question" name="myQstnTemplate.questions['+rowNumber+'].question" type="text" value="" maxlength="256" onclick="enableDirty()" style="text-align:left;"/><div id="pencil" title="Edit helptext" class="pencil-img fltRight" style="border: 0pt;padding-right: 15px; " onclick="displayHelpText('+rowNumber+',\'new\');return false;"></div><input id="myQstnTemplate.questions['+rowNumber+'].questionId" name="myQstnTemplate.questions['+rowNumber+'].questionId" type="hidden" value="-1" /><input style="width: 350px;" id="myQstnTemplate.questions['+rowNumber+'].helptext" name="myQstnTemplate.questions['+rowNumber+'].helptext" type="hidden" value="" onclick="enableDirty()" style="text-align:left;"/></td><td><select style="width:100px;" id="myQstnTemplate.questions['+rowNumber+'].schemeId" name="myQstnTemplate.questions['+rowNumber+'].schemeId"><option value="-1">Select</option><c:forEach var="answer" items="${myqstionTemplateBean.answerTypes}"><option title="${answer.questions}" value="${answer.displaySchemeId}">${answer.answerTypeName}</option></c:forEach></select></td> <td><div><input type="hidden" id="question'+rowNumber+'" name="question'+rowNumber+'" value="'+rowNumber+'"/><select id="myQstnTemplate.questions['+rowNumber+'].ansId" name="myQstnTemplate.questions['+rowNumber+'].ansId" style="width:100%"><option value="-1">Select</option><c:forEach var="ansId" items="${myqstionTemplateBean.ansIds}"><option value="${ansId}" <c:if test="${ansId eq question.ansId}">selected</c:if>>${ansId}</option></c:forEach></select></div></td>');
//Append the new row to the body of the #myTable table
$('#qstionTable tbody').append(newRow);
//Iterate row number
rowNumber++;
}
If you want all the radio button working together you should have the same name for each in your template.
Check that.
Edit:
If several radio button can be selected it is because their name are differents. Your radio' name must not depend of the rowNumber. Set a hard name, not a variable name.
Check that.

target only relevant div

I have a div structured this way:
<div class="sez-form ripart">
<table class="inc_prev"></table>
<div class="previsioni">A</div>
</div>
In table "inc_prev" I have a button that allows you to add another group of these. So by hitting the button you will get a structure like this:
<div class="sez-form ripart">
<table class="inc_prev"></table>
<div class="previsioni">A</div>
</div>
<div class="sez-form ripart">
<table class="inc_prev"></table>
<div class="previsioni">B</div>
</div>
Each "inc_prev" table has this html:
<table class="inc_prev">
<tbody>
<tr>
<td><label>Mese: </label><select id="mese[]" name="mese[]"></td>
<td><label>Anno: </label><select id="anno[]" name="anno[]"></td>
<td><label>Percentuale: </label><input class="importo" type="text" name="percent[]" maxlength="14" size="15" value="">%</td>
<td><img class="addRow" src="../images/plus.png"></td>
</tr>
</tbody>
</table>
This is my JS:
$(document).on('blur','.importo',function(){
var input_value = $(this).val();
var azione =$('#azione').val();
if ($(this).closest('.sez-form').find('.previsioni').length) {
$('.previsioni').load('bp/ripart_prev.php?perc='+input_value+'&id='+azione);
}else{
console.log('qui');
$(this).closest('.sez-form').append('<div class="previsioni"></div>');
$('.previsioni').load('bp/ripart_prev.php?perc='+input_value+'&id='+azione);
}
});
It appends the "previsioni" div if it's not there or update it loading content from DB. In the starting situation it works fine: previsioni div is added or updated in the right way. If I hit the plus button and add the second block when this JS gets triggered both the "previsioni" div get updated with the same content.
So my question is: "how do I change my JS so that when executed only the target "previsioni" is updated?" So if I blur the second "importo" only it's "previsioni" (B) gets updated? I am already using closest as mentioned here but this is not preventing the other previsioni to be updated too
Problem is $('.previsioni') will select all the existing elements with the class. You need to reuse the relationship to target the specific div. Here in the code snippet I have cached the $(this).closest('.sez-form').find('.previsioni') in an object.
Read inline comments
$(document).on('blur','.importo',function(){
var input_value = $(this).val();
var azione =$('#azione').val();
//Use the relationship again to traverse and cache it in a vraible the content
var previsioni = $(this).closest('.sez-form').find('.previsioni');
//If exist
if (previsioni.length) {
previsioni.load('bp/ripart_prev.php?perc='+input_value+'&id='+azione)
}else{
console.log('qui');
//Create div using JQuery
var div = $('<div class="previsioni"></div>');
//Load the content
div.load('bp/ripart_prev.php?perc='+input_value+'&id='+azione)
//Append the data
$(this).closest('.sez-form').append(div);
}
});

Detecting what is triggering a form submit

I'm trying to walk through and alter someone else's code (racktables open source application)... and maybe I've been looking at it too long.
But I can't figure out why clicking on the "Edit Row" image/button in the 4th cell below trigger the form to submit.
HTML Code
<tr>
<td id=""><img src="?module=chrome&uri=pix/tango-user-trash-16x16-gray.png" title="1 rack(s) here" height="16" width="16" border="0">
<form method="post" id="updateRow" name="updateRow" action="?module=redirect&page=rackspace&tab=editrows&op=updateRow">
<input tabindex="1" name="row_id" value="26270" type="hidden">
</form>
</td>
<td><div id="location_name"></div></td>
<td><div id="row_name">BLDG5:First Floor</div></td>
<td>
<input tabindex="1" name="edit" class="edit" src="?module=chrome&uri=pix/pencil-icon.png" id="" title="Edit row" type="image" border="0">
<input tabindex="1" style="display: none;" name="submit" class="icon" src="?module=chrome&uri=pix/tango-document-save-16x16.png" title="Save changes" type="image" border="0"></td>
<td>Row BLDG5:First Floor</td>
</tr>
I've added / created the edit button, as well as some jquery code to handle the edit click event.
Jquery Code
//edit button handler
$(".edit").click(function(e){
var location_id = this.id;
var menu = $( "#location_id" ).clone();
//locate the associated "location_name" field for the selected row & hide the column
var location_name=$(this).parent().siblings().children("#location_name").hide();
var row_name = $(this).parent().siblings().children("#row_name").hide();
//replace location_name with the new menu
$(location_name).replaceWith(menu);
menu.find('option').each(function(i, opt) {
// when the value is found, set the 'selected' attribute
if($(opt).attr('value') == location_id.toString()) $(opt).attr('selected', 'selected');
});
//change row name to input box for editing
var input = $(document.createElement('input'));
$(input).attr('type','text');
//$(input).attr('name','edit_row_name');
$(input).attr('value', $(row_name).text());
//replace exiting row_name with this new input box.
$(row_name).replaceWith($(input));
//show save button
var save_btn = $(this).siblings(".icon").show();
});
What I've tried so Far
When i disable / comment out the logic in PHP that creates the form, the edit button works the way i want it to.
I've been grepping the folder structure to see if there's some javascript embedded somewhere that I'm not seeing. But nothing is jumping out at me.
It's probably something really simple that I'm not seeing / recognizing.
Any suggestions?
See:
<input type='image' />
is also have a default action to submit the forms just like [type="submit"]. to prevent it you need to stop the default behavior:
$(".edit").click(function(e){
e.preventDefault(); // <----use this.

Which submit button was pressed?

In this jsfiddle
http://jsfiddle.net/littlesandra88/eGRRb/
have I submit buttons that are auto-generated. Each table row gets an unique ID number, and if needed each submit button can get the same unique number as well.
Question
The problem is that there are multiple submit buttons, so how do I know which was pressed?
HTML
<form action="" method="post">
<table class="alerts tablesorter" id="accTable" cellspacing="0">
<thead>
<tr class="header">
<th class="activity-header"> A </th>
<th class="activity-header"> Signed </th>
<th class="activity-header"> </th>
</tr>
</thead>
<tbody>
<tr class="row" id="7249">
<td class="activity-data">7249</td>
<!-- tablesorter can't sort a column with check boxes out-of-the-box, so it needs something to sort on. That is why the span tag is here -->
<!-- a jquery script is watching the state of the checkbox, so when clicked the value in the span is updated -->
<td class="checkbox"> <span style="display:none;">0</span> <input name="signed" type="checkbox" > </td>
<td class="edit-column"> <input value="Save" type="submit" name="7249"></td>
</tr>
<tr class="row" id="61484">
<td class="activity-data">61484</td>
<td class="checkbox"> <span style="display:none;">1</span> <input name="signed" type="checkbox" checked > </td>
<td class="edit-column"> <input value="Save" type="submit" name="61484"></td>
</tr>
</tbody>
</table>
</form>
JavaScript
$(document).ready(function() {
$("#accTable").tablesorter();
// :checkbox stops from executing the event on the save button. Same as input[type=checkbox]
$('#accTable input:checkbox').click(function() {
// insert 1 or 0 depending of checkbox state in the tag before the input tag. In this case <span> is before <input>
// this is done so tablesorter have something to sort on, as it doesn't support checkbox sort out of the box.
var order = this.checked ? '1' : '0';
$(this).prev().html(order);
$(this).parents("table").trigger("update");
});
});
// sends the form content to server side, and stay on page
$('form').live('submit', function() {
alert('test');
// don't redirect
return false;
});
You could use delegate():
$('form').delegate('input:submit','click',
function(){
alert(this.name);
return false;
});
JS Fiddle demo.
Edited to address question in the comments, from OP:
Would it be possible to display 0 or 1 as the state of the associated checkbox with this approach?
Yeah, that's possible, though it's a little more long-winded than I'd like:
$('form').delegate('input:submit','click',
function(){
var idString = this.name;
var checkboxState = $('#' + idString).find('input:checkbox').is(':checked');
if (checkboxState == true){
alert('1');
}
else {
alert('0');
}
return false;
});
JS Fiddle demo.
You will need to add an onClick handler to each button that does:
$(this).closest('form').data('submit_name', this.name);
Assign a function to the click event of the submit buttons. That function should simply store the clicked button's value in a variable. Inside the submit event of the form, check the value of that variable. The submit event will fire after the click event so you should be able to get the expected result.
Demo here
The thing to note here is that that different browsers behave differently when dealing with multiple submit buttons; specially when you hit enter to submit the form. I think my example takes care of this.
As you are using one form and using $('form').live('submit', function() { and both submit buttons are in the same form so its not possible in submit event. You have to use click event or two form tags.
Here is how I solve this,
HTML
<form id="my-form" ...>
....
Save
Save and add another
</form>
Javascript
$('.btn-submit-form').on('click', function(ev){
ev.preventDefault();
var $form = $(this).attr('href');
if ($form.length > 0) {
$form.trigger('submit', {'submitBtn': $this});
}
});
$('form').on('submit', function(ev, extra) {
if (extra && extra.submitBtn) {
var submitVal = extra.submitBtn.attr('data-value');
} else {
var submitVal = null;
}
// submit the form as you want with submitVal as one of the fields.
});
The advantage here is that your form only submits on the submit event and not on click event. This way you can have one common function to submit the form whether you click a button, hit return in a form field or submit it pragmatically.
I also like to make things as generic as possible and turn them into patterns. This two functions can work across your whole project if you come up with some patterns like naming each submit button with value as .btn-form-submit and having a data-value attribute to store the value.

Categories