Reputation: 2509
Having a pretty tricky ASP.NET MVC bug (using MVC4). I've tried to strip down the code so as to make this example as clear as possible
I have a Model
//this is the mvc model
public class CustomColumnConfiguration //stripped down to the "problem" property only
{
public CustomColumnConfiguration()
{
Columns = new List<ColumnConfig>();
}
public List<ColumnConfig> Columns { get; set; }
}
//MVC model contains a list of these
public class ColumnConfig
{
public ColumnConfig()
{
Name = "";
Alias = "";
Order = 0.0;
}
public string Name { get; set; }
public string Alias { get; set; }
public double Order { get; set; }
public bool IsEnabled { get; set; }
}
Here is a snippet from my razor view
<tbody>
@for (int i = 0; i < Model.Columns.Count; i++)
{
<tr>
<td>Column @(i+1)</td>
<td>@Html.TextBoxFor(x => x.Columns[i].Name)</td>
<td>@Html.TextBoxFor(x => x.Columns[i].Alias)</td>
<td>@Html.TextBoxFor(x => x.Columns[i].Order)</td>
<td>@Html.CheckBoxFor(x => x.Columns[i].UseRange)</td>
<td><button name="RemoveBtn@(i)" class="btn btn-danger submit-action" type="submit" value="remove-@(i)"><i class="icon-remove icon-white"></i></button></td>
</tr>
}
</tbody>
//...
@Html.Hidden("Action")
The following javascript is responsible for setting the "action" input in my form back to the controller so I know which index to remove:
$(document).ready(function () {
$(".submit-action").on("click", function (e) {
var $this = $(this);
$("#action").val($this.val());
});
});
GET and POST Controller code:
public ActionResult ManageCustomColumns()
{
CustomColumnConfiguration model = GetConfiguration(); //this works
return View(model);
}
[HttpPost]
public ActionResult ManageCustomColumns(CustomColumnConfiguration model)
{
var action = Request.Form["Action"];
if (action.Equals("SaveIndexConfiguration", StringComparison.CurrentCultureIgnoreCase))
{
this.AlertSuccess("Configuration Saved");
SetConfiguration(model); //this works
}
else if(action.StartsWith("remove",StringComparison.CurrentCultureIgnoreCase))
{
var indexToRemove = int.Parse(action.Split('-')[1]);
model.Columns.RemoveAt(indexToRemove);
}
else if (action.StartsWith("add", StringComparison.CurrentCultureIgnoreCase))
{
model.Columns.Insert(0, new ColumnConfig() { Name = "NEW_COLUMN_NAME", Alias = "New Column Alias" });
}
return View(model);
}
If I have a page with column configs 1, 2, 3, 4. I can click the "remove" button next to column 2 and when debugging the code in VS, I see that the ColumnConfig with name "2" is in fact deleted from the model's Columns property. When debugging the razor, again, the "2" column is gone.
BUT, with 100% consistency in IE and Chrome, the behavior I'm seeing is that the 1,2,3 are still there and it just deletes the last one (4). This is not consistent at all with what I'm seeing in the debugger. I'm not a novice with MVC but this has got to be one of the most magical bugs I've seen.
Does anyone have any idea what could be happening?
Update Furthermore, when I click add, I expect to have another row in my html table with things like "NEW_COLUMN_NAME". It DOES add another row, but it seems to duplicate the last row's data instead of putting the values from my controller.
Again, my controller would say that I have one new ColumnConfig with "NEW_COLUMN_NAME" in it when I debug, but the page that actually gets rendered to the client has the previous ColumnConfig's data in it. This is maddening!
Upvotes: 3
Views: 1041
Reputation: 506
I know this is an old post but I had the same problem with a .RemoveAt() call. Found a solution. I added this code before my .RemoveAt() call.
ModelState.Clear();
Upvotes: 3
Reputation: 2481
Assumptions:
<form>
tags somewhere that you didn't show us.ManageCustomColumns
action.Note that you are trying to determine which column config to remove based on the value of Request.Form["Action"]
. Where is this form field in your HTML? Note also that your button with the value of remove-1
is called RemoveBtn1
, so the result is that the value you want is going to be in Request.Form["RemoveBtn1"]
. You probably need a hidden form field called "Action" and use Javascript on the button click to populate that field with the button value.
Upvotes: 1