How to pass a nested model value in #Html.Partial - asp.net-mvc-3

I have a model as order inside it I have a another object which I am trying to use in #Html.Partial
Code snippet
public class Order{
public string Id{set;get;}
public Address BillingAdress{set;get;}
public Address ShippingAddress{set;get;}
}
public class Address{
public int Id{set;get;}
public string Address{set;get;}
}
IN VIEW
#model Order
OrderId:
#Html.TextBoxFor(x=>Model.Id)
ShippingAdress:
#Html.Partial("Adress", Model.ShippingAdress)
BillingAddress:
#Html.Partial("Adress", Model.BillingAdress)
this is not working .
but when I am passing Model instead of Model.ShippingAdress and Model.BillingAdress, TryUpdateModel(Order) is working in controller action can any one tell me why??
I have searched in net but not got any concreete solution so please help me?

The reason for that is because the Partial is not respecting the naming convention for the input fields. Use an editor template instead:
#model Order
OrderId:
#Html.TextBoxFor(x => x.Id)
ShippingAdress:
#Html.EditorFor(x => x.ShippingAdress)
BillingAddress:
#Html.EditorFor(x => x.BillingAdress)
Now move your Adress.cshtml to ~/Views/Shared/EditorTemplates/Address.cshtml. The name and location of the template is important. It should be located in ~/Views/Shared/EditorTemplates and named the same way as the model type (Address.cshtml):
#model Address
...

Related

MVC3 Razor Editor/Display templates and generics

There were a few questions regarding mvc templates and generics however there seems to be none related to what I'm looking for. Consider the following models:
namespace MyNamespace
{
public class ModelBase { /* Not important for example */ }
public class MyModel : ModelBase
{
public string Name { get; set; }
}
public class MyViewModel
{
public IEnumerable<ModelBase> Data { get; set; }
}
}
And a controller:
public class HomeController : Controller
{
public ActionResult Index
{
return View(new MyViewModel { Data = new List<MyModel>() })
}
}
A Razor view Views/Home/Index.cshtml would look like:
#model MyNamespace.MyViewModel
#Html.EditorFor(m => m.Data)
Nothing special there. If I want a display or editor template for that I can create a file under Views/Shared/EditorTemplate or under Views/Home/EditorTemplates called MyModel.cshtml and it finds it correctly.
What if I want to do something different for each implementation of ModelBase when showing a list? Mvc view finder will find List'1.cshtml template correctly in any of above paths. However what I need to do is do a template for List`1[MyModel].cshtml
I can't seem to get the correct file naming. What I've tried so far (relative to this example naming):
List`1[MyModel].cshtml
List`1[[MyModel]].cshtml
List`1[MyNamespace.MyModel].cshtml
List`1[[MyNamespace.MyModel]].cshtml
If possible I want to avoid writing a custom view finder. The only alternative I can think of for now if I can't get above stuff to work is to just have List`1.cshtml call a partial with naming deduced from List.
A very late response, useful if someone else bump in this very same question (as I did a few moments ago trying to remember how to do this)
You can use the UIHintAttribute to define the name of the editor
public class MyViewModel
{
[UIHint("MyModel")]
public IEnumerable<ModelBase> Data { get; set; }
}
I haven't checked this code but I would create different Views for each subtype and do something dumb like:
return View(MyModel.GetType().Name, new MyViewModel { Data = new List<MyModel>() })
So that your View matches the name of your type.
You could do this in the main view:
#model MyViewModel
#Html.EditorFor(x => x.Data)
and then have:
~/Views/Shared/EditorTemplates/MyModel.cshtml:
#model MyModel
...
~/Views/Shared/EditorTemplates/MyOtherModel.cshtml (where obviously MyOtherModel derives from ModelBase):
#model MyOtherModel
...
and so on ... ASP.NET MVC will take care of looping through the Data property and pick the correct template based on the runtime type of each element of this collection.

MVC OrderBy EditorFor IEnumerable

I have just registered, and this is my first post, so please bear with me if the question is not the best. I have had a look about and can't find an answer that suits my requirements; this is possibly because it's not possible to achieve what I want.
I have a partial view which pulls through an IEnumerable list of EditorFor fields from a viewmodel:
#model DocumentViewModelContainer
#Html.EditorFor(m => m.Document.Metadata)
The DocumentViewModelContainer has the following code:
public class DocumentViewModelContainer
{
public DocumentViewModel Document
{
get;
set;
}
The DocumentViewModel has the following code:
public class DocumentViewModel
{
public IEnumerable<DocumentMetadataFieldViewModel> Metadata
{
get;
set;
}
}
There's a ton of other objects in both view models that I've left out as being irrelevant in this question. The DocumentMetadataFieldViewModel is made up of several fields of standard types (int, strings etc.)
What I'm trying to achieve is adding an OrderBy to this list pulled back by ordering by an object in the bottom view model, such as follows:
#model DocumentViewModelContainer
#Html.EditorFor(m => m.Document.Metadata.OrderBy(i => i.InstanceFieldOrder))
However this gives the error:
System.InvalidOperationException : Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.
I'm not only very new to MVC, but to C# in general; this project has had me learning the language on the fly, so please play nice :)
Thanks,
Mark
You should do this ordering in your controller action which is responsible to retrieve your view models and pass them to the view.
You could always perform the following horror in your view:
#model DocumentViewModelContainer
#{
Model.Document.Metadata = Document.Metadata.OrderBy(i => i.InstanceFieldOrder).ToList();
}
#Html.EditorFor(m => m.Document.Metadata)
but promise me you won't do that.

IEnumerable property with MVC3 EditorTemplate

Similar to this post IEnumerable model property in an ASP.NET MVC 3 Editor Template, I have
Model
public class Student
{
public int StudentId { get; set; }
public string StudentName{ get; set; }
//FYI..Its virtual because of EF relationship
public virtual ICollection<Class> Classes{ get; set; }
}
public class Class
{
public int ClassId { get; set; }
public string ClassName{ get; set; }
}
View - EditStudent
#model Student
#Html.TextBoxFor(m => m.StudentName)
//I get the error for following..see below
#Html.EditorFor(m => m.Classes);
Student/EditorTemplates/Class
#model Class
<div>
#*checkbox here*#
#Html.LabelFor(x => x.ClassName)
</div>
Controller
public ActionResult EditStudent(int id)
{
ViewBag.Classes = repository.GetClasses();
Student student = repository.GetStudent(id);
return View("EditStudent", student);
}
Error in View on statement #Html.EditorFor(m => m.Classes); is..
The model item passes into the dictionary is of type
'System.Collections.Generic.HashSet`1[Class]', but this dictionary
required a model item of type 'Class'.
Basically, what I am trying to achieve is to display the list of all classes available with a checkbox next to it ( I have not reached to that part of code yet). Then check all classes to a student is enrolled and allow to change the selections.
How do I display the list of checkboxes with the given Model.
Should I bind my EditorTemplate with ViewBag.Classes (How?) or ?
I need to get selected checkbox values in Post ActionMethod as well.
I read some posts those suggest to create CheckBoxListHelper, but it should be possible to do with EditorTemplate as I need to display a simple list.
Please suggest. Thanks.
Okay, I figured it out. Thanks to very precise post here
How to provide an EditorTemplate for IEnumerable<MyModel>?
First, I renamed the EditorTemplate to StudentClass - not sure if this has anything to do with binding or not, but I did.
Second, modified EditorTemplate to bind with IEnumerable
#model IEnumerable<Class>
var checked = "";
#foreach (Class class in ViewBag.Classes)
{
if (Model != null)
{
Class class = Model.FirstOrDefault(c => c.ClassId.Equals(class.ClassId));
if (class != null)
{
checked = "checked=checked";
}
}
<input type="checkbox" name="Classes" value="#class.ClassId" #checked />
#class.ClassName
}
And I call the template with name
#Html.EditorFor(m => m.Classes, "StudentClass");
Now in controller's Post method I can get the array of Classes (name of checkboxes).

How do you call UpdateModel when you use a ViewModel class?

In MVC3, it seems that the default way to show properties of a model in your view is like so:
#Html.DisplayFor(model => model.Title)
This works fine if your model matches your object exactly. But if you define a custom ViewModel, for example like in the NerdDinner tutorial
public class DinnerFormViewModel {
// Properties
public Dinner Dinner { get; private set; }
public SelectList Countries { get; private set; }
// Constructor
public DinnerFormViewModel(Dinner dinner) {
Dinner = dinner;
Countries = new SelectList(PhoneValidator.AllCountries, dinner.Country);
}
}
Then your DisplayFor code would look like:
#Html.DisplayFor(model => model.Dinner.Title)
Which means the names of the form items are Dinner.Title instead of just Title, so if you call UpdateModel in your Action code, MVC won't "see" the properties it needs to see to update your Dinner class.
Is there a standard way of dealing with ViewModels that I'm overlooking for this scenario in MVC3?
Use the 'prefix' parameter for UpdateModel method
UpdateModel(model.Dinner, "Dinner");
and if you need to update a specified properties only - use something like this
UpdateModel(model.Dinner, "Dinner", new []{"Title"});

Two models in one view in ASP MVC 3

I have 2 models:
public class Person
{
public int PersonID { get; set; }
public string PersonName { get; set; }
}
public class Order
{
public int OrderID { get; set; }
public int TotalSum { get; set; }
}
I want edit objects of BOTH classes in SINGLE view, so I need something like:
#model _try2models.Models.Person
#model _try2models.Models.Order
#using(Html.BeginForm())
{
#Html.EditorFor(x => x.PersonID)
#Html.EditorFor(x => x.PersonName)
#Html.EditorFor(x=>x.OrderID)
#Html.EditorFor(x => x.TotalSum)
}
This, of course, don't work: Only one 'model' statement is allowed in a .cshtml file. May be there is some workaround?
Create a parent view model that contains both models.
public class MainPageModel{
public Model1 Model1{get; set;}
public Model2 Model2{get; set;}
}
This way you can add additional models at a later date with very minimum effort.
To use the tuple you need to do the following, in the view change the model to:
#model Tuple<Person,Order>
to use #html methods you need to do the following i.e:
#Html.DisplayNameFor(tuple => tuple.Item1.PersonId)
or
#Html.ActionLink("Edit", "Edit", new { id=Model.Item1.Id }) |
Item1 indicates the first parameter passed to the Tuple method and you can use Item2 to access the second model and so on.
in your controller you need to create a variable of type Tuple and then pass it to the view:
public ActionResult Details(int id = 0)
{
Person person = db.Persons.Find(id);
if (person == null)
{
return HttpNotFound();
}
var tuple = new Tuple<Person, Order>(person,new Order());
return View(tuple);
}
Another example : Multiple models in a view
Another option which doesn't have the need to create a custom Model is to use a Tuple<>.
#model Tuple<Person,Order>
It's not as clean as creating a new class which contains both, as per Andi's answer, but it is viable.
If you are a fan of having very flat models, just to support the view, you should create a model specific to this particular view...
public class EditViewModel
public int PersonID { get; set; }
public string PersonName { get; set; }
public int OrderID { get; set; }
public int TotalSum { get; set; }
}
Many people use AutoMapper to map from their domain objects to their flat views.
The idea of the view model is that it just supports the view - nothing else. You have one per view to ensure that it only contains what is required for that view - not loads of properties that you want for other views.
ok, everyone is making sense and I took all the pieces and put them here to help newbies like myself that need beginning to end explanation.
You make your big class that holds 2 classes, as per #Andrew's answer.
public class teamBoards{
public Boards Boards{get; set;}
public Team Team{get; set;}
}
Then in your controller you fill the 2 models. Sometimes you only need to fill one. Then in the return, you reference the big model and it will take the 2 inside with it to the View.
TeamBoards teamBoards = new TeamBoards();
teamBoards.Boards = (from b in db.Boards
where b.TeamId == id
select b).ToList();
teamBoards.Team = (from t in db.Teams
where t.TeamId == id
select t).FirstOrDefault();
return View(teamBoards);
At the top of the View
#model yourNamespace.Models.teamBoards
Then load your inputs or displays referencing the big Models contents:
#Html.EditorFor(m => Model.Board.yourField)
#Html.ValidationMessageFor(m => Model.Board.yourField, "", new { #class = "text-danger-yellow" })
#Html.EditorFor(m => Model.Team.yourField)
#Html.ValidationMessageFor(m => Model.Team.yourField, "", new { #class = "text-danger-yellow" })
And. . . .back at the ranch, when the Post comes in, reference the Big Class:
public ActionResult ContactNewspaper(teamBoards teamboards)
and make use of what the model(s) returned:
string yourVariable = teamboards.Team.yourField;
Probably have some DataAnnotation Validation stuff in the class and probably put if(ModelState.IsValid) at the top of the save/edit block. . .
In fact there is a way to use two or more models on one view without wrapping them in a class that contains both.
Using Employee as an example model:
#model Employee
Is actually treated like.
#{ var Model = ViewBag.model as Employee; }
So the View(employee) method is setting your model to the ViewBag and then the ViewEngine is casting it.
This means that,
ViewBag.departments = GetListOfDepartments();
return View(employee);
Can be used like,
#model Employee
#{
var DepartmentModel = ViewBag.departments as List<Department>;
}
Essentially, you can use whatever is in your ViewBag as a "Model" because that's how it works anyway. I'm not saying that this is architecturally ideal, but it is possible.
Just create a single view Model with all the needed information in it, normaly what I do is create a model for every view so I can be specific on every view, either that or make a parent model and inherit it. OR make a model which includes both the views.
Personally I would just add them into one model but thats the way I do it:
public class xViewModel
{
public int PersonID { get; set; }
public string PersonName { get; set; }
public int OrderID { get; set; }
public int TotalSum { get; set; }
}
#model project.Models.Home.xViewModel
#using(Html.BeginForm())
{
#Html.EditorFor(x => x.PersonID)
#Html.EditorFor(x => x.PersonName)
#Html.EditorFor(x => x.OrderID)
#Html.EditorFor(x => x.TotalSum)
}
You can use the presentation pattern http://martinfowler.com/eaaDev/PresentationModel.html
This presentation "View" model can contain both Person and Order, this new
class can be the model your view references.
Another way that is never talked about is
Create a view in MSSQL with all the data you want to present. Then use LINQ to SQL or whatever to map it. In your controller return it to the view. Done.
you can't declare two model on one view, try to use Html.Action("Person", "[YourController]") & Html.Action("Order", "[YourController]").
Good luck.
Beside of one view model in asp.net you can also make multiple partial views and assign different model view to every view, for example:
#{
Layout = null;
}
#model Person;
<input type="text" asp-for="PersonID" />
<input type="text" asp-for="PersonName" />
then another partial view Model for order model
#{
Layout = null;
}
#model Order;
<input type="text" asp-for="OrderID" />
<input type="text" asp-for="TotalSum" />
then in your main view load both partial view by
<partial name="PersonPartialView" />
<partial name="OrderPartialView" />
I hope you find it helpfull !!
i use ViewBag For Project and Model for task so in this way i am using two model in single view and in controller i defined viewbag's value or data
List<tblproject> Plist = new List<tblproject>();
Plist = ps.getmanagerproject(c, id);
ViewBag.projectList = Plist.Select(x => new SelectListItem
{
Value = x.ProjectId.ToString(),
Text = x.Title
});
and in view tbltask and projectlist are my two diff models
#{
IEnumerable<SelectListItem> plist = ViewBag.projectList;
}
#model List

Resources