I have a ViewModel class composed of several nested classes:
public class UserAccountViewModel : UserProfileViewModel
{
public UserAccountEmailViewModel UserAccountEmail { get; set; }
public UserAccountLocationViewModel UserAccountLocation { get; set; }
public UserAccountPasswordViewModel UserAccountPassword { get; set; }
}
The HTML rendered from this (pay attention to model.UserAccountEmail.Email):
<div class="editor-label">
#Html.LabelFor(model => model.UserAccountEmail.Email)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserAccountEmail.Email)
#Html.ValidationMessageFor(model => model.UserAccountEmail.Email)
</div>
Is this:
name="UserAccountEmail.Email"
I would prefer the name to simply be Email
Changing ViewData.TemplateInfo.HtmlFieldPrefix didn't help. Overloading the htmlFieldName in #Html.EditorFor isn't going to work because I still want the label and validation message to match the rendered HTML element (no overload for htmlFieldName in these functions).
I'd prefer to not create partials for this.
Edit:
Meh...using partials actually isn't too bad. It actually makes quite a lot of sense.
See my answer here about this design, it might make sense for this particular strategy
Many models to view with many partial views
Related
I have a View that loads a few partial views at load time depending on a database. It's working great so far with one exception: because this is a database first situation, I'm using a meta class to add the appropriate markup to the model fields. It worked perfectly fine when I had all the stuff on a single page but since I broke it out into partials, it refuses to recognize the meta class anymore.
There's a lot of unrelated code everywhere so I've broken out the important stuff (some names have been changed to protect the innocent)...
In the view to load said partials:
foreach (var p in MyProject.Controllers.UtilityController.GetPViews())
{
#Html.Partial("../Partial/" + p.ViewName, new ViewDataDictionary { { "column", "column" + p.ColumnSize } })
}
The main part of the meta class:
namespace MyProject.Models
{
public class MyProjectData_Meta
{
[MetadataType(typeof(MyProjectData_Meta))]
public partial class MyProjectData
{
public class MyProjectData_Meta
{
//Gets and sets and stuff. For example:
[Required(ErrorMessage = "Please enter a first name.")]
[Display(Name = "First Name")]
[UIHint("FloatLabel")]
public string MemberFirstName { get; set; }
}
}
}
}
And finally the code that pulls the list from said database...
public static List<ModuleList> GetPViews()
{
MyProjectEntities pv = new MyProjectEntities();
List<ModuleList> lvl = pv.ModuleLists.OrderBy(s => s.PageOrder).ToList();
return lvl;
}
So is there some glaring detail I'm missing or more information you need from me in order to tell me where I'm being stupid?
EDIT: Doh! Forgot to put in my actual view code:
#model MyProject.Models.MyProjectData
<div class="#ViewBag.column">
<fieldset id="fsBasic">
<legend>Basic Information</legend>
<div class="editor-label">
#Html.LabelFor(model => model.MemberFirstName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.MemberFirstName)
#Html.ValidationMessageFor(model => model.MemberFirstName)
</div>
</div>
To answer the other question: I know it's not using the meta class because the LabelFor is showing "MemberFirstName" instead of "First Name" which is what it does actually do when I'm not using a partial class.
Consider following compostion relation between Child & Root classes-
public class Child
{
public string ChildProperty { get; set; }
}
public class Root
{
public Child child { get; set; }
public string RootProperty { get; set; }
}
Creating strongly typed view for Root class, does not include child property.
<fieldset>
<legend>Root</legend>
<div class="editor-label">
#Html.LabelFor(model => model.RootProperty)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.RootProperty)
#Html.ValidationMessageFor(model => model.RootProperty)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
How do I generate scaffolding template to include child properties as well?
Thank you for your help.
Generally you would need to create your own T4 templates that do just that:
https://stackoverflow.com/a/16072432/176877
But, I'm not certain you could actually loop through those child models; I'm fairly certain you would need to take things a step farther and actually generate a Custom Scaffold with your own PowerShell script that loops through those models and calls a new set of templates you create.
http://blog.stevensanderson.com/2011/04/07/mvcscaffolding-creating-custom-scaffolders/
In other words, you have a lot of work ahead of you if you want to do this.
This is driving me crazy, and I have studied the related posts on StackOverflow.
Basically I have 2 tables, Album and Genre with the former having a foreign key to Genre. I have modelled this using Model First in EF5, and generated the context and class files using MS's T4 generator. Thus my Domains Class code is:
{
using System;
using System.Collections.Generic;
public partial class Album
{
public int Id { get; set; }
public string Title { get; set; }
public decimal Price { get; set; }
public string AlbumArtUrl { get; set; }
public int GenreId {get; set;}
public virtual Genre Genre { get; set; }
}
}
You will notice that I have added :
public int GenreId {get; set;}
as per guidance inorder to enable the scaffolding to create the dropdowns in the Views and "SelectList" code in the controller. But it does not! However it does create, in the view:
<div class="editor-label">
#Html.LabelFor(model => model.GenreId)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.GenreId)
#Html.ValidationMessageFor(model => model.GenreId)
</div>
So I am really puzzled. Any ideas would be greatly appreciated.
Thanks,
Ed
P.S. I am running VS2010 SP1 with ASP.NET MVC 3 Tools Update it seems, since I tried to install it and it said that it was already installed.
yee ... aah!!! I have got it working with the MS Scaffold. Now I need to figure out how. I believe the issue was with the fact that I am using Model First and I need to ensure that I add the extra foreign key property into the model and not just into the Domain Class code, it needs to be in the EDMX. However this creates another problem in that 2 fields are now creates in SQL Server, one from the Navigation property and the other from this field. I may need to play with the mapping. I will
Controller code generated:
// GET: /Album/Create
public ActionResult Create()
{
ViewBag.GenreId = new SelectList(db.Genre, "Id", "Name");
return View();
}
Associated View Code:
<div class="editor-label">
#Html.LabelFor(model => model.GenreId, "Genre")
</div>
<div class="editor-field">
#Html.DropDownList("GenreId", String.Empty)
#Html.ValidationMessageFor(model => model.GenreId)
</div>
This is what I wanted. Obviously if anyone wishes to make comment on this then great, but it does work.
Thanks.
I have something like this:
Main view:
#model AuthorViewModel
#using (Html.BeginForm("Action", "Controller", FormMethod.Post, new { id="someId" })) {
#Html.LabelFor(model => model.Name);
#Html.EditorFor(model => model.Name);
#Html.ValidationMessageFor(model => model.Name);
<label> Book </label>
#{Html.RenderPartial("_BookView", new BookViewModel());}
<label>One more book...</label>
#{Html.RenderPartial("_BookView", new BookViewModel());}
}
Partial view:
#model BookViewModel
#Html.LabelFor(model => model.Title);
#Html.EditorFor(model => model.Title);
#Html.ValidationMessageFor(model => model.Title);
AuthorViewModel:
public class AuthorViewModel
{
[Required]
[DataType(DataType.Text)]
public String Name { get; set; }
}
BookViewModel:
public class BookViewModel
{
[Required]
[DataType(DataType.Text)]
public String Title { get; set; }
}
So when it renders - it looks right, but validation is the same for all books. An I need to have a lot of books(say to add them dynamically) for author and each one have to be independent and "validatable".
How can I perform such behaviour?
I would have a collection of BookViewModel in your AuthorViewModel. That way the names and ids will be unique.
You could update your AuthorViewModel to have a List of BookViewModel. In the View, iterate over the list and create the necessary fields for the booktitles.
You're trying to model bind to a list.
Its pretty simple to implement, have a look at Phil Haacks post here.
He uses the old mvc views, but the same idea works fine for razor
I'm working with MVC3, and using Entity Framework 4.0 Entities as my model. So far, everything works great as far as using it as a model (all the crud operations/page generations work out of the box). I'm wondering, though, how do you get the same robust labels and validation information as when you generate a model manually?
Here's an example of what I mean. This is a class generated by the sample MVC3 project:
public class LogOnModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
With the example above, you can specify what gets rendered in a label for the field (Display), and what type of field to use (Password). However, when I try to use the entity framework and push it to the view below, I see the automatically generated labels are just the field names, and not anything I want the user to see/have to read:
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Person</legend>
<div class="editor-label">
#Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.MiddleName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.MiddleName)
#Html.ValidationMessageFor(model => model.MiddleName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.LastName)
#Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Birthdate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Birthdate)
#Html.ValidationMessageFor(model => model.Birthdate)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>}
My question is: How do I add these extra decorations to the entities that are generated using EF4? Is there something besides System.ComponentModel.DataAnnotations that I should be using? I know entities get regenerated and it's probably not a good idea to add this to entities' code directly, but for some reason I can't think of a better approach than manually entering the label text in the view (lame, there's no reason to have to do that, this is MVC!). I want to keep it so that the application is dynamic enough to be able to have the correct display information for my model come through and keep an MVC approach. How do I do it?
I haven't done this for ASP.NET MVC (only for Silverlight) but I believe the same principles would apply. You can create a "metadata buddy class" as below, because the types generated by EF should be partial, thus you can add a bit more to them (like the MetadataTypeAttribute) and then you create this sibling class that holds the metadata.
It's kind of ugly, but should work. It goes something like this (assuming the EF entity is named "Person"):
[MetadataType(typeof(PersonMetadata))]
public partial class Person {
// Note this class has nothing in it. It's just here to add the class-level attribute.
}
public class PersonMetadata {
// Name the field the same as EF named the property - "FirstName" for example.
// Also, the type needs to match. Basically just redeclare it.
// Note that this is a field. I think it can be a property too, but fields definitely should work.
[Required]
[Display(Name = "First Name")]
public string FirstName;
}
Same as above but with all the details, and it works
And Here is the Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
namespace Validate.Models
{
[MetadataType(typeof(PersonMetadata))]
public partial class Person
{
// Note this class has nothing in it. It's just here to add the class-level attribute.
}
public class PersonMetadata
{
// Name the field the same as EF named the property - "FirstName" for example.
// Also, the type needs to match. Basically just redeclare it.
// Note that this is a field. I think it can be a property too, but fields definitely should work.
[Required]
[Display(Name = "Enter Your Name")]
public string FirstName;
}
}
Like Austin Lamb's answer, but instead, nesting the MetaData class within the entity class, thereby reducing the number of classes in your public namespace list, and eliminating the need to have a unique name for each metadata class.
using System.ComponentModel.DataAnnotations;
namespace Validate.Models
{
[MetadataType(typeof(MetaData))]
public partial class Person
{
public class MetaData
{
[Required]
[Display(Name = "Enter Your Name")]
public string FirstName;
//...
}
}
}