I have an HTML table with dynamically generated rows (using MVC3's EditorFor). Users fill in the data in the table (row by row), then submit the form to the server (via MVC3 HTML form). Users can delete a row by pushing a button that calls $(tableRow).remove() on the TR element, and then calls an async server method that removes the row from the database.
I've found that if I have say 5 rows in my table and I delete the third one then submit, the server method receives rows 1 and 2, but looses the other rows (the original 4th and 5th rows).
I've tried searching online as to why the postback would receive the first two rows and miss the last two, but all the answers I could find revolved around JQuery posts, which I'm not using.
Any help or direction would be great, please let me know if I need to clarify anything.
EDIT: adding code from my project that applies to the question. if you need more code for context, let me know and I'll add it.
//////////////// VIEW ////////////////
// model info and initialization logic
#using (Html.BeginForm("EditTimesheet", "ControllerName", FormMethod.Post, new { enctype = "multipart/form-data", id = "editTimesheet" }))
{
#Html.ValidationSummary(true)
<fieldset>
<table width="100%">
<tr>
<td colspan="14" align="right">
// lots of code
</td>
</tr>
#Html.EditorFor(m => m.Rows)
<tr>
<td colspan="14" align="right">
// lots of code
</td>
</tr>
// closing statements
//////////////// EditorFor ////////////////
// model info and initialization logic
<tr class="timesheet-row">
<td>
<a href='#'>
<img src='#Url.Content("~/Content/Images/delete.gif")'
width='17' height='17' style='border: 0;'
onclick="DeleteRow(this, #Model.RowId)" />
</a>
</td>
// other td's
</tr>
//////////////// JS file ////////////////
function DeleteRow(box, rowId)
{
$(box).closest(".timesheet-row").remove();
// HACK: despicable, detestable HACK!
var url = deleteRowUrl;
url += '?rowId=' + rowId;
var ajaxData = {
type: "POST",
url: url,
dataType: "json",
contentType: "application/json; charset=utf-8",
data: null,
success: null,
error: function (error) {
alert("There was an error posting the data to the server: " + error.responseText);
}
};
$.ajax(ajaxData);
}
When you delete a row you are creating holes in the indexes of the names in the collection and the default model binder stops working because you no longer respect the expected format.
So instead of having the following sequential values:
<input type="text" name="Items[0].Id" value="1" />
<input type="text" name="Items[1].Id" value="2" />
<input type="text" name="Items[2].Id" value="3" />
<input type="text" name="Items[3].Id" value="4" />
<input type="text" name="Items[4].Id" value="5" />
if you delete the third row with $(tableRow).remove() on the TR element you end up with:
<input type="text" name="Items[0].Id" value="1" />
<input type="text" name="Items[1].Id" value="2" />
<input type="text" name="Items[3].Id" value="4" />
<input type="text" name="Items[4].Id" value="5" />
See the problem?
Here's an article which illustrates how to solve this problem by using a custom helper called Html.BeginCollectionItem and which uses GUIDs in the names of the input fields for the collection instead of integer indexes. Also checkout Phil Haacks article about the syntax that the default model binder expects your fields to be named. There's a section towards the end which is called Non-Sequential Indices in which he covers how this could be done.
Sounds like part of your form is being deleted.
Inspect the DOM
find your form
delete a row and see what changes
Related
Apologies if this question has been asked and answered elsewhere, but I couldn't find much help about it so thought I'll ask here. I am very new to Thymeleaf and still trying to make sense of things. So here is what I'm trying to do,
OBJECTIVE: Extract data from the database and display it on a simple webpage in tabular form (i.e., Rows and Columns). All fields are editable with a Submit and Reset button at the end. Something like this,
PROBLEM: So the issue I am facing is this, if there is only one row data, there is no issue in updating the records, but if there is more than 1 record when I edit the record and press Submit, the updated record remains the same as before, but the first record in the list gets the updated text concatenated to the existing text in their respective columns. This behavior is shown in the following image,
As seen in the image above, I had two records ... without even changing the record, I press Submit button and now I can see Record 1 has Row 2 text concatenated to its existing text.
This is my Code,
HTML
<form
action="#"
th:action="#{/saveMapping}"
th:object="${hanaMappingT}"
method="post"
>
<table id="mappings">
<thead>
<tr>
<th>ID</th>
<th>WBSNAME</th>
<th>ITEMDESCRIPTION</th>
<th>WBSCANDYCODE</th>
<th>WBSHENAME</th>
</tr>
</thead>
<tbody>
<tr th:each="mapping : ${allMappings}">
<td>
<input
type="text"
field="*{id}"
th:name="id"
th:value="${mapping.id}"
/>
</td>
<td>
<input
type="text"
field="*{wbsName}"
th:name="wbsName"
th:value="${mapping.wbsName}"
/>
</td>
<td>
<input
type="text"
id="itemDescription"
field="*{itemDescription}"
th:name="itemDescription"
th:value="${mapping.itemDescription}"
/>
</td>
<td>
<input
type="text"
field="*{wbsCandyCode}"
th:name="wbsCandyCode"
th:value="${mapping.wbsCandyCode}"
/>
</td>
<td>
<input
type="text"
field="*{wbsHeName}"
th:name="wbsHeName"
th:value="${mapping.wbsHeName}"
/>
</td>
</tr>
</tbody>
</table>
<input type="submit" value="Submit" />
<input type="reset" value="Reset" />
</form>;
CONTROLLER CLASS
#PostMapping("/saveMapping")
public String saveMapping(
#ModelAttribute HanaMappingT mapping, Model model) {
try {
log.info(mapping.toString());
List<HanaMappingT> mappingList = new ArrayList<HanaMappingT>();
mappingList.add(mapping);
log.info("List : " + mappingList.toString());
hanaService.updateMapping(mappingList);
} catch (Exception e) {
log.error("Unable to save mapping data - ", e);
}
return "result";
}
HanaMappingT class
#Data
#Entity
#Table(name = "MAPPING_T")
public class HanaMappingT {
#Id
#Column(name = "ID")
private Integer id;
#Column(name = "WBSNAME")
private String wbsName;
#Column(name = "ITEMDESCRIPTION")
private String itemDescription;
#Column(name = "WBSCANDYCODE")
private String wbsCandyCode;
#Column(name = "WBSHENAME")
private String wbsHeName;
}
I am not really sure how to deal with multiple records, how with one single submit button I can store all records in their respective columns and rows.
If you need further clarification, please feel free to ask questions and I'll try to answer to the best of my ability.
I need to update one dropdwonlist without reload the page, I mean, I have a form where I add the elements that I need, then I have another form where I have the dropdownlist conected to my database but if I do not have the element I need to select, I have to add it from the other form, but the problem is that i need to reload the page in order to the dropdownlist show the new element then I loose the data I was typing.
I wish to know a way to update the dropdownlist without reload the page.
Im using php and mysqli my code is simple:
<form action="edit_col_exe.php" method="post">
<p><label>Add Element:</label>
<input autofocus type="text" name="elemnt" class="input" required />
</p>
<table>
<tr>
<td><input type="submit" name="Save" value="Save" /></td>
</tr>
</table>
</form>
Form2:
Select Element
query("select * from Elements order by Element asc") or die("fail");
echo "Select an option";
while($reg=$con ->fetch_assoc()){
echo "";
echo $reg['Element'];
}?>
I hope someone can help me!
regards!
Use Ajax (I prefer jQuery) and remove your form.
JS
function addElement(){
// get new name
var name = $("#newElementsName").val();
// create ajax call
$.ajax({
type: "POST",
url: "edit_col_exe.php", // URL to php script
data: { // post data for php script (I use the data from your form (including the typo))
elemnt: name,
save: 'Save'
},
success: function(data){
// this function will be called when php script run successful (HTTP-Status 2xx)
// Clear the input filed
$("#newElementsName").val('');
// Add new name to dropdown
$("#elements").append("<option>"+name+"</option>");
}
});
}
HTML
<div>
<p><label>Add Element:</label>
<input autofocus type="text" id="newElementsName" class="input" required />
</p>
<table>
<tr>
<td><button type="button" onclick="addElement()">Save</button></td>
</tr>
</table>
</div>
<div>
<select id="elements" size="1">
</select>
</div>
I solved my problem and I want to share with you my solution, its simple:
setInterval(function(){
$('#searchelement').load('addelements.php');
});
<p><label>Element</label>
<select id="searchelement" name="element" required />
</option>
</select></p>
So everytime I add an element at 'addelements.php', I can search the new element in the select list.
I am working on a page where I show different input fields, depending on the selection that a user did.
Example 1:
<input type="text" name="color" value="" id="5">
<input type="text" name="location" value="" id="6">
<input type="text" name="age" value="" id="7">
Example 2:
<input type="text" name="color" value="" id="5">
<input type="text" name="destination" value="" id="8">
<input type="text" name="hours" value="" id="9">
<input type="text" name="date" value="" id="10">
Question 1:
How can I get all input fields with jQuery when the input fields itself are dynamic?
Question 2:
On the serverside, I want to process the input fields with their value and ID. How can I make this dynamic?
I know that this can be done easily when everything is fix, e.g.:
var color = $('#color').val();
var destination = $("#destination").val();
var dataString = 'color=' + color + "&destination=" + destination;
$.ajax({
type: "GET",
url: "do_something.php",
data: dataString,
async: false,
success: function(data){
console.log('success');
}
});
You can use .serialize() to create your data-string. It will dynamically grab all the data for the form and turn it into a string like you're attempting to build:
$.ajax({
type: "GET",
url: "do_something.php",
data: $("form").serialize(),
async: false,
success: function(data){
console.log('success');
}
});
Docs for .serialize(): http://api.jquery.com/serialize
Note, you may need to refine the $("form") selection to be more specific if you have more than one form in the DOM.
As for your second question, you should generally keep questions on SO to one question per post. That being said, you should set the ID attribute to the value attribute instead, that way it will be passed to the PHP script when the form is submitted, ID will be lost since it's not transmitted with the form.
If I understand well your first question is that you want get the values and the id's of the dinamic inputs with jquery.
Yo can do that, inserting the dinamic inputs allways in the same div and simply do something like:
var data = new Array();
$("#id-div input").each(function() {
data.push({id: $(this).attr("id"), value: $(this).attr("value")});
});
I have a webpage written in C#/Razor. I am printing all the values from a database on this page like so :
<div style="min-height: 150px; font-size: 1.25em">
<div style="margin-bottom: .5em">
<table>
<thead>
<tr>
<th>Name</th>
<th>Branch</th>
<th>Phone No.</th>
<th>Extension</th>
<th>Email</th>
</tr>
</thead>
<tbody>
#foreach (var prod in Model)
{
<tr>
<td>#prod.FullName</td>
<td>#prod.Branch</td>
<td>#prod.PhoneNo</td>
<td>#prod.Extension</td>
<td>#prod.Email</td>
#if (User.IsInRole(#"Admins") || User.Identity.Name == prod.DomainAC)
{
<td>edit</td>
}
else
{
<td>User => #User.ToString()</td>
}
<td>
<input type="checkbox" name="message" value="#prod.PhoneNo">Message<br>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
This works fine to display their info. What I would like to do now is a small bit more difficult.
Below this, I have a form with a username, password and message. Using this form, I would like to have the behaviour that on submit, it will take the values in the input boxes of the form and the above C#, construct a link, navigate to the link and print the response of the server
So I have :
#{if (IsPost)
{
//handle and print response
}
else
{
<form method="post" action="">
Username:<br />
<input type="text" name="u" /><br />
Password<br />
<input type="text" name="p" /><br />
<br />
Password<br />
<textarea name="m" cols="25" rows="5">
Enter your comments here...
</textarea><br>
<br />
<input type="submit" value="Submit" class="submit" />//when this is clicked, construct url and navigate to it.
</form>
}
}
The URL I want to construct from this form is :
http://webaddress.com/web/d.php?u=<Username entered by user>&p=<Password entered by user>&s=<List of Phone Numbers from the C# above where the checkbox is selected, comma separated>&m=<Comment submitted by user>
So, if my name is "John", Password is "Password1", Comment is "Test" and I have selected one checkbox for a user with the phone number "12345678", the URL I will navigate to is :
http://webaddress.com/web/d.php?u=John&p=Password1&s=12345678&m=Test
Ideally I would like to print the response of the webpage in a <div> while still on the same parent web page rather than going to a new one if this is possible.
I have no idea where to begin with this, how to do it or even if this is possible. Can anyone help me please ?
UPDATE :
Trying this JQuery which does not alert me so I cannot debug :
<script>
$("#thebutton").click(function() {
var form = $(document.getElementById('FormID'));
var urlToConstruct = 'http://webaddress.com/web/d.php';
urlToConstruct += '?u=' + form.find('#u').val();
urlToConstruct += '&p=' + form.find('#p').val();
('#employeeTable tbody tr').has(':checkbox:checked').find('td:eq(2)').each(function() {
urlToConstruct.append($(this).text());
alert(urlToConstruct);
})
});
</script>
$("#SubmitButtonID").click(function() {
var form = $(document.getElementById('FormID');
var urlToConstruct = 'http://webaddress.com/web/d.php';
urlToConstruct += '?u=' + form.find('#iDoFInputControl1').val();
urlToConstruct += '&p=' + form.find('#iDoFInputControl2').val();
form.submit();
});
this example uses jQuery, a javascript library (jquery.com), i'm using getElementById to find your form, faster then the native jQuery() selector. This example assumes all your controls are inside your form (but if they are not it wouldn't be a train smash, just cant use (jQuery obj).find).
.val() gets the value, and it should work for checkboxes too, but if it doesn't, a quick google search for getting values from checkboxes using jquery will return loads of results.
p.s. I've written that code mostly freehand, not checked it in a browser to make sure that it is completely correct.
Update... to answer your follow up question...
If you are using mvc (assumed as you using razor), inside your controller you can use Request.Params["urlParameter"] or Request.Form["controlID"] to access what you've received from the browser. Then once you've got those values, you should place them inside the 'ViewBag' (ViewBag.yourVariableHere="val") for them to be accessible in your view via #ViewBag.yourVariableHere, or you can include the required data in your model which can also be accessed in your view
I have a Spring MVC application and I am wondering how to successfully map multiple, dynamic form elements with the same name in my JSP page to my object class. For example:
In my locations.jsp page, I have multiple dropdown boxes:
<form id="tabs-3-form">
<input id="locations-1" name="location" />
<input id="locations-2" name="location" />
<input id="locations-3" name="location" />
... (more can be added or deleted dynamically by user)
</form>
I'm using jQuery to POST the form to my controller:
$("#tabs-3-form").submit(function() {
$.ajax({
type: 'POST',
url: '/searchResults',
data: $(this).serialize(),
dataType: 'json',
success: function(data) {
...
}
});
return false;
});
My LocationsController.java is set up as follows:
#RequestMapping(value = "/locationResults", method = RequestMethod.POST)
public #ResponseBody LocationsCollection locationsCollection
(
#ModelAttribute(value = "location") Location location,
BindingResult result
)
{
LocationsCollection locationsCollection = new LocationsCollection();
locationsCollection.addLocation(location);
// Anything else to do here?
return locationsCollection;
}
LocationsCollection.java just contains a List of Location objects.
Do I need to add brackets to the names of my input fields? Will MVC automatically do the mapping to a List, as it does with the other form elements? If anyone could provide an example, I'd appreciate it.
I was able to get it working by following the example from: http://lifeinide.blogspot.com/2010/12/dynamic-forms-lazylist-and-transparent.html?showComment=1355160197390#c6923871316812590644
I did make one adjustment, however. For the form names, I used:
<input name="locationList[0].locationName" />
instead of what the article suggests:
<input name="myFormObject.elements[0].property" />
I think you need to use brackets if you are using same name for form elements.
<c:forEach items="${expenseForm.expenseDetails}" varStatus="i">
<tr id="expenseDetail_${i.index}" class="lineItemsClass" lineNum="${i.index}">
<td><form:input path="expenseDetails[${i.index}].description" cssStyle="width: 200px;" /> <label style="color: red;"><form:errors path="expenseDetails[${i.index}].description" /></label></td>
<td><form:input path="expenseDetails[${i.index}].quantity" cssStyle="width: 60px;" readonly="true"/> <label style="color: red;"><form:errors path="expenseDetails[${i.index}].quantity" /></label></td>
<td><form:input path="expenseDetails[${i.index}].unitPrice" cssStyle="width: 60px;" readonly="true"/> <label style="color: red;"><form:errors path="expenseDetails[${i.index}].unitPrice" /></label></td>
<td><form:input path="expenseDetails[${i.index}].total" cssStyle="width: 60px;" /> <label style="color: red;"><form:errors path="expenseDetails[${i.index}].total" /></label></td>
</tr>
</c:forEach>
Here expenseDetails is the list in the modelAttribute class.
The path name will be used as html form name and it will be indexed based.
The above code segment is working fine for me.