Data is empty in Post method asp.net mvc 2 - asp.net-mvc-2

I`m trying to display classes that have properties of type some custom class inside it.
Model:
public class ComplexParent
{
public SimpleChild First { get; set; }
public SimpleChild Second { get; set; }
}
public class SimpleChild
{
public int Id { get; set; }
public string ChildName { get; set; }
public string ChildDescription { get; set; }
}
Controller:
public ActionResult Testify(int id)
{
ComplexParent par = new ComplexParent();
par.First = new SimpleChild() { Id = id };
par.Second = new SimpleChild()
{
Id = id + 1,
ChildName = "Bob",
ChildDescription = "Second"
};
return View("Testify", par);
}
[HttpPost]
public ActionResult Testify(ComplexParent pComplexParent)
{
return View("Testify", pComplexParent);
}
View:
<% using (Html.BeginForm())
{%>
<fieldset>
<legend>Fields</legend>
<%: Html.EditorFor(x => x.First) %>
<br />
<%: Html.EditorFor(x => x.Second.ChildName)%>
<br/>
<br/>
<br/>
<% Html.RenderPartial("SimpleChild", Model.First); %>
<p>
<input type="submit" value="Watch me :-)" />
</p>
</fieldset>
<% } %>
When it comes to Get it works just fine, I can see all the data. But on post pComplexParent parameter is empty (both properties of a complex class are nulls). Probably Im missing something here, but could not get this to work ...
Small addition: view part that only shows editor for name makes Second child not null and name is set to Bob. But I dont understand how to make it just with EditorFor or DisplayFor methods.
UPDATE: Thanks to Darin Dimitrov, who kindly went throught all my code and found what caused this problem. The exact problem was that if you are using display template, asp.net mvc 2 doesnt post any values back and if whole template is has nothing to post back object is null. I still thinking of the way how to get the data, even if you don`t want to edit it. But using editor template does the thing and I have all objects filled with proper data now.

Your view is a bit of a mess. You are using editor templates along with partials for the first child. It is not very clear what fields are included inside the form. I would recommend you using only editor templates:
Model:
public class ComplexParent
{
public SimpleChild First { get; set; }
public SimpleChild Second { get; set; }
}
public class SimpleChild
{
public int Id { get; set; }
public string ChildName { get; set; }
public string ChildDescription { get; set; }
}
Controller:
[HandleError]
public class HomeController : Controller
{
public ActionResult Testify(int id)
{
var par = new ComplexParent();
par.First = new SimpleChild() { Id = id };
par.Second = new SimpleChild()
{
Id = id + 1,
ChildName = "Bob",
ChildDescription = "Second"
};
return View(par);
}
[HttpPost]
public ActionResult Testify(ComplexParent pComplexParent)
{
return View(pComplexParent);
}
}
View:
<% using (Html.BeginForm()) { %>
<%: Html.EditorFor(x => x.First) %>
<%: Html.EditorFor(x => x.Second) %>
<input type="submit" value="Watch me :-)" />
<% } %>
Editor template for SimpleChild (~/Views/Home/EditorTemplates/SimpleChild.ascx):
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<SomeNs.Models.SimpleChild>" %>
<%: Html.HiddenFor(x => x.Id) %>
<%: Html.EditorFor(x => x.ChildName) %>
<%: Html.EditorFor(x => x.ChildDescription) %>
Now if you want to have different editor templates for the two child properties you could either specify the editor template name when including it:
<%: Html.EditorFor(x => x.First, "FirstChildEditor") %>
which corresponds to ~/Views/Home/EditorTemplates/FirstChildEditor.ascx or use an [UIHint] attribute at your model:
public class ComplexParent
{
[UIHint("FirstChildEditor")]
public SimpleChild First { get; set; }
public SimpleChild Second { get; set; }
}
My recommendation is not to use Html.RenderPartial for generating input fields because their names will be hardcoded and won't bind properly depending on your objects hierarchy.

Related

LINQ to Entities error when trying to bind a dropdownlist

I am trying to create a viewmodel and add a SelectListItem to allow me to bind a dropdown list.
I have a very basic viewmodel that looks like this
public class CreatePurchaseViewModel
{
public Product Product { get; set; }
public IEnumerable<SelectListItem> Products { get; set; }
public int SelectedProductId { get; set; }
public DateTime OrderDate { get; set; }
public bool OrderSent { get; set; }
}
My controller looks like this
[HttpGet]
public ActionResult Create()
{
var model = new CreatePurchaseViewModel
{
Products = context.Products.Select(x =>
new SelectListItem()
{
Text = x.ProductName,
Value = x.ProductID
})
};
return View(model);
}
However it complains that Value = x.Product cant convert type int to string. So if I add a .ToString it compiles ok but when I try load the view I get an error
LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression.
My View
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>CreatePurchaseViewModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.SelectedProductId)
</div>
<div class="editor-field">
#Html.DropDownListFor(x => x.SelectedProductId,Model.Products)
#Html.ValidationMessageFor(model => model.SelectedProductId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.OrderDate)
</div>
<div class="editor-field">
#Html.TextBoxFor(model=>model.OrderDate)
#Html.ValidationMessageFor(model => model.OrderDate)
</div>
<div>
Sent
#Html.RadioButtonFor(model => model.OrderSent, "Sent", new { #checked = true })
Not Sent
#Html.RadioButtonFor(model=>model.OrderSent,"Not Sent")
Im pretty new to both entity framework and mvc so any help would be great.
Thank you
You haven't specified how does your Product model look like but we can assume that the ProductID property is integer so you might need to convert it to a string:
[HttpGet]
public ActionResult Create()
{
var model = new CreatePurchaseViewModel
{
Products = context.Products.Select(x =>
new SelectListItem
{
Text = x.ProductName,
Value = x.ProductID.ToString()
}
)
};
return View(model);
}

Using ModelMetadata how do you call DisplayFor and have the metadata used

This question is based on using the Asp.Net MVC tabular layout display template from Phil Haack http://haacked.com/archive/2010/05/05/asp-net-mvc-tabular-display-template.aspx
The problem is in the Html.DisplayFor(m => propertyMetadata.Model). This displays the data from the property just fine, however it doesn't use any of the data annotations that may be present. I'm mostly thinking the DataType annotation here for example DataType.Date. This annotation correctly outputs a short date when used with DisplayFor, but not when the property is reflected from the ModelMetadata as in this example. (It shows a full DateTime)
<% for(int i = 0; i < Model.Count; i++) {
var itemMD = ModelMetadata.FromLambdaExpression(m => m[i], ViewData); %>
<tr>
<% foreach(var property in properties) { %>
<td>
<% var propertyMetadata = itemMD.Properties
.Single(m => m.PropertyName == property.PropertyName); %>
<%= Html.DisplayFor(m => propertyMetadata.Model) %>
</td>
<% } %>
</tr>
An example model would be an
public class TableModel
{
[UIHint("Table")]
public PeriodModel[] Periods { get; set; }
}
public class PeriodModel
{
[DisplayName("Description")]
public string Description { get; set; }
[DisplayName("Date From")]
[DataType(DataType.Date)]
public DateTime DateFrom { get; set; }
[DisplayName("Date To")]
[DataType(DataType.Date)]
public DateTime DateTo { get; set; }
}
So how would this be changed to get the full metadata behaviour of DisplayFor?
Seems this is a simple one, you just have to change:
Html.DisplayFor(m => propertyMetadata.Model)
to
Html.DisplayFor(m => propertyMetadata.Model, propertyMetadata.TemplateHint)
I've been struggling with this myself for a few hours and this is the only hit I found on Google. But the answer is wrong.
propertyMetadata.TemplateHint is always null in my case.
But this works:
Html.DisplayFor(m => propertyMetadata.Model, propertyMetadata.DataTypeName)
you can check #Html.ValueFor(m => propertyMetdata.Model) instead of DisplayFor.

In MVC2, how do I validate fields that aren't in my data model?

I am playing with MVC2 in VS 2010 and am really getting to like it. In a sandbox application that I've started from scratch, my database is represented in an ADO.NET entity data model and have done much of the validation for fields in my data model using Scott Guthrie's "buddy class" approach which has worked very well.
However, in a user registration form that I have designed and am experimenting with, I'd like to add a 'confirm email address' or a 'confirm password' field. Since these fields obviously wouldn't exist in my data model, how would I validate these fields client side and server side?
I would like to implement something like 'Html.ValidationMessageFor', but these fields don't exist in the data model. Any help would be greatly appreciated.
I use view models. I don't create the data model instance to persist until the view model is valid.
Below is a simple example. Notice that some of the properties are data models, but the validation properties only exist on this view model.(the base isn't pertinent here)
public class ProblemAddToDepartmentProductView : ViewModel
{
public Problem Problem { get; set; }
public IList<Product> AllProducts { get; set; }
public IList<Department> AllDepartments { get; set; }
public string ProblemId { get; set; }
public string ProblemName { get; set; }
[DisplayName("Choose the product:")]
[Required(ErrorMessage = "Select the Product.")]
public string SelectedProduct { get; set; }
public SelectList GetProducts()
{
var selectList = new SelectList(AllProducts, "Id", "Name");
return selectList;
}
[DisplayName("Choose the department using this problem for that product:")]
[Required(ErrorMessage = "Select the Department.")]
public string SelectedDepartment { get; set; }
public SelectList GetDepartments()
{
var selectList = new SelectList(AllDepartments, "Id", "Name");
return selectList;
}
internal class ProductSelect
{
public Guid Id { get; set; }
public string Name { get; set; }
}
}
It will also help to see it wired on the page:
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%= Html.LabelFor(x => x.SelectedProduct) %>
</div>
<div class="editor-field">
<%= Html.DropDownListFor(x => x.SelectedProduct, Model.GetProducts(),"--Select One--") %>
<%= Html.ValidationMessageFor(x => x.SelectedProduct)%>
</div>
<div class="editor-label">
<%= Html.LabelFor(x => x.SelectedDepartment) %>
</div>
<div class="editor-field">
<%= Html.DropDownListFor(x => x.SelectedDepartment, Model.GetDepartments(),"--Select One--") %>
<%= Html.ValidationMessageFor(x => x.SelectedDepartment)%>
</div>
<p>
<input type="submit" value="Add Selected" />
</p>
</fieldset>
I also do this so the model will have these values if the validation fails, to pull back in the needed data for the drop downs:
p.ProblemId) %>
<%= Html.HiddenFor(p => p.ProblemName) %>
Client-Side:
Using Javascript Validation
and/or
Server-Side:
Validate in Controller (Using FormCollection) - or
Create "CustomViewModel" Class that encapsulates all validation strongly type your view - or
You could add two string properties to your Model. Doing so will allow you to populate an instance of your model in a Controller and validate appropriately in your Model/s...

Why is FubuMVC new()ing up my view model in PartialForEach?

I'm getting started with FubuMVC and I have a simple Customer -> Order relationship I'm trying to display using nested partials. My domain objects are as follows:
public class Customer
{
private readonly IList<Order> orders = new List<Order>();
public string Name { get; set; }
public IEnumerable<Order> Orders
{
get { return orders; }
}
public void AddOrder(Order order)
{
orders.Add(order);
}
}
public class Order
{
public string Reference { get; set; }
}
I have the following controller classes:
public class CustomersController
{
public IndexViewModel Index(IndexInputModel inputModel)
{
var customer1 = new Customer { Name = "John Smith" };
customer1.AddOrder(new Order { Reference = "ABC123" });
return new IndexViewModel { Customers = new[] { customer1 } };
}
}
public class IndexInputModel
{
}
public class IndexViewModel
{
public IEnumerable<Customer> Customers { get; set; }
}
public class IndexView : FubuPage<IndexViewModel>
{
}
public class CustomerPartial : FubuControl<Customer>
{
}
public class OrderPartial : FubuControl<Order>
{
}
IndexView.aspx: (standard html stuff trimmed)
<div>
<%= this.PartialForEach(x => x.Customers).Using<CustomerPartial>() %>
</div>
CustomerPartial.ascx:
<%# Control Language="C#" Inherits="FubuDemo.Controllers.Customers.CustomerPartial" %>
<div>
Customer
Name: <%= this.DisplayFor(x => x.Name) %> <br />
Orders: (<%= Model.Orders.Count() %>) <br />
<%= this.PartialForEach(x => x.Orders).Using<OrderPartial>() %>
</div>
OrderPartial.ascx:
<%# Control Language="C#" Inherits="FubuDemo.Controllers.Customers.OrderPartial" %>
<div>
Order <br />
Ref: <%= this.DisplayFor(x => x.Reference) %>
</div>
When I view Customers/Index, I see the following:
Customers
Customer Name: John Smith
Orders: (1)
It seems that in CustomerPartial.ascx, doing Model.Orders.Count() correctly picks up that 1 order exists. However PartialForEach(x => x.Orders) does not, as nothing is rendered for the order. If I set a breakpoint on the Order constructor, I see that it initially gets called by the Index method on CustomersController, but then it gets called by FubuMVC.Core.Models.StandardModelBinder.Bind, so it is getting re-instantiated by FubuMVC and losing the content of the Orders collection.
This isn't quite what I'd expect, I would think that PartialForEach would just pass the domain object directly into the partial. Am I missing the point somewhere? What is the 'correct' way to achieve this kind of result in Fubu?
Update: In case it helps, this is the top few lines of the stacktrace the first time the Order constructor gets hit:
at FubuDemo.Customer..ctor()
at FubuDemo.Controllers.Customers.CustomersController.Index(IndexInputModel inputModel)
at lambda_method(ExecutionScope , CustomersController , IndexInputModel )
at FubuMVC.Core.Behaviors.OneInOneOutActionInvoker`3.performInvoke()
And the second time:
at FubuDemo.Customer..ctor()
at System.RuntimeType.CreateInstanceImpl(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean fillCache)
at System.Activator.CreateInstance(Type type, Boolean nonPublic)
at System.Activator.CreateInstance(Type type)
at FubuMVC.Core.Models.StandardModelBinder.Bind(Type type, IBindingContext context)
at FubuMVC.Core.Runtime.ObjectResolver.BindModel(Type type, IBindingContext context)
at FubuMVC.Core.Runtime.ObjectResolver.BindModel(Type type, IRequestData data)
at FubuMVC.Core.Diagnostics.RecordingObjectResolver.BindModel(Type type, IRequestData data)
at FubuMVC.Core.Runtime.FubuRequest.<>c__DisplayClass2.<.ctor>b__0(Type type)
at FubuMVC.Core.Util.Cache`2.Retrieve(KEY key)
at FubuMVC.Core.Util.Cache`2.get_Item(KEY key)
at FubuMVC.Core.Runtime.FubuRequest.Get[T]()
Jon:
PartialForEach does not new up new models, it passes the models you pass (i.e. x.Orders).
It looks like the problem you're facing is because you did not add ".Using" in your CustomerPartial.ascx
In CustomerPartial.ascx, change
<%= this.PartialForEach(x => x.Orders) %>
to
<%= this.PartialForEach(x => x.Orders).Using<OrderPartial>() %>

Html.EditorFor does not render anything if I use a custom object?

It seems like it's not possible to edit custom object anymore after I upgraded to asp.net mvc 2 rc 2? I use this approach http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-4-custom-object-templates.html with a custom object like this:
My model has just one property but inherits from an abstract base class
public class Page : ContentItem {
[DataType(DataType.MultilineText)]
public virtual string MainIntro { get;set; } // This property render correct
[DisplayFormat(NullDisplayText="(null value)")]
public virtual DetailCollection Tags { get; set; }
}
My controller looks like this
public ActionResult Edit(string pagePath) {
var page = _repository.GetByUrlSegment(pagePath);
return View(page.EditViewName, new DashboardModel(page, RootPages));
}
And my view looks like this
<% using (Html.BeginForm("update","Dashboard", FormMethod.Post, new { name = "editForm" } )) %>
<% { %>
<div>
<%=Html.EditorFor(model => model.CurrentItem) %>
<div class="editor-button">
<input type="submit" value="Save" />
</div>
</div>
<% } %>
Perhaps it would be better to expose this to the view as a space-separated string and exclude the collection from being displayed in the view. Alternatively, you might be able to define a specific template for how you want to display a collection. It's not clear to me how MVC would be able to determine what to display otherwise.
Try something like:
[ShowForDisplay(false)]
[ShowForEdit(false)]
public virtual DetailCollection Tags { get; set; }
public virtual string TagList
{
get
{
if (tags == null) return "(null value)";
// assumes DetailCollection implements IEnumerable<string>
return string.Join( " ", tags.Select( t => t).ToArray() );
}
set
{
tags = new DetailCollection( value.Split( new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries )
.Select( s => s.Trim() ) );
}
}