I am trying to pass the Model data from a View (and PartialView within the View) back to the Controller upon HttpPost. (Adapted from Pass SelectedValue of DropDownList in Html.BeginForm() in ASP.NEt MVC 3)
Why? I want to show a list of assets each with a DropDownList and number of options. Upon submission of form to read the selected items from DropDownList.
My 2 (simplified) models:
public class Booking
{
public int BookingID { get; set; }
public int StoreID { get; set; }
...
public IEnumerable<AssetShort> Assets { get; set; }
}
and
public class AssetShort
{
public int AssetID { get; set; }
....
public int SelectedAction { get; set; }
public IEnumerable<SelectListItem> ActionList { get; set; }
}
In my Booking Controller > Create I build the List:
public ActionResult Booking(int id)
{
// get myBag which contains a List<Asset>
// booking corresponds to 'id'
var myAssets = new List<AssetShort>();
foreach (var a in myBag.Assets)
{
var b = new AssetShort();
b.AssetID = a.ID;
b.SelectedAction = 0;
b.ActionList = new[]
{
new SelectListItem { Selected = true, Value = "0", Text = "Select..."},
new SelectListItem { Selected = false, Value = "1", Text = "Add"},
new SelectListItem { Selected = false, Value = "2", Text = "Remove"},
new SelectListItem { Selected = false, Value = "3", Text = "Relocate"},
new SelectListItem { Selected = false, Value = "4", Text = "Upgrade"},
new SelectListItem { Selected = false, Value = "5", Text = "Downgrade"}
};
myAssets.Add(b);
};
var model = new BookingRequirementsViewModel
{
BookingID = booking.ID,
StoreID = booking.StoreID,
Assets = myAssets.ToList(),
};
return View(model);
My View:
#model uatlab.ViewModels.BookingRequirementsViewModel
#{
ViewBag.Title = "Booking step 2";
}
<h4>Your booking ref. #Model.BookingID</h4>
#using (Html.BeginForm("Booking2", "Booking", FormMethod.Post))
{
<fieldset>
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.StoreID)
#Html.Partial("_Assets", Model.StoreAssets)
<input type="submit" value="Cancel" class="btn btn-default" />
<input type="submit" value="Next" class="btn btn-default" />
</fieldset>
}
The Partial View includes
#foreach (var item in Model)
{
<tr>
<td>#item.Name</td>
<td>#item.Number</td>
<td>#Html.DropDownListFor(modelItem=>item.SelectedAction, item.ActionList)</td>
</tr>
}
So, all this works fine in the browser and I can select dropdowns for each asset listed but when I submit the only value posted back is the StoreID as it is in a "HiddenFor".
The booking2 controller has the model for a parameter:
public ActionResult Booking2(BookingRequirementsViewModel model)
{
//loop through model.Assets and display SelectedActions
}
Let me make it clear what the problems is - in Booking2 controller the Model is null when viewed in Debug mode and I get error "Object reference not set to an instance of an object."
Any ideas please how to pass back the Model to controller from view?
Regards
Craig
You need to create an EditorTemplate for AssetShort. I also suggest moving ActionList to the BookingRequirementsViewModel so your not regenerating a new SelectList for each AssetShort
The models you have posted aren't making sense. Your controller has var model = new BookingRequirementsViewModel { ..., Assets = myAssets.ToList() }; but in the view you refer to #Html.Partial("_Assets", Model.StoreAssets)? Are these 2 different properties. I will assume that StoreAssets is IEnumerable<AssetShort>
/Views/Shared/EditorTemplates/AssetShort.cshtml
#model AssetShort
<tr>
<td>#Html.DispayFor(m => m.Name)</td>
....
<td>
#Html.DropDownListFor(m => m.SelectedAction, (IEnumerable<SelectListItem>)ViewData["actionList"], "--Please select--")
#Html.ValidationMessageFor(m => m.SelectedAction)
</td>
</tr>
In the main view
#model uatlab.ViewModels.BookingRequirementsViewModel
....
#using (Html.BeginForm()) // Not sure why you post to a method with a different name
{
....
#Html.HiddenFor(m => m.StoreID)
#Html.EditorFor(m => m.StoreAssets, new { actionList = Model.ActionList })
....
}
In the controller
public ActionResult Booking(int id)
{
....
var model = new BookingRequirementsViewModel
{
BookingID = booking.ID,
StoreID = booking.StoreID,
Assets = myBag.Assets.Select(a => new AssetShort()
{
AssetID = a.ID,
SelectedAction = a.SelectedAction, // assign this if you want a selected option, otherwise the "--Please select--" option will be selected
....
})
};
ConfigureViewModel(model); // Assign select list
return View(model);
}
And a separate method to generate the SelectList because it needs to be called in the GET method and again in the POST method if you return the view. Note use the overload of DropDownListFor() to generate the option label (null value) as above, and there is no point setting the Selected property (the value of SelectedAction determines what is selected, not this)
private ConfigureViewModel(BookingRequirementsViewModel model)
{
model.ActionList = new[]
{
new SelectListItem { Value = "1", Text = "Add"},
....
new SelectListItem { Value = "5", Text = "Downgrade"}
};
}
and the POST
public ActionResult Booking(BookingRequirementsViewModel model)
{
if (!ModelState.IsValid)
{
ConfigureViewModel(model); // Re-assign select list
return View(model);
}
// save and redirect
}
I recommend also making SelectedAction nullable with the [Required] attribute so you get client and server side validation
public class AssetShort
{
public int AssetID { get; set; }
....
[Required]
public int? SelectedAction { get; set; }
}
Related
I am following this tutorial which works very fine. But the problem is that it only provides key and without it own value (As shown in the picture below). How to include values to each of these keys?
Inspected Elements (Picture)
Model
[Required( ErrorMessage = "Selection is a MUST" )]
public string SelectedItem { get; set; }
private List<string> _items;
public List<string> Items
{
get
{
_items = new List<string>();
_items.Add("One");
_items.Add("Two");
_items.Add("Three");
return _items;
}
}
Controller
public class HomeController : Controller
{
//Render Action
[HttpGet]
public ViewResult Index()
{
DropdownListModel model = new DropdownListModel();
return View(model);
}
//Process Action
[HttpPost]
public ViewResult Index(DropdownListModel model)
{
//TODO: Validate using if(ModelState.IsValid) and process information
return View(model);
}
}
View
<div>
<!--Render the DropDownListmodel -->
#using (Html.BeginForm())
{
<p>#Html.ValidationSummary()</p>
<p>Select an Item : #Html.DropDownListFor(x => x.SelectedItem, new SelectList(Model.Items), "--Choose any Item--" )</p>
<input type="submit" value="Submit" />
}
<!-- Display Selected Item -->
#if (!String.IsNullOrWhiteSpace(Model.SelectedItem))
{
<span>Selected Item : #Model.SelectedItem</span>
}
</div>
What I Attempted
I replaced the codes in models with the code below. But I have error saying Models.KeyValueModel: : EntityType 'KeyValueModel' has no key defined. Define the key for this EntityType.
KeyValueModels: EntityType: EntitySet 'KeyValueModels' is based on type 'KeyValueModel' that has no keys defined.
public List<KeyValueModel> Items
{
get
{
List<KeyValueModel> item = new List<KeyValueModel>();
var n = new KeyValueModel();
n.Key = "1";
n.Value = "One";
item.Add(n);
n = new KeyValueModel();
n.Key = "2";
n.Value = "Second";
item.Add(n);
n = new KeyValueModel();
n.Key = "3";
n.Value = "Three";
item.Add(n);
return item;
}
}
You need to specify on your HttpGet request your Key Value pair like this one:
DropdownListModel model = new DropdownListModel();
model.ItemList = new List<SelectListItem>()
{
new SelectListItem { Text = "One", Value = "1" },
new SelectListItem { Text = "Two", Value = "2" }
};
Then on your model, add this:
public IEnumerable<SelectListItem> ItemList { get; set; }
And on your View:
#Html.DropDownListFor(x => x.SelectedItem, Model.Items, "--Choose any Item--" )
add the Model as:
public string nameofdropdown{ get; set; }
public IEnumerable<SelectListItem> listyouwanttodisplay{ get; set; }
In get method:
Model model=new Model;
List<SelectListItem> cd = Get() //call here----
var additionaldata= new SelectListItem()
{
Value = null,
Text = "--- place holder---"
};
cd.Insert(0, additionaldata);
model.listyouwanttodisplay=cd;
return View (model);
In View:
<div>
#Html.DropDownListFor(model => model.nameofdropdown, new
SelectList(Model.listyouwanttodisplay, "Value","Text"),htmlAttributes: new { #class = "form-control" })
</div>
I am trying to pull the data from a table stored in SQL 2008 into my MVC4
In My Controller :
public ActionResult Test()
{
SurveyEntities survey = new SurveyEntities();
var doctorList = survey.Doctors.ToList();
return View(doctorList);
}
and in my View:
#model IEnumerable<Survey.DataAccess.Doctor>
#{
ViewBag.Title = "Test";
}
<h2>Test</h2>
#using (Html.BeginForm("Test", "Home", FormMethod.Post))
{
#Html.DropDownListFor(m => m.)
}
But I am not able to access the field name in m, say for eg., the doctor's name, to bind it to the dropdownlist.
Where am i going wrong ?
If you dont need to bind to the result value you can also use Html.DropDownList('name', new SelectList(Model)) If you have to use DropDownListFor you would have to change your model and add a property to bind the select result like Html.DropDownListFor(m=>m.DoctorId, new SelectList(Model.Doctors).....
Normally, you want to use ViewModel, so that you can retrieve the selected doctorId when the form is posted back to server.
For example,
Model
public class SurveyModel
{
public string SelectedDoctorId { get; set; }
public IList<SelectListItem> AvailableDoctors { get; set; }
public SurveyModel()
{
AvailableDoctors = new List<SelectListItem>();
}
}
View
#model DemoMvc.Models.SurveyModel
#using (Html.BeginForm("Index", "Home"))
{
#Html.DropDownListFor(m => m.SelectedDoctorId, Model.AvailableDoctors)
<input type="submit" value="Submit" />
}
Controller
public ActionResult Index()
{
var model = new SurveyModel
{
AvailableDoctors = GetDoctorListItems()
};
return View(model);
}
[HttpPost]
public ActionResult Index(SurveyModel model)
{
if (ModelState.IsValid)
{
var doctorId = model.SelectedDoctorId;
// Do something
return View("Success");
}
// If we got this far, something failed, redisplay form
// Fill AvailableDoctors again; otherwise, DropDownList will be blank.
model.AvailableDoctors = GetDoctorListItems();
return View(model);
}
private IList<SelectListItem> GetDoctorListItems()
{
/*
SurveyEntities survey = new SurveyEntities();
return survey.Doctors
.Select(d => new SelectListItem {Text = d, Value = d.ToString()})
.ToList();
*/
// Simulate doctors return from database.
return new List<SelectListItem>
{
new SelectListItem {Text = "John Doe", Value = "1"},
new SelectListItem {Text = "Eric Newton", Value = "2"}
};
}
you can put this code in the Test method:
> ViewData["doctors"] = new SelectList(doctorList,"value", "text");
and then in a view:
#using (Html.BeginForm("Test", "Home", FormMethod.Post))
{
#Html.DropDownList("name", ViewData["doctors"] as SelectList)
input type="submit" value="Submit" />
}
I have a list of Deviation items getting from database. The list contains Severity which may be null. Thats why I made the SeverityNotNull property to transform null values to -1. BTW Severity can be 0-3.
I would like to show Deviations items in which each Severity should be a DropdownList line by line. And of course in the DropdownList the appropriate item should be selected.
my ViewModel is:
[MetadataType(typeof(DeviationMetaData))]
public partial class Deviation {
public int SeverityNotNull {
get { return Severity == null ? -1 : Severity.Value; }
}
...
}
public class DeviationMetaData {
public Nullable<int> Severity { get; set; }
...
}
public class DeviationViewModel {
public Deviation Dev { set; get; }
public IEnumerable<Deviation> Deviations {
get {
DeviationRepository dm = new DeviationRepository();
return dm.GetAll;
}
}
public DeviationViewModel() {
WindowsIdentity current = WindowsIdentity.GetCurrent();
string name = current.Name.Split(new[] { '\\' })[1];
Dev = new Deviation { CreatedBy = name, CreatedDate = DateTime.Now };
}
}
my Controller is:
public ActionResult Index() {
IList<SelectListItem> items = new List<SelectListItem> {
new SelectListItem{Text = "", Value = "-1"},
new SelectListItem{Text = "Minor", Value = "1"},
new SelectListItem{Text = "Major", Value = "2"},
new SelectListItem{Text = "Critical", Value = "3"}
};
ViewBag.SelectSeverity = new SelectList(items, "Value", "Text");
return View( new DeviationViewModel() );
}
my View is:
#model DeviationViewModel
#using (Html.BeginForm()) {
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.Dev.Severity)
</th>
</tr>
#foreach (var item in Model.Deviations) {
<tr>
<td>
#Html.DropDownListFor(modelItem => item.SeverityNotNull, (SelectList)ViewBag.SelectSeverity)
</td>
</tr>
}
</table>
</fieldset>
}
</div>
I checked the SeverityNotNull values and they are correct. In the result there are the Dropdownlists, but nothing is selected. That is the problem. Could you give me some idea? Thanks.
It is not necessary to create a SeverityNotNull property. A SelectList can contain an option with a null value that if selected will post back null. You have multiple other problems as well including using a foreach loop that will render <selects> with duplicate name attributes (wont bind on postback) and duplicate id attributes (invalid html).
Start by creating a view model to represent a Deviation
public class DeviationVM
{
public int ID { get; set; }
public int? Severity { get; set; }
// other properties of the Deviation data model that you want to edit/display
}
Controller
public ActionResult Index()
{
// Get your deviations and map to the view model
List<DeviationVM> model = dm.GetAll().Select(d => new DeviationVM
{
ID = d.ID,
Severity = d.Severity
}).ToList();
// create your select list (
var severityList = new[] { new { ID = 1, Name = "Minor" }, new { ID = 2, Name = "Major" }, new { ID = 2, Name = "Critical" } };
ViewBag.SeverityList = new SelectList(severityList, "ID", "Name");
return View(model)
}
public ActionResult(List<DeviationVM> model)
{
...
}
View
#model List<DeviationVM>
#using (Html.BeginForm()) {
....
for(int i = 0; i < Model.Count; i++)
{
#Html.HiddenFor(m => m[i].ID)
#Html.DropDownListFor(m => m[i].Severity, (SelectList)ViewBag.SeverityList, "Text for null value")
}
Note the use of the for loop which generates <select name="[0].Severity" ..> <select name="[1].Severity" ..> which are correctly named with indexers and will be bound on post back. Note also the use of the overload of #Html.DropDownListFor() which will generate the options as <option value>Text for null value</option> <option value="1">Minor</option> .... The first option does not have a value so if it is selected the value of property Severity will be null on postback.
Note also that if the value of property Severity is initiallt null then the first option will be selected by default. If the value is 2, then the 3rd option will be selected by default.
I have this VM properties
public IList<Guid> SelectedEligiableCategories { get; set; }
public IList<SelectListItem> EligiableCategories { get; set; }
I have this helpers in my view
#Html.LabelFor(x => x.EligibleCategoryFrmVm.SelectedEligiableCategories, "Eligible Categories:")
#Html.ListBoxFor(x => Model.EligibleCategoryFrmVm.SelectedEligiableCategories, Model.EligibleCategoryFrmVm.EligiableCategories, new { #class = "eligibleCategoryListBox" })
I have this code in my controller
List<SelectListItem> eligibleCategoriesListItems = Mapper.Map<List<EligibleCategory>, List<SelectListItem>>(eligibleCategories);
foreach (var rewardTier in creditCard.RewardTiers)
{
CbRewardTierFrmVm rewardTierFrmVm = new CbRewardTierFrmVm();
rewardTierFrmVm.EligibleCategoryFrmVm.EligiableCategories = eligibleCategoriesListItems;
foreach (var ec in rewardTier.EligibleCategories)
{
rewardTierFrmVm.EligibleCategoryFrmVm.SelectedEligiableCategories.Add(ec.Id);
}
vm.CbRewardTierFrmVm.Add(rewardTierFrmVm);
}
Yet when I load up my view. None of values for my ListBox are selected. I am not sure why. If this was a selectList this would work as it would match up the SelectedEligiableCategories to the value in the list.
I am not sure if this is because there is multiple selects
Edit
<select name="CbRewardTierFrmVm[63b504c0-0f9a-47ba-a8ff-db85f48d5f0f].EligibleCategoryFrmVm.SelectedEligiableCategories" multiple="multiple" id="CbRewardTierFrmVm_63b504c0-0f9a-47ba-a8ff-db85f48d5f0f__EligibleCategoryFrmVm_SelectedEligiableCategories" data-val-required="Must choose at least one eligible category." data-val="true" class="eligibleCategoryListBox ui-wizard-content ui-helper-reset ui-state-default" style="display: none;">
<option value="ed2bb5f9-4565-4f69-ab15-9fca011c0692">Gas</option>
</select>
Do you think it is because I am using http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/ ?
Edit2
I gone ahead and make an example. I must be missing something(not sure what). When I use "Darin Dimitrov" it works.
I switched the example to a dropdown as I am getting the same problem with it as well.
In this example I am not using a viewmodel since my initial assumption was somehow the helper I was using from Steven Sanders might be effecting it so I was going off his example.
This does not seem to be the case as I removed it and still get this problem.
public class Gift
{
public string Name { get; set; }
public double Price { get; set; }
public string SelectedItem { get; set; }
public IList<SelectListItem> Items { get; set; }
}
public ActionResult Index()
{
List<SelectListItem> items = new List<SelectListItem>
{
new SelectListItem {Value = "",Text ="--"},
new SelectListItem {Value = "1",Text ="1"},
new SelectListItem {Value = "2",Text ="2"},
};
var initialData = new[] {
new Gift { Name = "Tall Hat", Price = 39.95, Items = items, SelectedItem = "2" },
new Gift { Name = "Long Cloak", Price = 120.00, Items = items, SelectedItem = "1" }
};
return View("Index3",initialData);
}
#model IList<EditorDemo.Models.Gift>
#{
ViewBag.Title = "Index3";
}
#for (int i = 0; i < Model.Count; i++)
{
#Html.DropDownListFor(x => x[i].SelectedItem, new SelectList(Model[i].Items, "Value", "Text"))
}
It seems to not be able to handle when you put it in forloop and try it make more than one dropdown list.
The following works for me.
Model:
public class MyViewModel
{
public IList<Guid> SelectedEligiableCategories { get; set; }
public IList<SelectListItem> EligiableCategories { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
SelectedEligiableCategories = new[]
{
// preselect the second and the fourth item
new Guid("35830042-3556-11E1-BCDC-A6184924019B"),
new Guid("4253876A-3556-11E1-BC17-B7184924019B")
}.ToList(),
EligiableCategories = new[]
{
new SelectListItem { Value = "2DA62E3A-3556-11E1-8A0A-9B184924019B", Text = "item 1" },
new SelectListItem { Value = "35830042-3556-11E1-BCDC-A6184924019B", Text = "item 2" },
new SelectListItem { Value = "3D07EBAC-3556-11E1-8943-B6184924019B", Text = "item 3" },
new SelectListItem { Value = "4253876A-3556-11E1-BC17-B7184924019B", Text = "item 4" },
}
};
return View(model);
}
}
View:
#model MyViewModel
#using (Html.BeginForm())
{
#Html.ListBoxFor(
x => x.SelectedEligiableCategories,
Model.EligiableCategories,
new { #class = "eligibleCategoryListBox" }
)
}
Result:
UPDATE:
Now that you have shown an example allowing to illustrate the problem, you could specify the selected item when building the SelectList:
#Html.DropDownListFor(
x => x[i].SelectedItem,
new SelectList(Model[i].Items, "Value", "Text", Model[i].SelectedItem)
)
The reason a value was not preselected was because you were binding the dropdownlist to a list of properties (x => x[i].SelectedItem) whereas in my example I was using a simple property.
And if you wanted to do this with the ListBoxFor helper you could use the following:
#Html.ListBoxFor(
x => x[i].SelectedItems,
new MultiSelectList(Model[i].Items, "Value", "Text", Model[i].SelectedItems)
)
The SelectedItems property becomes a collection and we use a MultiSelectList instead of a SelectList.
The main problem is using
#Html.DropDownListFor
instead of this
#Html.ListBoxFor
Using the DropDownListFor will NOT help you with multiple values, whatever you do and no matter what your model is. Once you use ListBoxFor ... it will automatically just work !
I'm having problems retrieving the values of a selectlist in my form collection. I've tried making a viewmodel with an attribute with the same name as the select list.
I'm honestly just realizing I REALLY don't understand how model binding works with selectlists. I've just been assuming that the following conventions apply:
Name the select list the same thing as the attribute on the model you want it to bind to.
Apart from that, I really don't get it. I've looked at several books on it and they're useless frankly.
How does a select list work with a) form collection and b) a particular model?
Here's an example:
Model:
public class MyViewModel
{
public string SelectedItemValue { get; set; }
public IEnumerable<SelectListItem> Items { get; set; }
}
Controller:
public class HomeController: Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
// TODO: Fetch those from a repository
Items = new SelectList(
new[]
{
new SelectListItem { Value = "1", Text = "Item 1" },
new SelectListItem { Value = "2", Text = "Item 2" },
new SelectListItem { Value = "3", Text = "Item 3" },
},
"Value",
"Text"
)
};
}
[HttpPost]
public ActionResult Index(string selectedItemValue)
{
// Here you get the selected value from the dropdown
return ...
}
}
View:
<% using (Html.BeginForm()) { %>
<%= Html.DropDownListFor(x => x.SelectedItemValue, Model.Items)
<input type="submit" value="OK" />
<% } %>