Looks like others have had this problem but I can't seem to find a solution.
I have 2 Models: Person & BillingInfo:
public class Person
{
public string Name { get; set;}
public BillingInfo BillingInfo { get; set; }
}
public class BillingInfo
{
public string BillingName { get; set; }
}
And I'm trying to bind this straight into my Action using the DefaultModelBinder.
public ActionResult DoStuff(Person model)
{
// do stuff
}
However, while the Person.Name property is set, the BillingInfo is always null.
My post looks like this:
"Name=statichippo&BillingInfo.BillingName=statichippo"
Why is BillingInfo always null?
I had this problem, and the answer was staring me in the face for a few hours. I'm including it here because I was searching for nested models not binding and came to this answer.
Make sure that your nested model's properties, like any of your models that you want the binding to work for, have the correct accessors.
// Will not bind!
public string Address1;
public string Address2;
public string Address3;
public string Address4;
public string Address5;
// Will bind
public string Address1 { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public string Address4 { get; set; }
public string Address5 { get; set; }
Status no repro. Your problem is elsewhere and unable to determine where from what you've given as information. The default model binder works perfectly fine with nested classes. I've used it an infinity of times and it has always worked.
Model:
public class Person
{
public string Name { get; set; }
public BillingInfo BillingInfo { get; set; }
}
public class BillingInfo
{
public string BillingName { get; set; }
}
Controller:
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new Person
{
Name = "statichippo",
BillingInfo = new BillingInfo
{
BillingName = "statichippo"
}
};
return View(model);
}
[HttpPost]
public ActionResult Index(Person model)
{
return View(model);
}
}
View:
<% using (Html.BeginForm()) { %>
Name: <%: Html.EditorFor(x => x.Name) %>
<br/>
BillingName: <%: Html.EditorFor(x => x.BillingInfo.BillingName) %>
<input type="submit" value="OK" />
<% } %>
Posted values: Name=statichippo&BillingInfo.BillingName=statichippo is perfectly bound in the POST action. Same works with GET as well.
One possible case when this might not work is the following:
public ActionResult Index(Person billingInfo)
{
return View();
}
Notice how the action parameter is called billingInfo, same name as the BillingInfo property. Make sure this is not your case.
I had the same issue, the previous developer on the project had the property registered with a private setter as he wasn't using this viewmodel in a postback. Something like this:
public MyViewModel NestedModel { get; private set; }
changed to this:
public MyViewModel NestedModel { get; set; }
This is what worked for me.
I changed this:
[HttpPost]
public ActionResult Index(Person model)
{
return View(model);
}
To:
[HttpPost]
public ActionResult Index(FormCollection fc)
{
Person model = new Person();
model.BillingInfo.BillingName = fc["BillingInfo.BillingName"]
/// Add more lines to complete all properties of model as necessary.
return View(model);
}
public class MyNestedClass
{
public string Email { get; set; }
}
public class LoginModel
{
//If you name the property as 'xmodel'(other than 'model' then it is working ok.
public MyNestedClass xmodel {get; set;}
//If you name the property as 'model', then is not working
public MyNestedClass model {get; set;}
public string Test { get; set; }
}
I have had the similiar problem. I spent many hours and find the problem accidentally that I should not use 'model' for the property name
#Html.TextBoxFor(m => m.xmodel.Email) //This is OK
#Html.TextBoxFor(m => m.model.Email) //This is not OK
Related
I have bound listbox data from entity framework. I have selected multiple values in that listbox. But, the values are not fired. The count of the property is 0 only. I am using the code below:
public class Sample1
{
[Key]
public int SampleId{ get; set; }
public string SampleDesc{ get; set; }
}
public class ExpModel
{
public List<Sample1> Sample{ get; set; }
}
public ActionResult Index()
{
ViewData["SampleList"] = new List<Sample1>(entity.samp);
return View();
}
#Html.ListBoxFor(model => model.Sample, new SelectList(((List<Details.Models.Sample1>)ViewData["SampleList"]), "SampleId", "SampleDesc"))
What do I have to do? Please help me...
You should bind the ListBoxFor helper to a property that is a collection of simple/scalar values such as strings or integers:
public class Sample1
{
[Key]
public int SampleId { get; set; }
public string SampleDesc { get; set; }
}
public class ExpModel
{
public List<int> SelectedSampleIds { get; set; }
}
and then:
public ActionResult Index()
{
ViewData["SampleList"] = new List<Sample1>(entity.samp);
return View();
}
and in your view:
#Html.ListBoxFor(
model => model.SelectedSampleIds,
new SelectList(
(List<Details.Models.Sample1>)ViewData["SampleList"],
"SampleId",
"SampleDesc"
)
)
Now when you submit the form, the SelectedSampleIds collection will contain the selected ids in the list box.
I have a ViewModel as below:
public class CheckoutViewModel
{
public string ProductNumber { get; set; }
public string Name { get; set; }
public int Price { get; set; }
public Input UserInput;
public class Input
{
public string Email { get; set; }
public string Phone { get; set; }
}
}
And an action like this:
[HttpPost]
public ActionResult Index(CheckoutViewModel model)
{
// ...
return View();
}
And my model has bound as below:
#model GameUp.WebUI.ViewModels.CheckoutViewModel
#using (Html.BeginForm("Index", "Checkout", FormMethod.Post))
{
#Html.AntiForgeryToken()
<!-- some HTML -->
#Html.LabelFor(m => m.UserInput.Email)
#Html.TextBoxFor(m => m.UserInput.Email)
#Html.LabelFor(model => model.UserInput.Phone)
#Html.TextBoxFor(model => model.UserInput.Phone)
<button>Submit</button>
}
When I submit the form, the UserInput is null. I know ASP.NET MVC is able to bind nested types but in this code is not. Also I can get the Email and Phone values by:
var email = Request.Form["UserInput.Email"];
var phone = Request.Form["UserInput.Phone"];
Maybe I do something wrong! It's a simple model binding you can find everywhere in the web.
You forgot to put a setter in your UserInput, I don't think the setter is automatic. Anyway you can make it work by just putting a getter/setter in your UserInput and no need to do extra in your controller method:
public Input UserInput { get; set; }
Your complete model:
public class CheckoutViewModel
{
public string ProductNumber { get; set; }
public string Name { get; set; }
public int Price { get; set; }
public Input UserInput { get; set; }
public class Input
{
public string Email { get; set; }
public string Phone { get; set; }
}
}
Hope someone can help - this has been bugging me for around 2 hours - its probably something simple :)
Kendo UI Grid sends a request to my controller
http://localhost:1418/user/update?UserID=1&UserName=Admin&RoleName=Admin&Email=c.j.hannon%40gmail.com&Active=true&Company%5BCompanyID%5D=1&Company%5BCompanyName%5D=asd
However, the controller class 'Company' isnt bound by the binder? Can any one help my view model and controller action signature are below:
[HttpGet]
public JsonResult Update(UserViewModel model)
{
svcUser.UpdateUser(new UpdateUserRequest() {
UserID=model.UserID,
RoleID = model.RoleName,
Email = model.Email,
Active = model.Active.GetValueOrDefault(false),
UserName = model.UserName
});
return Json("", JsonRequestBehavior.AllowGet);
}
public class UserViewModel
{
public int UserID { get; set; }
public string UserName { get; set; }
public string RoleName { get; set; }
public string Email { get; set; }
public bool? Active { get; set; }
public CompanyViewModel Company { get; set; }
}
Cheers
Craig
A few things. Your immediate problem is that Company is mapped to a complex object not a primitive type. Kendo Grid just does not do this (as of this writing). Just guessing, but you probably want to setup a foreign key binding on the Grid and just pass back the Id of the company from a listbox. This is not as bad as you think and it will immediatly fix your problem and look nice too.
Maybe personal taste but seems to be a convention. Use the suffix ViewModel for the model that is bound to your View and just the suffix Model for your business objects. So a Kendo Grid is always populated with a Model.
Ex.:
public class UserModel
{
public int UserID { get; set; }
public string UserName { get; set; }
public string RoleName { get; set; }
public string Email { get; set; }
public bool? Active { get; set; }
public int CompanyID { get; set; }
}
public class CompanyModel
{
public int ID { get; set; }
public string Name { get; set; }
}
public class UserViewModel
{
public UserModel UserModel { get; set; }
public IList<CompanyModel> Companies { get; set; }
}
public ActionResult UserEdit(string id)
{
var model = new UserViewModel();
model.UserModel = load...
model.Companies = load list...
return View(model);
}
#model UserViewModel
...
column.ForeignKey(fk => fk.CompanyId, Model.Companies, "ID", "Name")
(Razor Notation)
BUT! This is just an example, you are better off Ajax loading the Grid with the IList becuase I assume you have many Users in the Grid at once, though you could server bind off the ViewModel with a List too. But the list of Companies is probably the same every time, so map it to the View just liek this rather than Ajax load it every time you do a row edit. (not always true)
I have two identical dropdownlists for identical subproperties of my item TaskViewModel.
#Html.DropDownListFor(x => x.AssignedTo.ID, TaskHelper.GetUsersSelect(Model.AssignedTo), new { #class = "select" })
#Html.DropDownListFor(x => x.Controller.ID, TaskHelper.GetUsersSelect(Model.Controller), new { #class = "select" })
Both values sending on posting form (...&AssignedTo.ID=1&Controller.ID=1...), but I always get only task.AssignedTo deserialized
public ActionResult SaveTask(TaskViewModel task)
{
task.AssignedTo.ID //value here
task.Controller.ID //null reference exception
}
public class TaskViewModel
{
public UserViewModel Controller { get; set; }
public UserViewModel AssignedTo{ get; set; }
}
public class UserViewModel
{
public long ID { get; set; }
public string Title { get; set; }
}
What may be a reason for that strange behaviour?
It looks like Controller is a reserved keyword and the default model binder doesn't like it. Try renaming the property:
public class TaskViewModel
{
public UserViewModel TaskController { get; set; }
public UserViewModel AssignedTo { get; set; }
}
The whole problem comes from the ValueProvider that the default model binder uses. When it encounters the Controller navigation property it does this:
var value = ValueProvider.GetValue("Controller");
which unfortunately first looks at route data and after that in query string. So this returns "Home" or whatever the name of the controller is and obviously trying to assign the string "Home" to a class of type UserViewModel cannot result in success.
I can't seem to get the value of a select list to populate the value of the parent object.
I'm using a wrapper object to bind to so that I have access to the values needed for the SelectList as well as the object which needs the value.
I'm willing to bet I'm missing something basic but I can't find it.
I have these models:
public class Status
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual bool IsClosed { get; set; }
public override string ToString()
{
return Name;
}
}
public class Issue
{
public virtual int Id { get; protected set; }
public virtual string Title { get; set; }
public virtual string Description { get; set; }
public virtual Status Status { get; set; }
public virtual DateTime CreatedOn { get; set; }
public virtual DateTime UpdatedOn { get; set; }
}
And a FormViewModel (from NerdDinner examples) to contain the list of Statuses
public class IssueFormViewModel
{
public Issue Issue { get; set; }
public SelectList Statuses { get; set; }
public IssueFormViewModel(Issue issue, IList<Status> statuses)
{
Issue = issue;
Statuses = new SelectList(statuses, "Id", "Name", statuses[1].Id );
}
public IssueFormViewModel() { }
}
My Create Actions on the Controller look like...
public ActionResult Create()
{
IList<Status> statuses;
Issue issue = new Issue();
// NHibernate stuff getting a List<Status>
return View(new IssueFormViewModel(issue,statuses));
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(IssueFormViewModel issueFormView)
{ // At this point issueFormView.Issue.Status == null
// ...
}
My Issue object is bound partially except the status using these names in the View:
<p>
<label for="Issue.Title">Title:</label>
<%= Html.TextBox("Issue.Title", Model.Issue.Title)%>
<%= Html.ValidationMessage("Title", "*") %>
</p>
<p>
<label for="Statuses">Status:</label>
<!-- also tried "Issue.Status" -->
<%= Html.DropDownList("Statuses", Model.Statuses)%>
<%= Html.ValidationMessage("Status", "*")%>
</p>
I tried the dropdown list on my computer and it works, you should make sure NHibernate is bringing back more than 1 item as your code is trying to set the selected item to be the second item.
Statuses = new SelectList(statuses, "Id", "Name", statuses[1].Id);
Remember that Lists a re zero based indexed.
As expected - it WAS something simple.
I changed the view to look like:
<label for="Status.Id">Status:</label>
<%= Html.DropDownList("Status.Id", Model.Statuses)%>
<%= Html.ValidationMessage("Status.Id", "*")%>
and the controller signature to take an "issue" (can't bind to a selectlist!)
[AcceptVerbs(HttpVerbs.Post)]
//public ActionResult Create(IssueFormViewModel issueFormView)
public ActionResult Create(Issue issueToAdd)
{
And in my Post-Create action I have an Issue with a Status. Albeit the status is invalid (it only contains the Id). So before commiting the Issue to the db, I set the issueToAdd like this:
issueToAdd.Status = (from p in GetAllStatuses()
where p.Id == issueToAdd.Status.Id
select p).First();
Edit: And it turns out I didn't even need to fetch a "proper" Status object. It's bound to Id and that's good enough.