I have a form in my asp.net mvc(C#) application which handles some dynamic controls.
On a button click "Add Row", i will add a row dynamically to the existing table as:
$('#btnAddMore').click(function() {
var _userBodyHtml = '';
_userBodyHtml += '<tr><td><input type="text" name="UserName" id="UserName' + _userDynId + '" size="10" /></td>';
_userBodyHtml += '<td><textarea name="UserComments" id="UserComments' + _userDynId + '" cols="60" rows="1"></textarea></td>';
_userBodyHtml += '</tr>';
_userDynId += 1;
$('#UserBody').append(_userBodyHtml);
});
Then the admin adds the username and comments and submits it.
On submit, i am handling it in controller's action as:
var _frmUserNames = new List<String>(form["UserName"].ToString().Split(','));
var _frmUserComments = new List<String>(form["UserComments"].ToString().Split(','));
if (_frmUserNames.Count > 0 && _frmUserComments.Count > 0)
{
List<UserComments> _userComments = Enumerable.Range(0, _frmUserNames.Count)
.Select(i => new UserComments
{
UserName = _frmUserNames[i],
UserComment = _frmUserComments[i]
}).ToList();
}
From the above code, the _frmUserComments returns the comma separated value when there are more than one textbox with the same name as i am differentiating the textboxes only with different ids.
The problem is when the admin enters the usercomments which has a comma(,) within that comment, then the form value _frmUserComments has the comma separated value and it gives invalid data to the List.
When Admin enters(Case 1) which is fine:
Sam Logged on 12/10/2010
David Looking for enhancement
the form values returns:
_frmUserNames = "Sam,David"
_frmUserComments = "Logged on 12/10/2010,Looking for enhancement"
When Admin enters(Case 2) which is problem causing:
Sam Logged on 12/10/2010
David Logged on 03/01/2011, Looking for enhancement
the form values returns:
_frmUserNames = "Sam,David"
_frmUserComments = "Logged on 12/10/2010,Logged on 03/01/2011, Looking for enhancement"
How can i handle the scenario like this.
Looks like you want to bind collection to model. I recommend to do it as Phil Haack does
Try to set dynamic controls name attributes same as IDs (with index - yours's _userDynId). You'll be able to iterate through form collection in controller, something like that (using LINQ):
foreach (var key in form.AllKeys.Where(k => k.StartsWith("UserName")))
{
var index = key.Replace("UserName", "");
var userName = form[key];
var userComment = form["UserComments" + index];
}
Related
I am building a C# MVC view that includes a field with a series of 4 checkboxes. I can populate the list with the values and return individual values just fine, so the basics of the form appear to work. Here is how the options get populated on the view:
#for (int i = 0; i < Model.QuesOptions.Count; i++)
{
<div>
#Html.HiddenFor(x => x.QuesOptions[i].Name)
#Html.CheckBoxFor(x => x.QuesOptions[i].Checked)
#Html.LabelFor(x => x.QuesOptions[i].Checked, Model.QuesOptions[i].Name)
</div>
}
The ultimate goal is to return a single list of values (ideally comma delimited) of the values of each item checked at at the time of posting. So when the user clicks "submit" on the form, it would be great if a single field on the view model (called "UserAnswer") would populate with a comma delimited string. Is this even possible right on the view model?
I had hopes, probably empty ones, that some variation of this would work:
#Html.HiddenFor(model => model.Ques.UserAnswer,
new { Value = Model.QuesOptions.Where(x=>x.Checked).Select(x=>x.Name) })
Or would this be a job for some kind of extension method or HTML helper?
Finding no way to process comma delimited strings within Razor itself (a way may exist, but I haven't found it), I instead opted for JavaScript. The following code inserts comma delimited lists into hidden fields on the view model:
function submitForm() {
var question2 = "";
for (i = 0; i <= 3; i++) {
var trueOrNo = document.getElementById('Quez2Options_' + i + '__Checked').checked;
if (trueOrNo) {
question2 += document.querySelector('label[for="Quez2Options_' + i + '__Checked"]').innerHTML + ",";
}
}
document.getElementById('Quez2_UserAnswer').value = question2.substring(0, question2.length - 1);
var val3a = document.getElementById('Quez3a').value;
var val3b = document.getElementById('Quez3b').value;
var val3c = document.getElementById('Quez3c').value;
var question3 = val3a + "," + val3b + "," + val3c;
document.getElementById('Quez3_UserAnswer').value = question3;
}
I have a pre-populated text box with with an AutoCompleteCustomSource:
var source = new AutoCompleteStringCollection();
using(propertiesManagementDataContext db = new propertiesManagementDataContext())
{
var q =
(from t in db.GetTable<Tenant>()
select t.lname + " " + t.fname).ToArray();
source.AddRange(q);
}
txt_callerName.AutoCompleteCustomSource = source;
txt_callerName.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
txt_callerName.AutoCompleteSource = AutoCompleteSource.CustomSource;
It works fine, but I want to know the ID of the auto completed item. How to get it?
I tried like this, but had an error:
var q =
(from t in db.GetTable<Tenant>()
select new {lfname = t.lname + " " + t.fname, t.tenantID}).ToArray();
source.AddRange(q);
//this does not work, because q is not an array of strings anymore
How to autocomplete the textbox and save the connection between Lname, Fname and tenantID of the autocompleted items?
I don't see anything in the AutoCompleteStringCollection class that allows you store extra data for each string ... it's pretty much just a collection of strings.
This isn't ideal, but you could include the ID at the end of the string so it doesn't affect the auto-complete experience while the user is typing, then parse the value back out when they make a selection:
var q =
(from t in db.GetTable<Tenant>()
select string.Format("{0} {1} ({2})", t.lname, t.fname, t.tenantID)).ToArray();
Alternatively, you could use a ComboBox control, which (when configured properly) does everything your TextBox is doing, as well as supports setting the data source to a collection of any class you want (not limited to a collection of strings).
You would then specify the property visible to the user (name) with DisplayMember and then the value of each item (ID) with ValueMember.
You could store all the additional data in parallel Dictionary (or other similar collection)
var q =
(from t in db.GetTable<Tenant>()
select new {lfname = t.lname + " " + t.fname, t.tenantID});
Dictionary<String, Int32> idDictionary = new Dictionary<String, Int32>();
foreach(var item in q)
{
idDictionary.Add(item.lfname, item.tenantID);
source.AddRange(q);
}
...
String currentSuggestion = ...
Int32 id = idDictionary[currentSuggestion];
dbContext.Entities.RemoveById(id);
That is not the ideal solution, because you will have to somehow associate for each textBox with AutoCompletion its own dictionary(either storing it in parallel, or subclassing the TextBox). Also there is possibility that lfname can occur twice in the query.
C# MVC web application
I have a button that adds input fields dynamically to my web page on a form.
I specifically give the dynamically added elements an id and name to conform to my Model so that they get passed back as an collection of like items to match a “Artists” collection property in my model.
So the input elements name and ids are for example
Artists[0].artist
Artists[1].artist
Artists[2].artist
In my model I have:
public Collection<Artist> Artists { get; set; }
public class Artist
{
public string artist { get; set; }
}
And here is my script to add remove an element:
var artists = new Array();
var AddArtist = function (e) {
artistname = $("#artistinput").val();
artists.push($("#artistinput").val());
var addDiv, artistVal;
addDiv = $("#artist");
artistVal = $("#artistinput").val();
var input_append = $('<div />'),
label = $('<input />', {
style: 'background-color:#e0ffff',
Value: artistVal,
id: 'artists[' + (artists.length - 1) + '].artist',
name: 'artists[' + (artists.length - 1) + '].artist',
readonly: true
}),
image = $('<img />', {
id: 'removeartist',
src: '/Content/bootstrap/img/cross-button.png',
on: {
click: function () {
input_append.remove();
artists.splice(artists.length - 1, 1);
var test = (artists.length - 1);
alert(test);
}
}
}
);
addDiv.append(input_append.append(label, image));
};
I can add/remove the elements on the actual page. The problem is, if I remove an element from the middle or beginning, the sequence of the name ids are broken and when the collection get passed back to my model the Artists collection is now empty.
So instead of
Artists[0].artist
Artists[1].artist
Artists[2].artist
This may be passed back:
Artists[0].artist
Artists[2].artist
Which is no longer a collection based on how the view is mapped to the model.
I need to rename all the name/ids in a ordered sequence once an item has been removed.
What’s the easiest solution for this problem.
So that this gets passed back
Artists[0].artist
Artists[1].artist
Okay here's what I used in a previous project to revise the IDs and names of inputs to allow model binding when posted to an MVC controller.
The first function takes an object and searches for all inputs, selects and textareas contained within it. The object you pass would probably be a row or div that contains all related inputs per Artist.
// Applies new id and name with correct number sequence to set fields
function reviseFieldNameAndId(obj, newNumber) {
obj.find('input,select,textarea').each(function () {
var parts = this.id.split(/_[\d+]__/); // Everything can be obtained from id only
this.id = parts[0] + '_' + newNumber+ '__' + parts[1]; // Payments_0__PaymentReasonId
this.name = parts[0] + '[' + newNumber+ '].' + parts[1]; // eg. Payments[0].PaymentReasonId
});
}
function reviseAllFieldNamesAndIds() {
$('#artists .row').each(function (index) {
reviseFieldNameAndId($(this), index);
});
}
Use the second function to go through all rows and perform the apply the new sequence.
PS - your answer is near enough the same as this but only works with artists whereas mine will work with different names and ids
I gave each dynamically added input item the same class name then used JavaScript to update and reorder every element id and name in an ordered sequence:
$('.divArtist').each(function (i, obj) {
this.id = 'artists[' + i + '].artist';
this.name = 'artists[' + i + '].artist';
});
In an ASP.NET application I have a IreeView.
Here is one of the nodes in the view:
<td style="white-space: nowrap;">
<input id="TreeView1n10CheckBox" type="checkbox" checked="checked" name="TreeView1n10CheckBox">
<a id="TreeView1t10" onclick="TreeView_SelectNode(TreeView1_Data, this,'TreeView1t10');" href="javascript:__doPostBack('TreeView1','sPreAnalytical\\Test Requisitions\\2 Specimens: 1 Req')" class="TreeView1_0">2 Specimens: 1 Req</a>
As you can see it is a checkbox and there is text after it 'TreeView1','sPreAnalytical\\Test Requisitions\\2 Specimens: 1 Req'
How do I get the text 2 Specimens: 1 Req' on the client side, and how do I modify this text using JavaScript and display the modified TreeView to the client?
this works beautifully:
function check_OnTreeNodeChecked(event) {
var TreeNode = event.srcElement || event.target;
if (TreeNode.tagName == "INPUT" && TreeNode.type == "checkbox") {
if (TreeNode.checked) {
var elNode = document.getElementById("TreeView1t10");
var sText = elNode.innerText || elNode.innerHTML;
alert(sText);
elNode.innerHTML = "Whatever you want";
}
}
}
however since i need to modify the specific text next to the checkbox i need to be able to know which element id it was instead of implicitly specifying var elNode = document.getElementById("TreeView1t10");
Question how do i get the element id of the box that was checked?
The text can be retrieved using:
var elNode = document.getElementById("TreeView1t10");
var sText = elNode.innerText || elNode.innerHTML;
Modify it using:
elNode.innerHTML = "Whatever you want";
To get the ID of the tree node in your click handler:
From the top of my head, untested, something like this will get you the tree node from the checkbox ID:
Checkbox ID = "TreeView1n10CheckBox"; replace "CheckBox" with nothing, so we have
"TreeView1n10". Then replace the "n" with "t" and we have "TreeView1t10", which is
the ID of the corresponding anchor tag.
var sTreeID = TreeNode.id.replace("CheckBox", "").replace("n", "t");
var elTreeNode = document.getElementById(sTreeID);
With jQuery, it's quite simple...
var oldText = $('.TreeView1_0').text();
$('.TreeView1_0').text('new text here');
EDIT :
example here : http://jsfiddle.net/shaneblake/ZG888/
With a tree view the class is probably utilized multiple times so accessing the specific element would be of more use.
var oldText = $('#TreeView1t10').html();
If you need to update all the trees text you can loop through them pretty simply as well.
$('.TreeView1t10').each(function() {
var oldText = $(this).find('a').html();
});
How do I request querystring using javascript from URL
e.g : http://localhost:1247/portal/alias__MySite/lang__en/tabid__3381/default.aspx
I want to get tabid...
var tabid = '<%= Request.QueryString["tabid"] %> ';
Above code works only in aspx page
but i dont need it, any ideas? thanks
There is now a new api URLSearchParams. Use that in conjunction with window.location.search
var urlParams = new URLSearchParams(window.location.search);
console.log(urlParams.get('tabid'));
If your browser does not support URLSearchParams, you can create a custom fallback function:
function getParam(name) {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
var results = regex.exec(location.search);
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
};
console.log(getParam('tabid'));
Don't know why but I've always found the javascript for querystring data fetching a bit hacky. if you don't need this value on the initial page load then perhaps you could use Request.QueryString in the code and set the value to a hidden field, which your javascript will read from?
Try this, It is working perfectly for me.
function getParameterByName(name) {
name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(location.search);
return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}
var tabId=getParameterByName("tabid");
I bet there is a server-side rewrite (DotNetNuke?), so the aspx.cs "sees" the redirection target which contains the correct QueryString.
For the client, you have to use another mechanism because the browser only "sees" the public URL. In this case, a Regex that picks the number behind 'tabid_' and before the next slash should work. This would be the same number (page id?) that the aspx page "sees".
This is what I used:
<script type="text/javascript">
function QueryString(key) {
//Get the full querystring
fullQs = window.location.search.substring(1);
//Break it down into an array of name-value pairs
qsParamsArray = fullQs.split("&");
//Loop through each name-value pair and
//return value in there is a match for the given key
for (i=0;i<qsParamsArray.length;i++) {
strKey = qsParamsArray[i].split("=");
if (strKey[0] == key) {
return strKey[1];
}
}
}
//Test the output (Add ?fname=Cheese&lname=Pizza to your URL)
//You can change the variable to whatever it is you need to do for example, you could
//change firstname to id and lastname to userid and just change the reference in the
//document.write/alert box
var firstname = QueryString("fname");
var lastname = QueryString("lname");
document.write("You are now logged in as " + firstname + " " + lastname + "!");
</script>
You can replace document.write with alert and it would give you an alert box instead!
I used this on my website. Its not done yet but when it is it will be at zducttapestuff.com
The output will look like this: You are now logged in as Cheese Pizza!
This is very unsecure for Passwords though since the password will be shown in the url.