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';
});
Related
Currently I'm working on a school project where I have this class members.
The listbox shows the propeties names as in Members.Name.
The problem I have is members with the same name shows the same information in the output textbox. I kinda need a different solution let me paste in my.
public void OutputMember()
{
searchResult = MemberSearch.ByName(Program.memberList, lbmembers.Text);
foreach (Members member in searchResult)
{
tboutput.Text = string.Format("Medlemsnr {0}" +
"Namn: {1}\r\n" +
"Eftername: {2}\r\n" +
"Personnummer: {3}\r\n" +
"Adress: {4}\r\n" +
"Email: {5}\r\n" +
"Tele:{6}\r\n\r\n\r\n",member.MemberNr, member.Name, member.SurName, member.BirthYear.outStringWithId(),
member.Adress, member.Email, member.Tele);
So I understand that the problem of this is the parameters.
I just send a list of members and just the name in text.
public static List<Members> ByName(List<Members> memberList, string member)
{
List<Members> searchList = new List<Members>();
for (int i = 0; i < memberList.Count; i++)
{
if (memberList[i].Name.Equals(member))
{
searchList.Add(memberList[i]);
}
}
return searchList;
so the questions remains how do I view the "right" object in the output textbox by double clicking one of three equal names in the list.
You have not coded what you expect the program to do.
You expect the program to display the related Member based on its position in the list.
But you have coded a search method that returns the Member based on a name match, of which there are possible duplicates.
You are also returning multiple items from the list when you only need 1, and are needlessly looping through all results only to return the last one.
You just need:
public void OutputMember()
{
member = Program.memberList[lbmembers.SelectedIndex]);
tboutput.Text = string.Format("Medlemsnr {0}" +
"Namn: {1}\r\n" +
"Eftername: {2}\r\n" +
"Personnummer: {3}\r\n" +
"Adress: {4}\r\n" +
"Email: {5}\r\n" +
"Tele:{6}\r\n\r\n\r\n",member.MemberNr, member.Name, member.SurName, member.BirthYear.outStringWithId(),
member.Adress, member.Email, member.Tele);
}
MVC 4 seems to be having trouble parsing the object I am sending it.
Using jQuery's ajax function, I send the data using POST request. I receive it in the Request object, it appears like this in the Request.Form:
{Name=Test&Groups%5b0%5d%5bName%5d=GroupName1&Groups%5b0%5d%5bCount%5d=123Groups%5b1%5d%5bName%5d=GroupName2&Groups%5b1%5d%5bCount%5d=123ID=bee4c411-f06c-43c6-815f-8002df4f2779}
//formatted for readability
Name=Test &
Groups[0][Name]=GroupName1 &
Groups[0][Count]=123 &
Groups[1][Name]=GroupName2 &
Groups[1][Count]=123 &
ID=bee4c411-f06c-43c6-815f-8002df4f2779
The Name and ID values are parsed just fine, but the Groups array is not... I do get an IEnumerable, and it contains the correct number of groups but the values within are null.
I've read this and this and I can't seem to find what I did wrong...
What am I missing?
The MVC Action looks like this:
public ActionResult UpdateGroups(GroupsListRequest req)
{
[...] //handle the request
}
and GroupsListRequest looks like this:
public class GroupsListRequest
{
public string Name { get; set; }
public string ID { get; set; }
public IEnumerable<GroupRequest> Groups { get; set; }
}
Finally, GroupsRequest looks like this:
public class GroupsRequest
{
public string Name { get; set; }
public int Count { get; set; }
}
try this
remove the square bracket around name and count field in request.
Name:Test &
Groups[0].Name:GroupName1 &
Groups[0].Count:123 &
Groups[1].Name:GroupName2 &
Groups[1].Count:123 &
ID:bee4c411-f06c-43c6-815f-8002df4f2779
your input name should be like this
<input type="text" name="Groups[0].Name" value="George" />
So, as #sangramparmar and #stephenmuecke mentioned, the problem was with the format of the objects in the array - they were surrounded by an extra pair of brackets.
Several circumstances made this problematic to fix:
I am using jQuery to send the request to the server, and the param function is the one causing the problem by parsing the array incorrectly (at least as far as C#'s MVC is concerned
Each request to the server needs to have a (session-constant) key added to its data prior to sending the request. This is performed automatically through a global ajaxSetup
For some reason, MVC failed to read the ajax request's data when I set the contentType to application/json (at least, failed to read it in the way I was expecting... which, if I've learned anything ever, was probably misdirected in some way)
The solution I ended up using was to build my own stringify function to parse the data object to a string, like so:
stringify: function (obj, prefix) {
var objString = '';
prefix = prefix || '';
if(typeof(obj) === 'object') {
if (Array.isArray(obj)) {
_.each(obj, function (item, index) {
objString = objString + utilityMethods.stringify(item, prefix + '%5b' + index + '%5d');
});
} else {
if (prefix != undefined && prefix.length !== 0) {
prefix = prefix + '.';
}
_.each(_.keys(obj), function (key) {
objString = objString + utilityMethods.stringify(obj[key], prefix + key);
});
}
} else {
objString = objString + '&' + prefix + '=' + obj;
}
return objString;
}
Then, in global ajaxSetup, I added a beforeSend to add the needed key, like so:
beforeSend: function (jqXHR, settings) {
var userKeyString = 'userKey=' + [... fetch the user key...];
if (settings.type.toUpperCase() === 'GET') {
settings.url = settings.url + '&' + userKeyString;
} else {
if (settings.data != undefined) {
settings.data = userKeyString + '&' + settings.data;
}
}
},
Now, I do end up with an extra ampersand in each request. I'll fix that eventually... maybe.
I've got a listbox of items where I allow multi select.
I am trying to get the latest most recent item selected, not the order value, but what the user selected last.
If I try to print out .val() I get a list of the selected values, but I want the most recent (the last one in val()).
I tried using :last like so:
$("#MainContent_lbTeams option:selected:last").val();
But last works on order of ID's from ascending to descending, doesnt work on time when someone selects a value.
For instance if I have a list box of 4 items. If I select them in ascending order what I posted above prints correctly. But if I click the 1st, then the 4th, and back to the 2nd what I posted above prints the 4th item (even though I most recently selected the 2nd one).
//when a team is selected
//tell the user what this does to the record
$("#MainContent_lbTeams").on('change', function () {
//was a value selected?
var latest_value = $("#MainContent_lbTeams option:selected:last").val();
var latest_text = $("#MainContent_lbTeams option:selected:last").text();
alert(latest_value);
alert(latest_text);
if ($("#MainContent_lbTeams :selected").length > 0) {
$("#MainContent_lblTeamMembers").text("Members of '" + latest_text + "':");
// act only when the returned promise is resolved
PopulateMembers(latest_value).then(function () {
$("#MainContent_lbMembers_chosen a").removeClass("search-choice-close");
});
}
});
Is there something else I can use to pick up the latest selected value of my multiselect listbox?
Not sure if there is a better way but because it's multi you can find an array of objects for all the added ones in added and removed ones in removed.
$('select').on('change', function () {
var latest_value = $(this).data('value') || {},
latest_text = $(this).data('text') || {},
new_value = {},
new_text = {},
added = [],
removed = [];
$("option:selected", this).each(function(){
new_value[$(this).val()] = true;
new_text[$(this).text()] = true;
if($(this).val() in latest_value){
delete latest_value[$(this).val()];
delete latest_text[$(this).text()];
}else{
added.push({value: $(this).val(), text: $(this).text()});
}
});
for(v in latest_value){
removed.push({value: latest_value[v], text: latest_text[v]});
}
console.log('added', added);
console.log('removed', removed);
$(this).data({value: new_value, text: new_text});
});
DEMO
I ended up doing something a bit different.
Given that the .val() for a multi select is stored as an array, I took the difference between the two arrays. Here is what I ended up with:
var myTeam = [];
$("#trMembers").hide();
$("#MainContent_lbTeams").on('change', function () {
//was a value selected?
//store the difference of what teams are selected vs myTeam[]
var diff = [];
jQuery.grep($("#MainContent_lbTeams").val(), function (el) {
if (jQuery.inArray(el, myTeam) == -1) diff.push(el);
});
if (diff.length > 0) {
var latest_value = diff; //has the latest selected value
var latest_text = $("#MainContent_lbTeams option[value='" + diff + "']").text(); //stores the text
if ($("#MainContent_lbTeams :selected").length > 0) {
$("#dTeamNotice").show();
$("#MainContent_lblTeamMembers").text("Members of '" + latest_text + "':");
// act only when the returned promise is resolved
PopulateMembers(latest_value).then(function () {
$("#MainContent_lbMembers_chosen a").removeClass("search-choice-close");
$("#trMembers").fadeIn();
});
} else {
//hide it...
$("#dTeamNotice").css("display", "none");
$("#trMembers").hide();
}
}
myTeam = $("#MainContent_lbTeams").val();
});
I have list that have values like"
[0] = "{ id = ES10209005, views = 501 }"
[1] = "{ id = HYT0209005, views = 5678}"
[3] = "{ id = POI0209005, views = 4568}"
I would like to pass the values(id,views) to a method using a for each loop.
method(id,views)
Something like:
foreach (string v in updatereponse)
{
method()
}
How do I isolate each value(id,views) from each row in the list then pass it to the method?
The list contains just a bunch of strings, anything based on this to fix the problem would be just a workaround (e.g. string parsing). You should really switch to a strongly typed model, e.g. define a class ViewCount:
public class ViewCount
{
public string Id {get;set;}
public int Views {get;set;}
}
You can then use a List<ViewCount> populate the list:
List<ViewCount> viewcounts = new List<ViewCount>();
viewCounts.Add(new ViewCount() { Id = "ES10209005", Views = 501 });
Since each ViewCount instance has Id and Views properties you can now do the proper thing:
foreach (var item in updatereponse)
{
method(item.Id, item.Views);
}
If you are saving this data in a file, an alternative would be to use XML instead of custom strings, then you could use Linq to XML to populate a List<ViewCount>, e.g. using a simple XML like this:
<ViewCounts>
<ViewCount id="ES10209005" views="501" />
</ViewCounts>
You can then load your list:
XElement viewXml = XElement.Load("test.xml");
List<ViewCount> viewCounts = viewXml.Descendants("ViewCount")
.Select(x => new ViewCount()
{
Id = (string)x.Attribute("id"),
Views = (int)x.Attribute("views")
}).ToList();
foreach (string v in updateresponse)
{
var values = v.Split(",");
var id = values[0].Replace("{ id = ", "").Trim();
var view = values[1].Replace("views = ", "").("}","").Trim();
method(id, value);
}
Here's another way...you may want to add error checking:
String Data = "{ id = ES10209005, views = 501 }";
String[] Segments = Data.Split(new char[] { ' ', ',' });
string ID = Segments[3];
int views = int.Parse(Segments[7]);
Assuming the structure of your String is like you showed us always, this can work for you.
// First split id and views part.
String[] firstSplit = v.Split(',');
// Get the respected value for each part.
String id = firstSplit[0].Split('=')[1].Trim();
String views = firstSplit[1].Split('=')[1].Trim().Replace("}","");
You can use String methods to retrieve the items (use Split and SubString for example) or you can use a regular expression.
E.g.
var list = UpdateResponse[0].Split("=,} ") ;
will result in a list split by all these characters (including space).
Then check the correct indices to use (use a debugger for that). Then you get something like:
var id = list[5];
var views = list[8];
(note: check the indices 5 or 8, they are just a guess).
My Ajax block looks like so:
$('#btnTest').click(function(){
$.getJSON('/User/ViewMessages',function(result) {
// TODO: update the DOM with the items
$("table#tblMessages").append("<tr><td>"+result.MESSAGETEXT+"</td><td>"+result.DATESENT+"</td>");
});
});
My Action in my Controller looks like such:
public ActionResult ViewMessages()
{
var recipient = Convert.ToInt32(HttpContext.Session["User_Id"]);
var query = (from m in context.Messages
from rec in context.Recipients
where rec.RECIPIENT == recipient
where rec.MESSAGEID == m.MESSAGEID
select new
{
m.MESSAGETEXT,
m.DATESENT
}).ToList();
return Json(query.ToList());
}
When Debugging, my query variable returns:
{ MESSAGETEXT = "seresr", DATESENT = {9/15/2011 11:06:45 AM} }
The thing is, my table is added with "undefined" for both my values. Where have i gone wrong? I have already added the maproute as well, but I'm still out of luck.
It looks like you're returning a list, which will be represented in JSON as an array. So your result object is a JavaScript array rather than a single object. To loop through all the items and add a table row for each, try something like this:
$.getJSON('/User/ViewMessages', function(result) {
for (var i in result) {
var row = result[i];
$("table#tblMessages").append("<tr><td>" + row.MESSAGETEXT
+ "</td><td>" + row.DATESENT + "</td>");
}
});