Reputation: 33071
In my MVC application I have a View Model that looks similar to this:
public class ComplexViewModel
{
public ComplexDetailsViewModel Details1 { get; set; }
public ComplexDetailsViewModel Details2 { get; set; }
}
public class ComplexDetailsViewModel
{
public int Id { get; set; }
public string DisplayValue1 { get; set; }
public string DisplayValue2 { get; set; }
// ...
}
I was originally doing the following in my view:
@Html.HiddenFor(model => model.Details1.Id)
@Html.HiddenFor(model => model.Details2.Id)
@Html.DisplayFor(model => model.Details1.DisplayValue1)
...
I would POST the full model to the controller:
public ActionResult Post(ComplexViewModel model)
I don't actually need anything from ComplexViewModel except for the Id values, so I decided to create another view model used specifically for POSTing the data:
public class PostViewModel
{
public int Details1Id { get; set; }
public int Details2Id { get; set; }
}
public ActionResult Post(PostViewModel model)
The problem is that now my @HiddenFor(model => model.Details1.Id)
does not map to my POST model so nothing actually gets POSTed.
Is there a way to have the separate structure for my POST model and my GET model while still using the HiddenFor
helper?
Upvotes: 3
Views: 6514
Reputation: 1642
Easy: simply have your view's view-model inherit from your controller post-model. Then your HiddenFor methods with work, you just need to make sure the properties you are expecting to post are in the base class (the post model).
Upvotes: 0
Reputation: 5534
Just write the HTML for the hidden inputs by hand instead of using the Html Helpers.
<input type="hidden" id="Details1Id" value="@Model.Details1.Id"/>
<input type="hidden" id="Details2Id" value="@Model.Details1.Id"/>
Update
I had issues doing something similar. I ended up flattening out the form related properties on my views. Automapper makes it really easy to map from other objects to your view and can flatten out hierarchies. Doing this, your new view might end up looking similar to this.
public class ComplexViewModel
{
public long Details1Id { get; set; }
public string Details1Name { get; set; }
public long Details2Id { get; set; }
public string Details2Name { get; set; }
}
Upvotes: 1
Reputation: 54638
The models need to be more similar. One your "View" model has class properties, and your "Post" model does not. If the names of the class properties, and properties don't match, they don't get bound by the model binder. Try something like:
public class ComplexViewModel
{
public ComplexDetailsViewModel Details1 { get; set; }
public ComplexDetailsViewModel Details2 { get; set; }
}
public class ComplexDetailsViewModel : PostDetailsModel
{
public string DisplayValue1 { get; set; }
public string DisplayValue2 { get; set; }
}
public class PostModel
{
public PostDetailsModel Details1 { get; set; }
public PostDetailsModel Details2 { get; set; }
}
public class PostDetailsModel
{
public int Id { get; set; }
}
The big problem not using the same models between View
and Post
is validation. What happens if you want to change validation (assuming using the built in MVC validation for client side and server side). Now you have to change both models! Oh wait... no you don't...
public interface MyValidation
{
[required]
public int id { get; set; }
}
Then simply add these validation to your classes: (MetadataType tells MVC what to use for validation)
[MetadataType(typeof(MyValidation))]
public class ComplexDetailsViewModel ....
[MetadataType(typeof(MyValidation))]
public class PostDetailsModel ....
This certainly would work, but I would HIGHLY discourage anyone from using two different models between views and postbacks. In this instance, I see absolutely no reason why they should be different.
Upvotes: 1
Reputation: 20004
Just because you don't use all of the data in the POST version doesn't mean you have to make another model. Why not keep it simple?
This is how it should work:
Your post details view should be strongly typed to a specific view model. Then in your controller you have two action results named Post for instance, one is decorated with the [HTTPGET]
attribute, and the action you want to post to is decorated with the [HTTPPOST]
attribute. Additionally, your get method should take a parameter such as post id, and your post method should take the model as parameter.
To enforce server side validation correctly you can decorate your class properties like so:
public class ComplexDetailsViewModel
{
[Required]//Works for just the Id property
public int Id { get; set; }
public string DisplayValue1 { get; set; }
public string DisplayValue2 { get; set; }
// ...
}
Now in your controller you can use this bool: ModelState.IsValid
. Basically if they had JavaScript turned off and the model was posted with no Id
then the model would be invalid.
This pattern is extremely powerful, and makes client side and server side validation quick to implement. And of course client side validation uses jQuery out of the box so we can easily extend the validators. You can even do AJAX validation very quickly. When I build my forms I don't sacrifice anywhere when it comes to validation.. as it takes no time all to do it correctly.
To answer your original question:
A view can only be strongly typed to one model. You can't load the view with one model, and post it with another (As far as I know). I think if you're trying to do that, your problem is in the way you've built your model.
Upvotes: 5