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!
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
So I have a simple ASP.NET/ C# generated html form where I have a list of textboxes that I want to be able to add and/or delete on the fly. There are pre-existing textboxes that are generated from a SP that look like this, with an 'add another textbox' button below:
<tr>
<td id="lblRole" style="vertical-align:top;" ><strong>The Role *</strong><br />(2000 characters maximum each)</td>
<td id="rolesColumn">
<div id="roles-1" class="div_row">
<textarea name="ctl00$mainContent$uxRolesList$ctl01" rows="5" cols="100"
id="ctl00_mainContent_uxRolesList_ctl01">yuyuy</textarea>
<input type="button" style="vertical-align:top;" value="X" class="remove-roles-btn" /><br /><br />
</div>
<input type="hidden" name="ctl00$mainContent$uxTxtBoxRolesCount"
id="ctl00_mainContent_uxTxtBoxRolesCount" value="1" />
</td>
</tr>
<tr>
<td> </td>
<td>
<input type="submit" name="ctl00$mainContent$uxAddRoleBtn"
value="Add a new role requirement"
id="ctl00_mainContent_uxAddRoleBtn" class="btn" />
</td>
</tr>
My jQuery is this:
$("#ctl00_mainContent_uxAddRoleBtn").live("click", (function (e) {
var rolesCounter = $('#ctl00_mainContent_uxTxtBoxRolesCount').val();
rolesCounter++;
if (rolesCounter < 10) {
var rolesCounterText = "0" + rolesCounter;
} else {
var rolesCounterText = rolesCounter;
}
$('#rolesColumn').append("<div id='roles-" + rolesCounter + "' class='div_row'><textarea name='ctl00$mainContent$uxRolesList$ctl" + rolesCounterText + "' rows='5' cols='100' id='ctl00_mainContent_uxRolesList_ctl" + rolesCounterText + "'></textarea><input class='remove-roles-btn' type='button' value='X' style='vertical-align:top;' /><br /><br /></div>");
e.preventDefault();
$('#ctl00_mainContent_uxTxtBoxRolesCount').val(rolesCounter);
}));
$(".remove-roles-btn").on("click", (function (e) {
$(this).parents('.div_row').remove();
e.preventDefault();
var rolesCounter = $('#ctl00_mainContent_uxTxtBoxRolesCount').val();
rolesCounter--;
$('#ctl00_mainContent_uxTxtBoxRolesCount').val(rolesCounter);
}));
But when I click to add a new textbox, all the textboxes are deleted.
And when I click to delete a textbox, nothing happens.
Thank you.
You have a typo in your code:
$("#ctl00_mainContent_uxAddRoleBtn").live("click", (function (e) {
//-------------------------------------------^----here you can see a "("
and here:
$(".remove-roles-btn").on("click", (function (e) {
//------------------^-----------------here
but i suggest you to use .on() method:
$(document).on("click", "#ctl00_mainContent_uxAddRoleBtn", function (e) {
and this:
$(document).on("click", ".remove-roles-btn", function (e) {
NOTE, be sure to be using jQuery 1.8.3 or lower, otherwise it will NOT work. All you have to do is change 'on' to 'live'
$(".remove-roles-btn").live("click", (function (e) {
Here is a simple example with your code that shows it working.
For remove button you need to use event delegation
$(document).on("click", "#rolesColumn .remove-roles-btn", function (e) {
e.preventDefault();
$(this).closest('.div_row').remove();
var rolesCounter = $('#ctl00_mainContent_uxTxtBoxRolesCount').val();
$('#ctl00_mainContent_uxTxtBoxRolesCount').val(rolesCounter - 1);
});
$(document).on("click", "#ctl00_mainContent_uxAddRoleBtn", function (e) {
var rolesCounter = $('#ctl00_mainContent_uxTxtBoxRolesCount').val();
rolesCounter++;
if (rolesCounter < 10) {
var rolesCounterText = "0" + rolesCounter;
} else {
var rolesCounterText = rolesCounter;
}
$('#rolesColumn').append("<div id='roles-" + rolesCounter + "' class='div_row'><textarea name='ctl00$mainContent$uxRolesList$ctl" + rolesCounterText + "' rows='5' cols='100' id='ctl00_mainContent_uxRolesList_ctl" + rolesCounterText + "'></textarea><input class='remove-roles-btn' type='button' value='X' style='vertical-align:top;' /><br /><br /></div>");
e.preventDefault();
$('#ctl00_mainContent_uxTxtBoxRolesCount').val(rolesCounter);
});
Demo: Fiddle
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.
I am using c# and razor to produce a list of invoices. Each invoice is a table row and has a huge list of notes against it. To avoid a massive amount of space between rows, I want to hide the notes and allow a pop to view it. It is currently:
<td>
#foreach (var invoiceLine in invoice.InvoiceLines)
{
<p>
<strong>#invoiceLine.Date.ToShortDateString() #invoiceLine.Username</strong> <br />
#Html.Raw(invoiceLine.Notes.Replace(Environment.NewLine, "<br />"))
#Html.Raw((invoiceLine.DueDate.HasValue ? "<br /><strong>Follow up:</strong> " + invoiceLine.DueDate.Value.ToShortDateString() : ""))
#Html.Raw(invoiceLine.Completed ? "<br /><strong>Completed</strong>" : "")
</p>
}
So what I want to do is to add the popup using jquery:
$(function () {
$('#clickMe').click(function (event) {
var mytext = $('#myText').val();
$('<div id="dialog">' + mytext + '</div>').appendTo('body');
event.preventDefault();
$("#dialog").dialog({
width: 600,
modal: true,
close: function (event, ui) {
$("#dialog").hide();
}
});
}); //close click
});
Then modify my code:
<td>
<h3 id="clickMe">Open Notes</h3>
<textarea cols="1" rows="75" id="myText" style="display:none">
#foreach (var invoiceLine in invoice.InvoiceLines)
{
<p>
<strong>#invoiceLine.Date.ToShortDateString() #invoiceLine.Username</strong> <br />
#Html.Raw(invoiceLine.Notes.Replace(Environment.NewLine, "<br />"))
#Html.Raw((invoiceLine.DueDate.HasValue ? "<br /><strong>Follow up:</strong> " + invoiceLine.DueDate.Value.ToShortDateString() : ""))
#Html.Raw(invoiceLine.Completed ? "<br /><strong>Completed</strong>" : "")
</p>
}
</textarea>
</td>
First problem is, that only the first row appears. I presume because my id is the same all the way down?
How do I make the dialog open for each row?
I am a newb at c# and js btw :)
First: The textarea makes no sense at all.
Then change the parts like this. See it working in the jsfiddle.
HTML
<td>
#foreach (var invoiceLine in invoice.InvoiceLines)
{
<p>
<strong>#invoiceLine.Date.ToShortDateString() #invoiceLine.Username</strong> <br />
#Html.Raw((invoiceLine.DueDate.HasValue ? "<br /><strong>Follow up:</strong> " + invoiceLine.DueDate.Value.ToShortDateString() : ""))
#Html.Raw(invoiceLine.Completed ? "<br /><strong>Completed</strong>" : "")
<h3 class="notesClick">Open Notes</h3>
<div class="notesHtml" style="display:none">
#Html.Raw(invoiceLine.Notes.Replace(Environment.NewLine, "<br />"))
</div>
</p>
}
</td>
JS
$(function() {
$('.notesClick').click(function(event) {
var notes = $(event.currentTarget).next('.notesHtml');
$("<div>").html(notes.html()).dialog();
});
});