Get ID of control from View into Controller - c#

In my MVC application I have a view where I will display different data from a SQL table. This will generate different amount of drop down lists and text boxes, depending on what is passed in from the Model.
My issue is if I want to then use that data I can't seem to figure out how I can relate control X to object Y in SQL. For example, if I have 2 textboxes that I want to do an update on, then when the Post happens in my application the FormCollection parameter will let me see the Value of the objects, but not their control name or any form of identifying factor.
I could set the Value to a combination of the entered value + a name, then split this, but it seems very much like a lazy workaround.
I've tried to assign an ID to each, for example:
#foreach (DataObject item in Model.AllDataObjects)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Data)
</td>
<td>
#if (item.Rule.Contains("Yes;No"))
{
#Html.DropDownListFor(model => item.Value, new List<SelectListItem>
{
new SelectListItem {Text="Yes", Value="Yes"},
new SelectListItem {Text="No", Value="No" }
}, new { #id = item.ObjectId });
}
else
{
#Html.TextAreaFor(model => item.Value, new { style = "width: 400px;", #rows = 5, #id = item.ObjectId })
}
</td>
</tr>
}
Edit: The following is my Post ActionResult method in the Controller, albeit it isn't complete as I can't figure out how to get an ID for the control from the FormCollection
[HttpPost]
[ValidateInput(false)]
public ActionResult UpdateData(FormCollection collection, int objectId=0)
{
try
{
int propertyTypeId = 0;
string propertyValue = string.Empty;
// Get all the control values from the collection
string[] allValues = new string[] { };
IValueProvider valueProvider = collection.ToValueProvider();
foreach(string key in collection.Keys)
{
ValueProviderResult result = valueProvider.GetValue(key);
allValues = result.RawValue as string[];
}
ObjectData objectData = _execution.GetObjectDetails(0);
UpdateDataResponse result = _execution.UpdateData(0, objectId,
objectValue, UserName);
return RedirectToAction("Details",
new { ObjectId = objectData.ObjectId, error = result.ErrorMessage });
}
catch (Exception ex)
{
// My exception handling here
}
}
So I can see in the mark-up that the controls are assigned the object ID as their own ID, but how can I get this back? When I check FormCollection I only see the values for each control, but no way of identifying which is which.
Edit: I'm using MVC version 4.

A form only submits the values of its successful controls (as name/value pairs based on the controls name and value attributes) so if you do not generate a control for the ObjectId properties, they will not be submitted.
However, you current use of foreach loop will not allow you to obtain any meaning information from the data which is posted because all your names are identical and there is no way to reliable match up which value belongs to which item in the collection. Instead use a for loop or EditorTemplate in the view and bind to your model, rather than using FormCollection.
The view should be
#for (int i = 0; i < Model.AllDataObjects.Count; i++)
{
<tr>
<td>#Html.DisplayFor(m => m.AllDataObjects[i].Name)</td>
<td>#Html.DisplayFor(m => m.AllDataObjects[i].Data)</td>
<td>
#Html.HiddenFor(m => m.AllDataObjects[i].ObjectId)
#if (Model.AllDataObjects[i].Rule.Contains("Yes;No"))
{
#Html.DropDownListFor(m => m.AllDataObjects[i].Value, new SelectList(new string[]{ "Yes", "No" }));
}
else
{
#Html.TextAreaFor(m => m.AllDataObjects[i].Value, new { style = "width: 400px;", #rows = 5 })
}
</td>
</tr>
}
And assuming the model in the view is #model MyModel, change the POST method to
[HttpPost]
[ValidateInput(false)]
public ActionResult UpdateData(MyModel model)
and the value of model.AllDataObjects will contain a collection with its ObjectId and Value properties correctly bound.
For more information on why using a foreach loop will not work, refer to this answer.

Related

submit button only sends the first checkbox value

I have a simple project that I want to send information via form and multiple check boxes in asp.net mvc.
This is the view:
And this is the controller method:
[HttpPost]
public ActionResult Index(Customer customer)
{
if (customer != null)
{
return Content(customer.Name.ToString());
}
return Content("empty");
}
I know that I am getting a single variable in the controller method but the point is that if I put an IEnumerable type like list, array or even IEnumerable itself I will receive null value, and if I put a single variable the view page only sends the value of the first checkbox.
Here you can see that I have chosen the B option but the view still returns the A option.
Model binding to a list doesn't play nicely with foreach; you need to use for instead.
ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries - Scott Hanselman's Blog
Which means you'll need to change your model to be an indexable list.
#model IReadOnlyList<WebApplication2.Models.Customer>
...
#for (int index = 0; index < Model.Count; index++)
{
<tr>
<td>#Html.DisplayFor(m => m[index].Name)</td>
<td>
#Html.ActionLink("Edit", "Edit", new { Model[index].ID })
#Html.ActionLink("Details", "Details", new { Model[index].ID })
#Html.ActionLink("Delete", "Delete", new { Model[index].ID })
#Html.CheckBoxFor(m => m[index].IsSelected)
#Html.HiddenFor(m => m[index].ID)
#Html.HiddenFor(m => m.[index].Name)
</td>
</tr>
}

DropDownList how select default value

I have many DropDownLists on page
class BigViewModel
{
public List<SmallViewModel> SmallVM {get;set;}
public List<SelectListItem> Items {get;set;}
//some other properties
}
class SmallViewModel
{
public string ItemId {get;set;}
//some other properties
}
<table>
#for( var i = 0;i<Model.SmallVM.Count();i++)
{
<tr>
<td>
#Html.DropdownListFor(m=> m.SmallVM.ItemId, Model.Items)
</td>
</tr>
}
//display other properties
</table>
in controller
bigViewModel.Items = List<SelectListItem>
{
new SelectListItem{Value = "1", Text = "aaa"},
new SelectListItem{Value = "2", Text = "bbb"},
new SelectListItem{Value = "3", Text = "ccc"},
}
bigViewModel.SmallVM = new List<SmallViewModel>
{
new SmallViewModel{ItemId = 3},
new SmallViewModel{ItemId = 2},
}
In controller I set diffrent ItemId for every SmallVM and each DropDownList uses the same Items collection. I want to set default Value from SmallViewModel for each DropDownList. For example in this case there are two DropDownLists first should display default text "ccc" and second "bbb".
Should I put diffrent List<SelectedListItem> for every SmallViewModel and set them Selected property or there is other way?
This behavior has been reported as a bug on CodePlex but not yet fixed. Using DropDownListFor() in a for loop does not bind correctly and the first option is always selected despite the value of the property. In order for DropDownListFor() to work correctly when using a collection, you need to use an EditorTemplate for the model.
In /Views/Shared/EditorTemplates/SmallViewModel.cshtml
#model SmallViewModel
#Html.DropdownListFor(m => m.ItemId, (SelectList)ViewData["Items"])
Then in the main view
#model BigViewModel
#using(Html.BeginForm())
{
// Pass the select list to the EditorTemplate as addtionalViewData
#Html.EditorFor(m => m.SmallVM, new { Items = Model.Items })
<input type="submit" />
}
You should now have 2 <select> controls displaying "ccc" and "bbb" respectively.
Based on your code updates, I think you just need to modify your view code to:
#Html.DropdownListFor(m=> m.SmallVM[i].ItemId, Model.Items)
However, I have distinct feeling that this is very much an XY problem, and although this change will make everything wire up on post, you're still not going to be getting what you actually need for the true problem you're trying to solve.

TwitterBootStrapMVC Dropdownlist Selected Value

I can't get the selected values from the following dropdownlist, here how I generate the list:
In the repository:
public IQueryable<OhaType> GetAllOhaTypes ()
{ return db.OhaType.OrderBy(d => d.Name); }
In the controller:
....
var OhTypes = _medicationRepository.GetAllOhaTypes();
ViewBag.OhTypes = new SelectList(OhTypes, "OhaTypeId", "Name");
.....
In the View:
#foreach (var item in this.Model.OhaMedication)
{
<tr>
<td>
#(Html.Bootstrap().DropDownList("OhaTypeId", (SelectList)ViewBag.OhTypes)
.HtmlAttributes(new
{
Name ="OhaTypeId",
style = "width: 300px;"
}).Size(InputSize.Small)
.Class("form-group input-sm").Id("OhaTypeId")
.ShowValidationMessage(true))
</td>
.....
I get the first item of the list displayed not the correct value. I get the correct values in other fields from the table.
I also tried "#HTML.DropdownList(...)", same issue.
Would appreciate your suggestions.

C# RemoveAt keeps removing the last item

Can't seem to get the list to remove the correct item.
I've put breakpoints in the code to test if the id is the correct index with shipment.shipmentfreight[id] within the Immediate Window and it shows the correct list item, but it always removes the last item.
In my view model I have a list of ShipmentFreightLine declared as:
public List<ShipmentFreightLine> shipmentfreight { get; set; }
I have a call back that removes the line that returns the new partial view:
public virtual ActionResult ShipmentNewFreightLineRemove(int id,NewShipment shipment)
{
int cnt = shipment.shipmentfreight.Count();
if (id >= 0 && cnt != 1 && id <= cnt)
{
shipment.shipmentfreight.RemoveAt(id);
//shipment.shipmentfreight.RemoveAt(shipment.shipmentfreight.IndexOf(shipment.shipmentfreight[id]));
//shipment.shipmentfreight.Remove(shipment.shipmentfreight[id]);
//shipment.shipmentfreight[id]
}
return PartialView("ShipmentNewFreightLineEdit", shipment);
}
As you can see I've commented out different variations that I have tried and each one removes the last item in the list.
What am I missing?
I am coding in VS2012 ASP.NET MVC using C#.
Any help would be aprreciated
As requested by Kirk the id field is from an anchor
Remove
Which calls this javascript function
function RemoveFreightLine(index) {
var ShipmentData = $('#fShipmentNew').serialize();
var url = "/Tracking/ShipmentNewFreightLineRemove/" + index
$.post(url, ShipmentData,
function (data) {
$("#dNewFreightLines").html(data);
});
}
These all have the correct id in them so i don't believe its the cause
I traced the view and found that it is removing the correct item in the list but the view is showing the old data
<table id="tFreight" class ="tblFreight">
<tbody>
#for (int i = 0; i < Model.shipmentfreight.Count; i++)
{
<tr>
<td>#(i+1)</td>
<td>#Html.TextBoxFor(m => m.shipmentfreight[i].Pieces, new { autocomplete = "off", #Class = "txtboxsmall",maxlength ="3"})</td>
<td>#Html.TextBoxFor(m => m.shipmentfreight[i].Description,new { autocomplete = "off", #Class = "txtareasmall"})</td>
<td>#Html.TextBoxFor(m => m.shipmentfreight[i].WeightPerPiece, new { autocomplete = "off", #Class = "txtboxsmall",maxlength ="3"})</td>
<td>#Html.TextBoxFor(m => m.shipmentfreight[i].ActualWeight, new { autocomplete = "off", #Class = "txtboxsmall",maxlength ="3"})</td>
<td>#Html.TextBoxFor(m => m.shipmentfreight[i].Length, new { autocomplete = "off", #Class = "txtboxsmall",maxlength ="3"})</td>
<td>#Html.TextBoxFor(m => m.shipmentfreight[i].Width, new { autocomplete = "off", #Class = "txtboxsmall",maxlength ="3"})</td>
<td>#Html.TextBoxFor(m => m.shipmentfreight[i].Height, new { autocomplete = "off", #Class = "txtboxsmall",maxlength ="3"})</td>
<td>Remove</td>
<td>#Model.shipmentfreight[i].Description</td>
</tr>
}
</tbody>
</table>
The last cell was for debugging purposes and does display the correct information.
I am thinking its some quirk with TextBoxFor that I don't know about.
Basically the last cell is showing the correct data from the new list but anything in the TextBoxFor is showing the old list data.
Any ideas on why I am getting this behavior?
Looks like this was a problem with the ViewData and not the List.
This seems to fix the problem:
ViewData = null;
or
ModelState.Clear();
Found the answer with Why won't a List of complex types bound to TextBoxes in a table show changes to the model in MVC 4? and TextBoxFor Helper retains previous value even when model value is empty
As with all things...Finding the real cause is key to finding the answer.

How to perform a simple multiple selection in a table ASP.NET MVC4

Here is what my view looks like:
#model Affiliate
<div class="box paint color_16">
<div class="title">
<h4><i class="icon-tasks"></i><span>#Model.CompanyName's Commissions</span> </h4>
</div>
<div class="content top ">
<div class="subtitle">
#Html.ActionLink("Void", "DeleteInvoice", new { commList = "??", affId = Model.Id }, new { #class = "btn" })
#Html.ActionLink("Create Invoice", "CreateInvoice", new { commList = "??", affId = Model.Id }, new { #class = "btn" })
#Html.ActionLink("Pay", "PayInvoice", new { commList = "??", affId = Model.Id }, new { #class = "btn" })
</div>
<table class="table table-striped table-hover">
<tr>
<h3>Commissions</h3>
</tr>
<tr>
<td></td>
<td>Amount</td>
<td>Status</td>
<td>Action</td>
</tr>
#foreach (var item in Model.Commissions)
{
<tr>
#if (item.Status == ViewBag.PaymentStatus || ViewBag.PaymentStatus == "All")
{
<td>#Html.CheckBox("commId", new { value = item.Id })</td>
<td>#Html.DisplayFor(x => item.PayoutAmount)</td>
<td>#Html.DisplayFor(x => item.Status)</td>
}
</tr>
}
</table>
</div>
What I want to be able to do is when I hit an actionlink on the top, grab all the items from the table that are checked, and pass that list of id's to the controller logic. I am assuming a viewmodel may be the solution, something like this:
public Affiliate affilite { get; set; }
public List<int> selectedItems { get; set; }
etc.
But how to I get the selected Items into that VM selectedItems container?
Based off your comments, you don't seem to be looking for the most "correct" answer, but rather just a quick and dirty "how would I do this" answer. If you just want to pass the list, you could setup your controller action like this:
public ActionResult MyAction(int[] id)
{
...
}
Or, you seem to indicate it is strongly typed to a view model with a property that contains a List (I would shorten the name of the property, you'll see why in a second).
In javascript, the easiest thing to do would be to use jQuery to bind a click event on your hyperlink that gets the list of items that are checked and appends that to the query string.
$("#myLink").click(function()
{
var url = "site.com/action?";
var ids = $(".table").find("input:checked");
ids.each(function()
{
url += "id=" + $(this).val() + "&"
});
window.location = url;
});
Basically, you want to create one long query string with the action parameter's name repeated over and over, which identifies an array. It looks something like this (id is for int[] id in MyAction):
id=15&id=20&id=25&id=30&....
And then once the query string is built, redirect the user to that url. MVC should then be able to bind that to an array and you're all set.
That's basically the idea, anyway; the syntax and the javascript I wrote could be way off so don't copy my code and expect it to work as is - I wrote that off the top of my head. If your action is bound to a viewmodel, then you need to set the parameter in the query string to the name of the property of your model:
selectedids=1&selectedids=2&selectedids=3...
Or, if the array is a property of an object, which is a property of the model...
model.selectedids=1&model.selectedids=2&model.selectedids=3...
You'll just need to play around with it some.
Use html checks inside form tag ( you could use helpers too) and post the model to a post action.
MVC will serialize the model automatically

Categories

Resources