Can't bind model to Controller, MVC3 - asp.net-mvc-3

I have 2 Views and 1 EditorTemplate
The first view is the Cart.cshtml view and this view has a Model (CartModel)
Inside of this view there is a call to the second view using:
#Html.Partial("OrderSummary", Model.CartSummaryModel)
Inside of the OrderSummary.cshtml view I have this
#Html.EditorFor(m => m.OrderItems)
And inside the EditorTemplate (called OrderItemModel) I have
#Html.DropDownListFor(m => m.SelectedQuantity, Model.QuantityList)
The problem is when causing a post on the top most view (Cart.cshtml) the model isn't binding in the controller the "CartSummaryModel" is null. When swapping to a FormCollection there are 2 keys which are:
OrderItems[0].SelectedQuantity
OrderItems[1].SelectedQuantity
How do I bind the form collection data to the Controller's action method?

It's because of the partial:
#Html.Partial("OrderSummary", Model.CartSummaryModel)
Here you are loosing the CartSummaryModel prefix that is required in your input field names. So instead of using partials use editor templates.
Go ahead and move this OrderSummary.cshtml to EditorTemplates/OrderSummary.cshtml and then inside your view replace:
#Html.Partial("OrderSummary", Model.CartSummaryModel)
with:
#Html.EditorFor(x => x.CartSummaryModel, "OrderSummary")
and if the type of the CartSummaryModel property is OrderSummary you don't even need to specify the name of the editor template as ASP.NET MVC will find it by convention:
#Html.EditorFor(x => x.CartSummaryModel)
Now you are good to go and you will see the correct keys sent to the server:
CartSummaryModel.OrderItems[0].SelectedQuantity
CartSummaryModel.OrderItems[1].SelectedQuantity

Related

Dynamically Add Rows when using Editor Templates and EditorFor in MVC 3

Expanding on a previous question, I'm using EditorFor to edit a list of objects of type (let's say) Hobby.
Now, I need to implement functionality to add editors for individual Hobby objects, so that the user can add additional hobbies.
I read Steven Anderson's blog about how to do that in MVC 2. However, he's rendering the individual Hobby instances using a loop and RenderPartial, rather than using EditorFor. I understand his approach of making Ajax calls to render another partial view and inserting the view result into the DOM, but don't know how to apply the Ajax add in my case, with EditorFor.
His code:
<% foreach (var item in Model)
Html.RenderPartial("GiftEditorRow", item);
%>
My code is using EditorFor like this:
// Model is a List<Hobby>
#Html.EditorFor(model => model.AllowedSurveys)
How do I add an additional Hobby editor, given that the editor is implemented as an Editor Template rather than as a Partial View?
You could replace your editor template with a partial:
#foreach (var item in Model.AllowedSurveys)
{
Html.RenderPartial("_Hobby", item);
}
and inside the partial (~/Views/controllerName/_Hobby.cshtml):
#model Hobby
<div class="editorRow">
#using(Html.BeginCollectionItem("AllowedSurveys"))
{
#Html.EditorFor(x => x.Name)
...
}
</div>
The Html.BeginCollectionItem custom helper is used to generate names with Guids and allow for reordering and adding new items to the collection using javascript. You can download it from the updated article. The one you were reading was for ASP.NET MVC 1 and is obsolete.

Using model data in a form in a partial view

I have the following code in a partial view:
#model MyOrganization.MyApp.Models.ProductListing
#using (Ajax.BeginForm("TagProduct", new AjaxOptions() { UpdateTargetId = "FormContainer" , OnSuccess = "$.validator.unobtrusive.parse('form');" }))
{
<p>
#Html.LabelFor(m => m.ModelNumber):
#Html.EditorFor(m => m.ModelNumber)
Tag Product with This Model Number
#(Html.ValidationMessageFor(m => m.ModelNumber))
</p>
}
The viewmodel that this partial view gets is instantiated and many of its properties are hydrated by the view that contains this partial view. However, when the submit is called here, the viewmodel that the controller gets has only the ModelNumber property hydrated. All the other properties are null as if a new instance is created by the partial view with only the property that was edited (ModelNumber) getting a value.
I know that the viewmodel instance passed to the partial view has all the other property values, because if I add an #Html.EditorFor(m => m.SerialNumber) I can see the SerialNumber value that the containing view had rendered in the browser's textbox and I also get it back in the controller when the form is submitted. However, I don't want an editor for the SerialNumber property on the form - I just want it back in the controller when it is submitted.
How can I can I get the entire model back to the controller as it was passed in to the partial view with only the changes that the partial view made?
When it posts the form, it DOES create a new copy of the model with whatever values you posted to it. If you need the other values, put #Html.HiddenFor the other elements you need posted.

MVC3 DropDownListFor Model is null on PostBack

I am using a DropDownListFor like this:
#Html.DropDownListFor(model => model.SelectedOrganisationValue, new SelectList(Model.OrganisationList, "Value", "Text"))
And I am also using:
[Required]
As Attribute in the View.
So when I PostBack the View to the Server and the Required Attribute will fail, the View is showed again, but then Model is null. So I will get a NullReferenceException in Model.OrganisationList.
That is the default behaviour. As you know ,MVC does not rely on ViewState, It can not keep the content inside the drop down across Postbacks (generic term). You need to repopulate it again in the ActionMethod.

ModelState validation errors for properties that are not present in my view

Suppose this view :
#Html.HiddenFor(model => model.Batiment.Client.Id)
#Html.LabelFor(model => model.Batiment.Code)</td>
#Html.EditorFor(model => model.Batiment.Code)</td>
<br>
#Html.LabelFor(model => model.Batiment.Nom)</td>
#Html.EditorFor(model => model.Batiment.Nom)</td>
When I submit my form on the controler the ModelState is invalid for the property "Nom" required into my class Client. Is true, the metadata in my class Client is set to required but I dont include this field into my view...! Why Mvc raise this error?
Can I hide a field (like Id) without specify all the required field into my view?
Errors enter model state during binding, therefore you could exclude your property from binding by including the following in your action method signature:
public ActionResult Register([Bind(Exclude="PropertyName")] UserViewModel user)
{
// Your logic here
}
This should exclude PropertyName from binding, therefore error won't enter your model state and your validation should succeed. Just to add, I think that this is more of a hack, rathern then solution. If you need only a part of your view model, than this view model should not be used and you should really consider creating a new view model without this property.
It might look very similar and it might seem like duplicate code, but it isn't. It promotes seperation of concerns and you should see the benefit of doing this in the near future when it comes to extending/modifying your application.
No, the validation is done on the model, not the view, why it is called 'model state'
You need to create another view for this scenario. This is exactly what ViewModels are for.
Then you can use a tool like AutoMapper to easily to copy the properties between this view model to your base model.

MS MVC3 Nested EditorFor and View Templates with same ViewModel

I have a hierarchy of Models I need to render out editors for.
I want to have a nice ViewModel that contains all the info needed for each part of the hierarchy to render itself and have that ViewModel get passed down the chain of templates.
I have strongly typed View to that ViewModel, and I can get to the first level of nesting fine:
MyView.cshtml:
#model MyViewModel
#Html.EditorFor(x => x, "ViewTemplateA", "ViewTemplateA")
Within EditorTemplates/ViewTemplateA.cshtml:
#model MyViewModel
#Html.EditorFor(x => x, "ViewTemplateB", "ViewTemplateB")
---works OK up to here, ViewTemplateA gets rendered--
EditorTemplates/ViewTemplateB.cshtml:
#model MyViewModel
...etc...
-- ViewTemplateB never gets called. If I change it's #model to something else, and pass in a different object to match, such as x => x.SubModel, it gets called.
Any ideas??

Resources