0

I am using nested view models to display views based on user roles.

Model:

public class MainVM {
  //some properties
  public OneVM One {get; set;}
  public TwoVM Two {get; set;}
}

public class OneVM {
   //properties
}

public class TwoVM {
   //properties
}

As written here that only main model is need to be sent controller. I am using Automapper to map properties from received model.

Controller:

public ActionResult EditAction(MainVM model){
  var item = db.Table.Find(model.Id);
  //automapper to map
  AutoMapper.Mapper.Map(model.One, item); //does not work

  db.Entry(item).State = EntityState.Modified;
  db.SaveChanges();
}

Is this the right way to do that? What am I doing wrong here.

Update: This was the view I was using to render nested view models from partial views

View:

@model MainVM

@Html.RenderPartial("_OnePartial", Model.One)

This answer https://stackoverflow.com/a/6292180/342095 defines an Html helper which will generate the partial view with right names.

Community
  • 1
  • 1
SMUsamaShah
  • 7,677
  • 22
  • 88
  • 131
  • `var result = Mapper.Map(item);` – Farhad Jabiyev May 17 '15 at 13:37
  • @FarhadJabiyev `Mapper.Map(item);` but you passed item as a source? While source should be the received `model` through parameter – SMUsamaShah May 17 '15 at 13:44
  • Don't you want to convert `item` to `OneVM`? – Farhad Jabiyev May 17 '15 at 13:45
  • @FarhadJabiyev No. I updated the code. I want to map properties from received model to `item` and will save the recieved changes in db – SMUsamaShah May 17 '15 at 13:50
  • What you mean by **not working?** – Farhad Jabiyev May 17 '15 at 13:52
  • Please specify what exactly is not working to improve your question. Is the problem AutoMapper related or the nested MainVM model not totally filled? – annemartijn May 17 '15 at 14:09
  • @annemartijn Debugging took ages. Nested model `One` is not filled. – SMUsamaShah May 17 '15 at 14:22
  • The whole nested model was `null` earlier. Than I added initializers for them in constructor of `MainVM` as `this.One = OneVM()`. Now every property in the nested model is null. – SMUsamaShah May 17 '15 at 14:25
  • @FarhadJabiyev Nested model is not being filled – SMUsamaShah May 17 '15 at 14:26
  • @LifeH2O The problem probably lies in your HTML. If a model is nested, then the input fields of properties should be like this: `` – annemartijn May 17 '15 at 14:33
  • @annemartijn oh I got it now. I am passing nested model to partial view with `Partial("_partialView", Model.One)` and in there I am using `EditorFor(model=>model.property)`. What is the right construct? – SMUsamaShah May 17 '15 at 14:42
  • http://stackoverflow.com/a/5197087/342095 and http://stackoverflow.com/questions/18460150/mvc4-nested-partial-view-loses-model-data suggest using editor templates. I already have many editor templates. – SMUsamaShah May 17 '15 at 14:44
  • @LifeH2O If you would improve your question, I could remove the downvote. This will help others having the same difficulties solve their problems. – annemartijn May 17 '15 at 14:59

2 Answers2

2

The value of property One will be empty because you are passing an instance of OneVM to the partial (not the main model) so the form controls are not correctly named with the prefix (which need to be name="One.SomeProperty").

You have included a link to a PartialFor() helper (which works) but don't use it. In the main view it needs to be

@Html.PartialFor(m => m.One, "_OnePartial")

Which is the equivalent of

@Html.Partial("_OnePartial", Model.One, 
    new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "One" }})
  • Helper gets called but nothing is rendered. Helper is a generic solution, why should I avoid it? The solution without helper, as you have given, does display the partial view correctly and data is submitted fine. – SMUsamaShah May 19 '15 at 14:40
  • It should also be possible to have a generic way to receive and update only those properties which were used in the view. – SMUsamaShah May 19 '15 at 14:42
  • Sorry, I don't understand your comments. In you question you use `@Html.RenderPartial("_OnePartial", Model.One)` which is invalid and does nothing - it needs to be `@{ Html.RenderPartial("_OnePartial", Model.One) }`. But even if you used it correctly, this does not add the prefix (by design). As I noted in the first part of my answer, you included a link to a custom helper method named `PartialFor()` which does work if you use it correctly, which is in your case `@Html.PartialFor(m => m.One, "_OnePartial")` –  May 20 '15 at 01:51
  • 1
    Also, if you are wanting to generate controls for properties which are complex objects or collections, then you should be using an `EditorTemplate`, not a partial. Just copy the partial into `/Views/Shared/EditorTemplates/OneVM.cshtml` (note the name of the file must match the name of the class) and in the main view use `@Html.EditorFor(m => m.One)` and the generated controls will all be correctly named with the prefix. –  May 20 '15 at 01:55
1

The problem probably lies in your HTML. If a model is nested, then the input fields of properties should be like this:

<input type="text" name="SubModel.PropertyName" />

Using HTML helpers, it would look something like this:

@Html.EditorFor(model => model.SubModel.PropertyName)

The ASP.NET MVC Action cannot know, that you want to fill your submodel if it's not in your HTML.

annemartijn
  • 1,538
  • 1
  • 23
  • 45
  • Helpers in http://stackoverflow.com/questions/1488890/asp-net-mvc-partial-views-input-name-prefixes/6292180#6292180 are not working. – SMUsamaShah May 17 '15 at 15:33
  • If you really want to use partial views, then you could consider passing the parent model. The answer I gave will work in that scenario. If you don't want to pass the parent model, then you'll probably have to use `@Html.Editor("SubModel.PropertyName", Model.PropertyName)`. – annemartijn May 17 '15 at 15:39