No value for the dropdownlist when my form is submitted - asp.net-mvc

I try to use a dropdownlist in my view for showing a list of authors (users). I'm able to populate this dropdown and see the content in my view. When submitting my form, I debug my action in my controller and when inspecting my model, the value of the field associated with my dropdown is null.
Here is my action controller (before showing my view):
public ActionResult Create()
{
IEnumerable<User> authors = m_AccountBusiness.GetAllUsers();
PageCreateViewModel viewModel = new PageCreateViewModel
{
PageToCreate = new PageFullViewModel(),
Authors = authors.Select(x => new SelectListItem { Text = x.UserName, Value = x.UserID.ToString() })
};
return View(viewModel);
}
Here is (a portion of) my view:
#model MyBlog.ViewModels.PageCreateViewModel
<h3>Create</h3>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.PageToCreate.PageID)
<div class="editor-label">
#Html.LabelFor(model => model.PageToCreate.Title)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.PageToCreate.Title, new { #class = "titleValue" })
#Html.ValidationMessageFor(model => model.PageToCreate.Title)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.PageToCreate.Author)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.PageToCreate.Author, Model.Authors, "Please select...")
</div>
Here is my PageCreateViewModel:
public class PageCreateViewModel
{
public PageFullViewModel PageToCreate { get; set; }
public IEnumerable<SelectListItem> Authors { get; set; }
}
Any idea?
Thanks.

Thank you guys. I finally found my error: it is not Author the right property to bind to, it must be AuthorID !!
<div class="editor-field">
#Html.DropDownListFor(model => model.PageToCreate.AuthorID, Model.Authors, "Please select...")
</div>
Thanks anyway.

You have to add an extra string property to your PageCreateViewModel. In this property we will store the selected value. Lets say it's name is "Author". Edit: I noticed you have a property for it in your model but give it a try like this.
The dropdownlist filling needs to look like this on your view.
#Html.DropDownList("Author", Model.Authors)

Related

ASP.Net MVC 4.0 - Validation Issues With array based properties on ViewModel

ASP.Net MVC 4.0 - Validation Issues With array based properties on ViewModel .
Scenario :
When a ViewModel has a string array as a property type,the default Scaffolding template for say, Edit, does not render the that property in the markup.
Say, I have ViewModel setup like this :
Employee.cs
public class Employee
{
[Required]
public int EmpID
{
get;
set;
}
[Required]
public string FirstName
{
get;
set;
}
[Required]
public string LastName
{
get;
set;
}
[Required]
public string[] Skills
{
get;
set;
}
}
}
The (strongly typed) Edit View generated by the scaffolding template, as shown below, typically skips the portion relevant to field Skills.
**Employee.cshtml**
#model StringArray.Models.Employee
#{
ViewBag.Title = "EditEmployee";
}
<h2>EditEmployee</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Employee</legend>
<div class="editor-label">
#Html.LabelFor(model => model.EmpID)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.EmpID)
#Html.ValidationMessageFor(model => model.EmpID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.LastName)
#Html.ValidationMessageFor(model => model.LastName)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
The corresponding Controller code is
..
[HttpGet]
public ActionResult EditEmployee()
{
Employee E = new Employee()
{
EmpID = 1,
FirstName = "Sandy",
LastName = "Peterson",
Skills = new string[] { "Technology", "Management", "Sports" }
};
return View(E);
}
[HttpPost]
public ActionResult EditEmployee(Employee E)
{
return View(E);
}
To get the missing section for the Skills field, I added
Snippet to the View
<div class="editor-label">
#Html.LabelFor(model => model.Skills)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Skills)
#Html.ValidationMessageFor(model => model.Skills)
</div>
Corresponding UIHint to the ViewModel
[UIHint("String[]")]
public string[] Skills ...
EditorTemplates inside relevant folder as
~\View\shared\EditorTemplates\String[].cshtml
and
~\View\shared\EditorTemplates\mystring.cshtml
string[].cshtml
#model System.String[]
#if(Model != null && Model.Any())
{
for (int i = 0; i < Model.Length; i++)
{
#Html.EditorFor(model => model[i], "mystring")
//Html.ValidationMessageFor(model => model[i])
}
}
mystring.cshtml
#model System.String
#{
//if(Model != null)
{
//To resolve issue/bug with extra dot getting rendered in the name - like
//Skills.[0], Skills.[1], etc.
//ViewData.TemplateInfo.HtmlFieldPrefix=ViewData.TemplateInfo.HtmlFieldPrefix.Replace(".[", "[");
#Html.TextBoxFor(model => model)
}
}
But despite this all, the Validations for the Skills section [with 3 fields/elements - refer the EditEmployee method in Controller above.]
are entirely skipped, on postback.
I tried below changes inside the mystring.cshtml EditorTemplate :
//to correct the rendered names in the browser from Skills.[0] to Skills for all the 3 items in the
//Skills (string array), so that model binding works correctly.
string x = ViewData.TemplateInfo.HtmlFieldPrefix;
x = x.Substring(0, x.LastIndexOf("."));
#Html.TextBoxFor(model =>model, new { Name = x })
Postback WORKS But Validations DON'T, since the "data-valmsg-for" still points to <span class="field-validation-valid" data-valmsg-for="Skills" data-valmsg-replace="true"></span>
and thus doesn't apply at granular level - string element level.
Lastly, I tried removing #Html.ValidationMessageFor(model => model.Skills) from the Employee.cshtml and correspondingly adding the
same to string[].cshtml as #Html.ValidationMessageFor(model => model[i]).
But this led to data-valmsg-for getting rendered for each granular string element like
data-valmsg-for="Skills.[0]" ,
data-valmsg-for="Skills.[1]" and data-valmsg-for="Skills.[2]", respectively.
Note: Validations work for other fields - EmpID, FirstName LastName, BUT NOT for Skills.
Question
How do I set the data-valmsg-for="Skills" for each of the above three granular elements related to Skills property.
I am stuck on this for quite some time now. It would be nice if some one can point out the issue, at the earliest.
Thanks, Sandesh L
This is where you like to change
[Required]
public string[] Skills
{
get;
set;
}
You are giving validation on the array.
you might want to have a new string class call Skill
[Required]
public string Skill
{
get;
set;
}
And you can change to you model with
[Required]
public List<Skill> Skills
{
get;
set;
}
I prefer using List instead of array. Then, you can change you skill view according to the model updated
you template view can be something like
#model IEnumerable<Skill>
<div class="editor-label">
<h3>#Html.LabelFor(model=> model.Skills)
</h3>
</div>
<div class="editor-field">
#foreach (var item in Model)
{ #Html.Label(model => item)
#Html.TextBoxFor(model => item) <br/>
}
#Html.ValidationMessageFor(model => item)

ICollection from ViewModel showing up as null, modelstate is not valid in MVC4

I have the following viewModel:
public class CreateCardViewModel
{
[HiddenInput(DisplayValue = false)]
public int SetId { get; set; }
[Required]
public ICollection<Side> Sides { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime DateCreated { get; set; }
[Required]
public bool IsReady { get; set; }
}
And the following actions defined for Create:
[HttpGet]
public ActionResult Create(int setId)
{
var model = new CreateCardViewModel();
// attach card to current set
model.SetId = setId;
// create a new Side
var side = new Side() {Content = "Blank Side"};
// Add this to the model's Collection
model.Sides = new Collection<Side> { side };
return View(model);
}
[HttpPost]
public ActionResult Create(CreateCardViewModel viewModel)
{
if (ModelState.IsValid)
{
var set = _db.Sets.Single(s => s.SetId == viewModel.SetId);
var card = new Card {Sides = viewModel.Sides};
set.Cards.Add(card);
_db.Save();
}
return View(viewModel);
}
When I try to create a new card, the Sides property of the viewModel is null, so the ModelState is coming up as null. I can't quite figure out why that initial Side isn't getting passed with the model.
My View looks like this:
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>CreateCardViewModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.SetId)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.SetId)
#Html.ValidationMessageFor(model => model.SetId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.DateCreated)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DateCreated)
#Html.ValidationMessageFor(model => model.DateCreated)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.IsReady)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.IsReady)
#Html.ValidationMessageFor(model => model.IsReady)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
There's nothing in your View that is bound to the Sides property of your ViewModel. Without anything in the form to hold the value of that property, it will be null when model binding occurs. You'll need to somehow capture the Side in your form - how are you adding to/removing from this property? Via user interaction that should take place on the form?
Try adding a second argument on Create() of type ICollection<Side> and see if that gets anything passed to it.

How do I specify the column to display in a dropdownlist?

I have a database table with a computed column. The computed column merges two names (first and last) into a single display name. In the code below, I want to specify the value of the computed column, named DisplayName. How do I do this? Thanks!
FYI, this is MVC Beta 4.
<div class="editor-label">
#Html.LabelFor(model => model.PdId, "Pd")
</div>
<div class="editor-field">
#Html.DropDownList("PdId", String.Empty)
#Html.ValidationMessageFor(model => model.PdId)
</div>
Use a view model and a strongly typed view and DropDownListFor helper. Can't see your code, so here’s an untested psuedo:
public class MyViewModel
{
public int PdId { get; set; }
public IEnumerable<SelectListItem> Names { get; set; }
}
populate view model from controller
public ActionResult VIewName(?)
{
var people = // query
var model = new MyViewModel
{
PdId = people.nameId,
FullNames = people.Select(x => new SelectListItem
{
Value = x.nameId.ToString(),
Text = x.FirstName + " " + x.LastName
})
};
return View(model);
}
In view use the strongly typed DropDownListFor helper
#model MyViewModel
#Html.DropDownListFor(
x => x. PdId,
Model. FullNames
)
<div class="editor-label">
#Html.LabelFor(model => model.PdId, "Pd")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.PdId, Model.DisplayName)
#Html.ValidationMessageFor(model => model.PdId)
</div>
As long as the column is an actual field, it should be an option when you connect to the database when you do a SELECT in the DDL with a DISTINCT.
If it is an aliased column, and is being reference with a computated column then just reference the computed column in your select statement on the C# side...

passing dropdown's selected value from view to controller in mvc3?

I have mvc3 web application.
In that i have used EF and populate two dropdownlists from database.
Now when i select values from those dropdownlists i need to show them inside webgrid
how can i do this?
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Mapping</legend>
<div class="editor-label">
#Html.Label("Pricing SecurityID")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.ID,
new SelectList(Model.ID, "Value", "Text"),
"-- Select category --"
)
#Html.ValidationMessageFor(model => model.ID)
</div>
<div class="editor-label">
#Html.Label("CUSIP ID")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.ddlId,
new SelectList(Model.ddlId, "Value", "Text"),
"-- Select category --"
)
#Html.ValidationMessageFor(model => model.ddlId)
</div>
<p>
<input type="submit" value="Mapping" />
</p>
</fieldset>
}
when i clicked on Mapping button it will goes to new page called Mapping.cshtml and have to show webgrid with those two values.
I would create a ViewModel
public class YourClassViewModel
{
public IEnumerable<SelectListItem> Securities{ get; set; }
public int SelectedSecurityId { get; set; }
public IEnumerable<SelectListItem> CUSIPs{ get; set; }
public int SelectedCUSIPId { get; set; }
}
and in my Get Action method, I will return this ViewModel to my strongly typed View
public ActionResult GetThat()
{
YourClassViewModel objVM=new YourClassViewModel();
objVm.Securities=GetAllSecurities() // Get all securities from your data layer
objVm.CUSIPs=GetAllCUSIPs() // Get all CUSIPsfrom your data layer
return View(objVm);
}
And In my View Which is strongly typed,
#model YourClassViewModel
#using (Html.BeginForm())
{
Security :
#Html.DropDownListFor(x => x.SelectedSecurityId ,new SelectList(Model.Securities, "Value", "Text"),"Select one") <br/>
CUSP:
#Html.DropDownListFor(x => x.SelectedCUSIPId ,new SelectList(Model.CUSIPs, "Value", "Text"),"Select one") <br/>
<input type="submit" value="Save" />
}
and now in my HttpPost Action method, I will accept this ViewModel as the parameter and i will have the Selected value there
[HttpPost]
public ActionResult GetThat(YourClassViewModel objVM)
{
// You can access like objVM.SelectedSecurityId
//Save or whatever you do please...
}
Post the form to mapping actionresult. in the actionresult mapping receive dropdown in parameters as mapping(string ID, string ddID). Take these values to view using ViewData.
A better approach will be to make a viewmodel for grid view and make your mapping view strongly typed and use value on grid as you required

MVC, passing values back from multiple partial views in a page

I have problem when I am trying to pass values back from my page which contains the same partial view twice.
My class definiton is like below:
public class Account : IEntity
{
public decimal CurrentBalance { get; set; }
public List<Person> AccountHolders { get; set; }
//to get round the non-existing enum support in EF4.3 wrap enum to int
public int StatusValue { get; set; }
public AccountStatus Status { get { return (AccountStatus)StatusValue; } set { StatusValue = (int) value; } }
public DateTime AccountOpenDate { get; set; }
public DateTime AccountCloseDate { get; set; }
public DateTime AccountSuspensionDate { get; set; }
}
It has a List of Person , which I made a partial view for (for a single one).
<fieldset>
<legend>Person</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Age)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Age)
#Html.ValidationMessageFor(model => model.Age)
</div>
</fieldset>
In the Create page for the Account I include 2 of the partial views I created as below.
<div id="Person1">
#Html.Partial("_CreateAccountHolder" )
</div>
<div id="Person2">
#Html.Partial("_CreateAccountHolder")
</div>
When I look at what is posted back, it contains the values (Name and Age as the properties of Person) I put in the form values of the page and I have have the tow of them as expected:
CurrentBalance=19&Status=Closed&AccountOpenDate=12%2F12%2F2012&Name=mustafa&Age=20&Name=sofia&Age=20&AccountCloseDate=12%2F12%2F2012&AccountSuspensionDate=12%2F12%2F2012
But when I look at my create method on my controller I see the AccountHolder list as null. I tried with various signatures...
public ActionResult Create(Account personalaccount, Person [] accountHolders)
public ActionResult Create(Account personalaccount, List accountHolders)
If I only have one partial view of Person and have my controller like this, I can see the Person object bound correctly.
public ActionResult Create(Account personalaccount, Person accountHolder)
Any ideas as to where I am going wrong?
If I understand your scenario correctly one way to accomplish this is to use Editor Templates instead of partial views. I have a little write up about them here:
codenodes.wordpress.com - MVC3 Editor Templates
To create an Editor Template:
if you don't already have a folder called "EditorTemplates" in the web project of your solution then create one in the Views\Shared folder.
add a new partial view and name it the same as the model you're rendering, in your case Person, so you would call it Person.cshtml (I know partial views are supposed to start with an underscore "_" but for an Editor Template it needs to be named the same as the model).
paste the code from your "_CreateAccountHolder" partial view into the new Person.cshtml Editor Template.
in your Create page render your AccountHolders list thusly:
<div id="People">
#Html.EditorFor(x => x.AccountHolders)
</div>
If you need to have individual divs around each Person then you can add these to your Editor Template. The good thing about Editor Templates is that you only need a single call to the template even if you have multiple Person objects in your list - no need to loop or anything like that as the template automatically renders each Person object. It also names the field correctly so it should post back something like this if for example you have 2 Person objects in your collection:
AccountHolders[0].Name
AccountHolders[0].Age
AccountHolders[1].Name
AccountHolders[1].Age
Here's the code for your Editor Template:
#model Person
<fieldset>
<legend>Person</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Age)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Age)
#Html.ValidationMessageFor(model => model.Age)
</div>
</fieldset>
I understood my problem after reading
[http://stackoverflow.com/questions/653514/asp-net-mvc-model-binding-an-ilist-parameter][1]
Putting 2 partial views of the same type made the view return Name and Age pair with nothing to distingusih between the first and the second pair. I changed the parial view as below, but dont really like it...
<div class="editor-field">
#Html.TextBox("person[0].Name", "")
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-field">
#Html.TextBox("person[0].Age", "")
#Html.ValidationMessageFor(model => model.Age)
</div>
<div class="editor-field">
#Html.TextBox("person[1].Name", "")
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-field">
#Html.TextBox("person[1].Age", "")
#Html.ValidationMessageFor(model => model.Age)
</div>
It now postbacks something like below and I can read the IList in my controller..
CurrentBalance=19&Status=Closed&AccountOpenDate=12%2F12%2F2012&person%5B0%5D.Name=mustafa&person%5B0%5D.Age=19&person%5B1%5D.Name=sofia&person%5B1%5D.Age=20&AccountCloseDate=10%2F10%2F2012&AccountSuspensionDate=12%2F12%2F2012
First you should create CreateAccountModel with two instances of AccountModel, something like:
public class CreateAccountModel
{
public Account Person1 { get; set; }
public Account Person2 { get; set; }
}
Next, when you add your partial views, you should pass individual models to them, e.g:
<div id="Person1">
#Html.Partial("_CreateAccountHolder", Model.Person1)
</div>
<div id="Person2">
#Html.Partial("_CreateAccountHolder", Model.Person2)
</div>
Now MVC will automatically prefix all account fields with PersonX, so all fields will be unique.
Alternatively you can specify prefixes manually when you add your partial views:
{
var prefixData = new ViewDataDictionary { TemplateInfo = { HtmlFieldPrefix = "Person1" } };
Html.RenderPartial("_CreateAccountHolder", new ViewDataDictionary(prefixData));
}

Resources