I have a need to be able to post back an unknown number of this model class:
public class UserRegisterModel
{
public string UserName { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
public string RepName { get; set; }
public string ContactNumber { get; set; }
}
in a List<UserRegisterModel> to a controller.
I need my view to have a form that can send back multiple instances of the model to the controller, so for example if my user wants to add 3 Reps, he can make 3 reps and give them each details and usernames and passwords, and send them all back at once in a List.
I have this starting point for my view code:
#model List<PicsWebApp.Models.UserRegisterModel>
#{
ViewBag.Title = "NewRep";
}
<h2>NewRep</h2>
#using (Html.BeginForm("NewRep", "Admin", FormMethod.Post, new { id = "fieldform", #class = "form" }))
{
}
Can anybody show me the correct way of accomplishing this? I know I need javascript in order to dynamically add more form elements if the user clicks "add new rep" so this is mainly about the C#.
you need to make sure the properties' ID's conform to the standard set (I used the link from Felipe's comment when I was doing this). I did this before, where a table row would get added when the user click a button. I found the best way is to add an entry before the page gets rendered, then taking the HTML for that, and using it in your jquery.
Here's a function I've used:
$("#nameAdd").click(function (e) {
var index = $("#nameTable tr").length - 1;
e.preventDefault();
var newItem = $("<tr> \
<td> \
<span id='Names_" + index + "__FullName' /> \
</td> \
<td> \
<input class='table-editor-small update-name' id='Names_" + index + "__FirstName' name='Names[" + index + "].FirstName' type='text' value='' /> \
</td> \
<td> \
<input class='table-editor-small update-name' id='Names_" + index + "__MiddleName' name='Names[" + index + "].MiddleName' type='text' value='' /> \
</td> \
<td> \
<input class='table-editor-small update-name' data-val='true' data-val-required='Person Last Name Required' id='Names_" + index + "__LastName' name='Names[" + index + "].LastName' type='text' value='' /> \
</td> \
<td class='center'> \
<input data-group='nameRadios' id='Names_" + index + "__IsPrimary' name='Names[" + index + "].IsPrimary' type='radio' value='True' /> \
</td> \
<td class='center'> \
<input data-val='true' data-val-required='The Delete? field is required.' id='Names_" + index + "__ToDelete' name='Names[" + index + "].ToDelete' type='checkbox' value='true' /> \
<input name='Names[" + index + "].ToDelete' type='hidden' value='false' /> \
</td> \
</tr>");
$("#nameTable").append(newItem);
}
again, the HTML to add is straight from an item that had been rendered by the view, i just swapped in the index
Related
I have multiple textboxes added dynamically through jQuery. But due to some reasons, the id of some textboxes is missing.
I want to make a check on all the textboxes if they have their IDs or not. If not then add it using C#.
I have done this part from backend:
// Say I have 3 textboxes added in string "Question" here.
string Question="<input data-size="0" data-type="0" id="Answer1" placeholder="Answer1" type="text" /><br/>
<br/>
<input data-size="0" data-type="0" id="Answer2" placeholder="Answer2" type="text" /><br/>
<br/>
<input data-size="0" data-type="0" id="Answer3" placeholder="Answer3" type="text" /><br/>
<br/>
";
string textboxTagPattern= #"(<input)([^>]*)(type=\"")(text)(\"")([^>]*)(/>)";
Regex rgx = new Regex(textboxTagPattern, RegexOptions.IgnoreCase);
MatchCollection questionInputText = rgx.Matches(Question);
string textboxes ="";
if (questionInputText.Count > 0){
for (int i = 0; i < questionInputText.Count; i++)
{
textboxes = questionInputText[i].Value;
var id = textboxes.IndexOf("id");
if (textboxes.IndexOf("id") == -1)
{
I am stuck here. How to add the id attribute to the textboxes?
}
}
}
You can use String.Insert(int startIndex, string value) like below. Use appropriate value instead of Answer1 in "id=\"Answer1\" ".
if (textboxes.IndexOf(\"id\") == -1)
{
textboxes = textboxes.Insert(textboxes.IndexOf("<input ") + "<input ".Length, "id=\"Answer1\" ");
}
I have a table where I add and remove rows dynamically:
#model AHBReports.Models.AdjustmentModel
#using (Html.BeginForm())
{
<table id="container">
#Html.EditorFor(model => model.Adjustments)
</table>
<div >
<input type="button" id="btnAdd" value="+ Add" />
<input type="submit" value="Submit" />
</div>
}
EditorTemplate:
#model AHBReports.Models.Adjustment
<tr>
<td>#Html.HiddenFor(x => x.Id, new { #class = "iHidden" })</td>
<td>#Html.AutocompleteFor(model => model.BRANCH, "GetBranches", "Report700H")</td>
<td>#Html.EditorFor(model => model.Amount)</td>
<td><a onclick="removeRow(this)">x</a></td>
</tr>
Script for table manipulation:
<script type="text/javascript">
function removeRow(selector)
{
if ($('#container tr').length > 1)
{
$(selector).closest('tr').remove();
}
}
$(document).ready(function () {
$("#btnAdd").click(function (e) {
var ind = $("#container tr:last").find("input.iHidden").val();
var itemIndex = parseInt(ind);
itemIndex++;
console.log(itemIndex);
e.preventDefault();
var newItem = $("<tr>"+
"<td><input id='Adjustments_" + itemIndex + "__Id' type='hidden' value='"+itemIndex+"' class='iHidden' name='Adjustments[" + itemIndex + "].Id' /></td>" +
"<td><input type='text' id='Adjustments_" + itemIndex + "__BRANCH' name='Adjustments[" + itemIndex + "].BRANCH' data-autocomplete-url='#Url.Action("GetBranches", "Report700H")'/></td>" +
"<td><input type='text' id='Adjustments_" + itemIndex + "__Amount' name='Adjustments[" + itemIndex + "].Amount'/></td>" +
"<td><a onclick='removeRow(this)'>x</a></td>" +
"</tr>");
$("#container").append(newItem);
});
});
My add/delete functions work fine visually in my view, as well as when I accept the collection in my POST method:
public ActionResult Adjust(AdjustmentModel model)
{
//working with model.Adjustments
}
I receive correct values. However, when I try to delete some row, which is in the middle of the table and then sumbit the form, I receive only elements that were above deleted row, for example:
id branch amount
0 aaa 500
1 bbb 200
2 ccc 300 --deleted this row
3 ddd 400
Collection receives:
id branch amount
0 aaa 500
1 bbb 200
So, the last row is missing.
What am I doing wrong??
Thanks a lot
When the row you deleted it containts model's input and input has name and id based on index.
So when you delete row you have to update input's name and id that are in row after the deleted row..
or regenerate row next all from deleted row with new index name
.
Replace your delete function with this one
function removeRow(selector) {
if ($('#container tr').length > 1) {
$(selector).closest('tr').remove();
var itemIndex =0;
$('#container tr').each(function(){
var this_row = $(this);
this_row.find('input[name$=".BRANCH"]').attr('name','Adjustments[' + itemIndex + '].BRANCH');//replace name of input that ends the name BRANCH
this_row.find('input[name$=".Amount"]').attr('name','Adjustments[' + itemIndex + '].Amount');
this_row.find('input[name$=".Id"]').attr('name', 'Adjustments[' + itemIndex + '].Id');
itemIndex ++;
});
}
}
The indexer for collections must start at zero and be consecutive unless you use an Index property where the value of Index is equal to the indexer. For example
<input ... name="Adjustments[0].ID" ..>
<input ... name="Adjustments[2].ID" ..>
wont post back correctly. But if you add an Index property for the object
<input ... name="Adjustments[0].ID" ..>
<input ... name="Adjustments[0].Branch" ..>
<input ... name="Adjustments[0].Index" value="0"..>
<input ... name="Adjustments[2].ID" ..>
<input ... name="Adjustments[2].Branch" ..>
<input ... name="Adjustments[2].Index" value="2"..>
Then the collection will post back correctly
Since you don't have access to the indexer in the EditorTemplate, you will need to generate the controls in a for loop in the main page
for (int i = 0; i < Model.Adjustments.Count; i++)
{
var name = string.Format("Adjustments[{0}].Index", i);
#Html.HiddenFor(m => m[i].ID)
....
<input type=hidden name="#name" value="#i" />
}
You will also need to modify your script to include the hidden input for the Index property. Rather than basing the value of itemIndex on the number of existing rows, base it on a unique value. For example
$("#btnAdd").click(function (e) {
var itemIndex = (new Date()).getTime();
u can give the row an unique id.
var newItem = $("<tr id='row"+itemIndex+"'>"+
"<td><input id='Adjustments_" + itemIndex + "__Id' type='hidden' value='"+itemIndex+"' class='iHidden' name='Adjustments[" + itemIndex + "].Id' /></td>" +
"<td><input type='text' id='Adjustments_" + itemIndex + "__BRANCH' name='Adjustments[" + itemIndex + "].BRANCH' data-autocomplete-url='#Url.Action("GetBranches", "Report700H")'/></td>" +
"<td><input type='text' id='Adjustments_" + itemIndex + "__Amount' name='Adjustments[" + itemIndex + "].Amount'/></td>" +
"<td><a onclick='$('#row"+ itemIndex +").remove();'>x</a></td>" +
"</tr>');
Actually this works fine for a smiliar page which i've created.
Greetings
I have a table filled with generated data using razor. Each row has the id of the PlanID in the database.
<tbody>
#foreach(var plan in ViewBag.plans) {
<tr id="#plan.PlanID">
<td contenteditable="true">#plan.Name</td>
<td contenteditable="true">#plan.Code</td>
<td contenteditable="true">#plan.Description</td>
<td contenteditable="true">#plan.DataAmount</td>
<td contenteditable="true">#plan.Price</td>
<td contenteditable="true">#plan.SmartPhoneCost</td>
<td><a><span class="glyphicon glyphicon-pencil"></span></a></td>
</tr>
}
</tbody>
I have also made button click append the table with a new row to add a plan
function Add() {
$("#tableData tbody").append(
"<tr role='form' action='/api/wirelessplans' method='post'>" +
"<td><input name='Name' type='text' size='30' maxlength='30'/></td>" +
"<td><input name='Code' type='text' /></td>" +
"<td><input name='Description' type='text' size='50' maxlength='50'/></td>" +
"<td><input name='DataAmount' type='text' /></td>" +
"<td><input name='Price' type='text' /></td>" +
"<td><input name='SmartPhoneCost' type='text' /></td>" +
"<td><a class='save' type='submit'><span class='glyphicon glyphicon-ok'></span></a></td>" +
"</tr>")
};
$(function () {
$("body").on("click", ".addPlan", function () {
Add()
});
});
I have made the the td's editable and would like to click on the icon to submit the changes to -- api/wirelessplans/ that I defined in my controller. How do I grab only the values that are changed (or added in the new row) and post/put to the database?
I am bit new in the c# programming, so I got stuck at one place. Need your help.
Actually through javascript I am generating table(4 columns) rows(having textboxes so that user can give inputs) as per as the user button click. As the number of rows are not fixed and we dont have the exact name of the textboxes so now my problem is how should we insert these rows into the sqlserver table?
should I use simply the loop for generating the name of the Textboxes for every user button click? and once we got the name for all controls can we insert these all through a single insert statement by using loop?
warm regrads,
ammy
If you're using just a regular html table add 'runat="server"' Tag with JS as well as html form controls then just change TableRow to HtmlTableRow and TextBox to HtmlInputText. All of those controls are in the System.Web.UI.HtmlControls namespace.
Assuming you're using the Table server control then it's just:
foreach (TableRow row in table.Rows)
{
var col2 = (TextBox)row.Cells[1].Controls[0];
//Do DB inserting stuff here With col2
string stringToInsert= col2 .Text;
}
I'm asuming you're using MVC..
You can start by creating a model such as:
public class YourModel
{
public IEnumerable<Users> users { get; set; }
}
Than create a view and add the rows dynamically by script given below:
<script type="text/javascript">
var Rows = 1;
// We already got the 0 element as html so start from 1
function AddUser() {
$("#UserTable").append('<tr>' +
'<td><input type="text" name="users[' + Rows + '].Name" style="width:100px;" /></td>' +
'<td><input type="text" name="users[' + Rows + '].Surname" style="width:100px;" /></td>' +
'<td><input type="text" name="users[' + Rows + '].Age" style="width:50px;" /></td>' +
'<td><input type="text" name="users[' + Rows + '].Date" style="width:70px;" /></td>' +
'</tr>');
// Add datepicker (this is an optional jQueryUI stuff)
$('input[name="users[' + Rows + '].Date"]').datepicker({ dateFormat: 'yy.mm.dd' });
// Go to next row
Rows = Rows + 1;
}
$(document).ready(function(){
// Create an empty row on load
AddUser();
// Than on each click add another row
$('input[type=button]').click(function(){ AddUser(); });
});
</script>
<div>
<table id="UserTable">
<tr>
<td><input type="text" name="user[0].Name" style="width:100px;" value="Berker" /></td>
<td><input type="text" name="user[0].Surname" style="width:100px;" value="YĆ¼ceer" /></td>
<td><input type="text" name="user[0].Age" style="width:50px;" value="24" /></td>
<td><input type="text" name="user[0].Date" style="width:70px;" value="2012.12.11" /></td>
</tr>
</table>
<input type="button" id="add" value="Add" />
</div>
fiddle for the script: http://jsfiddle.net/BerkerYuceer/YFecD/
From your controller you can get the values as show below:
// Thanks to LinqToSql you can define ur Sql-DB this way
YourDBDataContext db = new YourDBDataContext();
//
// POST: /YourForm/Edit
[HttpPost]
public ActionResult Edit(YourModel model)
{
try
{
// if users not empty..
if (model.users != null)
{
// Each User in Users
foreach (var user in model.users)
{ // Save it to your Sql-DB
db.Users.InsertOnSubmit(user);
db.SubmitChanges();
}
}
// Return
return RedirectToAction("Index");
}
catch (Exception ex)
{
return RedirectToAction("Error", new { ErrorMessage = "Message[" + ex.Message + "] - Source[" + ex.Source + "] - StackTrace[" + ex.StackTrace + "] - Data[" + ex.Data + "]" });
}
}
//
// GET: /YourForm/Error
public ActionResult Error(String ErrorMessage)
{
ViewData["Error"] = ErrorMessage;
return View();
}
simple as this!
I have the following Model :
public class FileModel
{
public int Id { get; set; }
[Required(ErrorMessage = "Required")]
[StringLength(100, ErrorMessage = "Max is 100, Min is 3", MinimumLength = 3)]
public string Name { get; set; }
public string Path { get; set; }
[Required(ErrorMessage = "Required")]
public string FileTypeId { get; set; }
public DateTime RegistrationDate { get; set; }
}
the following is my view :
#using (Html.BeginForm("Index", "FileManagement", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<table class="content-table" style="min-width: 600px; border-spacing: 15px;">
<tr>
<td colspan="4" class="table-header">New File
<div class="add-icon">+</div>
</td>
</tr>
<tr>
<td>Name: </td>
<td>#Html.TextBoxFor(q => q.NewFile.Name, new { maxlength = "100", id = "NewFile_Name1", name = "NewFile.Name1" })
<br />#Html.ValidationMessageFor(q => q.NewFile.Name)
</td>
<td>
<input type="file" id="FileUploadField1" /></td>
<td style="width: 16px; text-align: center;"> </td>
</tr>
<tr>
<td colspan="4" style="text-align: center;">
<input type="submit" value="Submit" />
</td>
</tr>
</table>
<script type="text/javascript">
$('.content-table .add-icon').click(function () {
var lastFileField = $('.content-table input[type="file"]').last();
var lastId = lastFileField.attr('id').replace(/\D*/g, '');
lastId = parseInt(lastId) + 1;
var newFields = '<tr>' +
'<td>Name : </td>' +
'<td><input data-val="true" data-val-length="Max chars is 100, Min chars is 3" data-val-length-max="100" data-val-length-min="3" data-val-required="Required" id="NewFile_Name' + lastId + '" name="NewFile.Name' + lastId + '" type="text" value="" /><br /><span class="field-validation-valid" data-valmsg-for="NewFile.Name' + lastId + '" data-valmsg-replace="true"></span></td>' +
'<td><input type="file" id="FileUploadField' + lastId + '"/></td>' +
'<td style="text-align:right;"><div class="delete-icon"></div></td>' +
'</tr>';
var lastTr = $(lastFileField).parents('tr:first')[0];
$(lastTr).after(newFields);
});
$('.content-table .delete-icon').live('click', function () {
$(this).parents('tr:first').remove();
});
</script>
}
As you can see, We have a form for uploading one or more files. So, I've added an + button for users that they can add a file field to form.
Users must enter the name of the file and select a file for uploading. But MVC client validator just validate the first inputs that added with Razor.
How can I force MVC validator to validate all fields at the client side and server side.
Another question is:
How can we handle getting the field values at a MVC Controller.
Thanks
This great blog will help you understand how the default model binder will bind lists and arrays. Just make a ViewModel for your page that looks somewhat like this:
public class FileUploadViewModel
{
public List<FileModel> lFileModels { get; set; }
}
Then in your "+" jQuery function, make sure the generated input names are something like lFileModels.[0].Title (or it might be lFileModels[0].Title, just click that link and you'll figure it out)
Then to get that info in the controller, it's just like any other form!
[HttpPost]
public ActionResult Index(FileUploadViewModel viewModel)
{
}
Edit
Another link for MVC file uploading
Edit 2
After reading your code once again, I now realise that the validation problem is due to adding unobtrusive validations after the library was loaded. You have to re-parse the validators.
$("form").data("validator", null);
$.validator.unobtrusive.parse($("form"));
The binding applies to server-side validation and the 2nd part of your question.
Edit 3
For adding fields, instead of doing it straight in JS, you should Ajax load the section of your form with the file fields. When you click on the add button, it request a partial view of the file fields with, in it's post data, a list of the current items. The partial view then returns a rendered view with an extra field. It's just an idea. I haven't tried or even seen that idea. I just though about it and figured I could share it.