I have a table that have a row of dynamic textbox. Example below:
I ad
<script src="Scripts/jquery-1.10.2.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
function DeleteRow(btn) {
//alert("delete" + btn);
var tr = btn.closest('tr');
tr.remove();
}
$(".btnsd").click(function () {
// debugger;
alert("hjkghk");
divs = $('.val')
for (ind in divs) {
var div = divs[ind];
if (div.value == "") {
div.focus();
return false;
}
}
$('#Employertbl').append(
'<tr>' +
'<td> @Html.TextBox("item.employer_name", null, new { @class = "form-control val" })</td>' +
'<td width="24%"> <div style="float:left; padding-right:5px;">@Html.TextBox("item.duration_From", null, new { @id = "", @placeholder = "From Date", @class = "form-control input-date datepicker val", @readonly = true })</div> ' +
'<div>@Html.TextBox("item.duration_to", null, new { @id = "", @class = "form-control input-date datepicker val", @placeholder = "To Date", @readonly = true })</div></td>' +
'<td> @Html.TextBox("item.designation", null, new { @class = "form-control val" })</td>' +
'<td> @Html.TextBox("item.worked_skill", null, new { @class = "form-control val" })</td>' +
'<td> @Html.TextBox("item.proj_handled", null, new { @class = "form-control val" })</td>' +
'<td> @Html.CheckBox("item.current_employed",new{@class = "current" })</td>' +
'<td><input type="button" value="Remove" onclick="DeleteRow(this)" name="delete" class="btn blue pull-right" /> </td>' +
'</tr>'
);
});
});
</script>
<div class="table-responsive">
<table id="Employertbl" class="table table-striped table-bordered table-hover dataTable no-footer">
<tbody>
<tr>
<th>Employer Name</th>
<th>Duration</th>
<th>Designation</th>
<th>Worked skill(s)</th>
<th>Reason of change</th>
<th>Currently Employed</th>
<th>Action</th>
</tr>
<tr>
<td>
<input class="form-control val" id="item_employer_name" name="item.employer_name" type="text" value="">
</td>
<td width="24%">
<div style="float:left; padding-right:5px;"><input class="form-control input-date datepicker val hasDatepicker" name="item.duration_From" placeholder="From Date" type="text" value="" id="dp1459328857835"></div>
<div> <input class="form-control input-date datepicker val hasDatepicker" name="item.duration_to" placeholder="To Date" type="text" value="" id="dp1459328857836"></div>
</td>
<td>
<input class="form-control val" id="item_designation" name="item.designation" type="text" value="">
</td>
<td>
<input class="form-control val" id="item_worked_skill" name="item.worked_skill" type="text" value="">
</td>
<td>
<input class="form-control val" id="item_proj_handled" name="item.proj_handled" type="text" value="">
</td>
<td>
<input class="current" id="item_current_employed" name="item.current_employed" type="checkbox" value="true"><input name="item.current_employed" type="hidden" value="false">
</td>
<td>
<input id="myButton" type="button" value="add" name="delete" class="btnsd bcbn">
</td>
</tr>
<tr><td> <input class="form-control val" id="item_employer_name" name="item.employer_name" type="text" value=""></td><td width="24%"> <div style="float:left; padding-right:5px;"><input class="form-control input-date datepicker val hasDatepicker" name="item.duration_From" placeholder="From Date" type="text" value="" id="dp1459328857837"></div> <div><input class="form-control input-date datepicker val hasDatepicker" name="item.duration_to" placeholder="To Date" type="text" value="" id="dp1459328857838"></div></td><td> <input class="form-control val" id="item_designation" name="item.designation" type="text" value=""></td><td> <input class="form-control val" id="item_worked_skill" name="item.worked_skill" type="text" value=""></td><td> <input class="form-control val" id="item_proj_handled" name="item.proj_handled" type="text" value=""></td><td> <input class="current" id="item_current_employed" name="item.current_employed" type="checkbox" value="true"><input name="item.current_employed" type="hidden" value="false"></td><td><input type="button" id="myButton" value="add" name="delete" class="btnsd dfsd"> </td></tr>
<tr><td> <input class="form-control val" id="item_employer_name" name="item.employer_name" type="text" value=""></td><td width="24%"> <div style="float:left; padding-right:5px;"><input class="form-control input-date datepicker val hasDatepicker" name="item.duration_From" placeholder="From Date" type="text" value="" id="dp1459328857839"></div> <div><input class="form-control input-date datepicker val hasDatepicker" name="item.duration_to" placeholder="To Date" type="text" value="" id="dp1459328857840"></div></td><td> <input class="form-control val" id="item_designation" name="item.designation" type="text" value=""></td><td> <input class="form-control val" id="item_worked_skill" name="item.worked_skill" type="text" value=""></td><td> <input class="form-control val" id="item_proj_handled" name="item.proj_handled" type="text" value=""></td><td> <input class="current" id="item_current_employed" name="item.current_employed" type="checkbox" value="true"><input name="item.current_employed" type="hidden" value="false"></td><td><input type="button" id="myButton" value="add" name="delete" class="btnsd"> </td></tr>
</tbody>
</table>
</div>
You are not including the necessary data-val
attributes to the textboxes, or the placeholder elements for displaying the validation messages, which are used by jquery.validate.unobtrusive.js
to do client side validation. In addition, your current implementation does not allow the user to remove anything other that the last row which can be solved by including a hidden input for the indexer which allows non consecutive indexers to be posted and bound to your collection.
First start by adding one default ClsTargetInfo
object to your TargetInfo
property and generate its html in the view
<table id="table"> // add an id attribute
<thead>.....</thead>
<tbody is="tablebody"> // add an id attribute
for(int i = 0; i < Model.TargetInfo.Count; i++)
{
<tr>
<td>
@Html.TextBoxFor(m => m.TargetInfo[i].TargetColor_U, new { id="", @class="form-control" }) // remove the unnecessary id attribute
@Html.ValidationMessageFor(m => m.TargetInfo[i].TargetColor_U)
// Add the following hidden input to only one column in the row
<input type="hidden" name="TargetInfo.Index" value=@i />
</td>
<td>
@Html.TextBoxFor(m => m.TargetInfo[i].TargetColor_V, new { id="", @class="form-control" }) // remove the unnecessary id attribute
@Html.ValidationMessageFor(m => m.TargetInfo[i].TargetColor_V)
</td>
.... // other columns
</tr>
}
</tbody>
</table>
Then inspect the html it generates for the <tr>
element which should look something like
<tr>
<td>
<input data-val="true" data-val-required="The TargetColor_U field is required" name="TargetInfo[0].TargetColor_U" type="text" value="">
<span class="field-validation-valid errorText" data-valmsg-for="TargetInfo[i].TargetColor_U" data-valmsg-replace="true"></span>
<input type="hidden" name="TargetInfo.Index" value="0" />
</td>
....
</tr>
and copy it inside a hidden element that is placed outside the form tags and replace all instance of the indexer with a dummy character so name="TargetInfo[0].TargetColor_U"
becomes name="TargetInfo[#].TargetColor_U"
), and also replace the value
attribute of the hidden input so value="0"
it becomes value="#"
<table id="newrow" style="display:none">
.... // copy the tr element and its contents here
</table>
Then the script will look like
var form = $('form'); // or use the id if you have given the form an id
var newrow= $('#newrow');
var tablebody = $('#tablebody'); // modify to suit your id
$("#btnAddTarget").click(function() {
var index = (new Date()).getTime(); // unique indexer
var clone = newrow.clone(); // clone the new row
clone.html($(clone).html().replace(/#/g, index)); // update the indexer of the clone
var row = clone.find('tr');
tablebody.append(row); // add the new row to the table
// Reparse the validator
form.data('validator', null);
$.validator.unobtrusive.parse(form);
});
Side notes:
data-val
attributes
when the form is first rendered. When you add dynamic content, it is
necessary to re-parse the validator as indicated in the last 2 lines
of the script.<td style="padding-left:0px;padding-top:0px;padding-bottom:0px;padding-right:0px">
, you should use #table td { padding: 0; }
in your .css
file[StringLength]
attribute), you will need to update the html to
suit. As an alternative, you can consider using the
BeginCollectionItem helper which means you have one partial
view (representing a table row). For existing items, you use a
foreach
loop with @Html.Partial()
and for new rows, you use ajax
to call a controller method that return a the partial view, and
update the DOM