MetadataType attribute is not working for details view in MVC3 - asp.net-mvc-3

I am newbie in entity framework and I have created Ado.Net Entity Data model which contains two properties FullName and EmailAddress. what I am trying to do is that in my details view I want to display Full Name instead of FullName. To accomplish this I have written code like this:
namespace ScaffFolding.Models
{
[MetadataType(typeof(EmployeeMetaData))]
public partial class Employee
{
}
public class EmployeeMetaData
{
[Display(Name="Full Name")]
public string FullName { get; set; }
[Display(Name = "Email Address")]
public string EmailAddress { get; set; }
}
}
My edmx file also sharing the same namespace and have same class named as Employee
Now the problem is that in Details view this code is not working means showing FullName instead of Full Name but in Create view it's working as expected.
Here is code for my Detail view:
#model ScaffFolding.Models.Employee
#{
ViewBag.Title = "Details";
}
<h2>Details</h2>
<fieldset>
<legend>Employee</legend>
<div class="display-label">FullName</div>
<div class="display-field">
#Html.DisplayFor(model => model.FullName)
</div>
<div class="display-label">Gender</div>
<div class="display-field">
#Html.DisplayFor(model => model.Gender)
</div>
<div class="display-label">Age</div>
<div class="display-field">
#Html.DisplayFor(model => model.Age)
</div>
<div class="display-label">HireDate</div>
<div class="display-field">
#Html.DisplayFor(model => model.HireDate)
</div>
<div class="display-label">EmailAddress</div>
<div class="display-field">
#Html.DisplayFor(model => model.EmailAddress)
</div>
<div class="display-label">Salary</div>
<div class="display-field">
#Html.DisplayFor(model => model.Salary)
</div>
<div class="display-label">PersonalWebSite</div>
<div class="display-field">
#Html.DisplayFor(model => model.PersonalWebSite)
</div>
</fieldset>
<p>
#Html.ActionLink("Edit", "Edit", new { id=Model.Id }) |
#Html.ActionLink("Back to List", "Index")
</p>
Can anyone please tell me what is going wrong here?

DisplayFor is used for picking up templates from DisplayTemplates folder.
more at What is the #Html.DisplayFor syntax for?
Where as, LabelFor picks stuffs from data annotations

Ok I got the answer what is going wrong here. The problem is in the code generated by VS:
Visual Studio is generating the Details view using div tag as shown below
<div class="display-label">FullName</div>
Where as LabelFor is being used by Vistual Studio for Create view:
#Html.LabelFor(model => model.FullName)

Related

Page to create object and sub object

I'm sure there have been tons of people asking this type of question but I can't quite figure out how to word it.
I will try to explain. I am working to model an ethernet network where devices have ip addresses. I've setup my entity framework models so that the ip and subnet are stored in a separate table to ensure uniqueness across the system.
I'd like the user to be able to create a device and its associated IP at the same time if the IP they want is not already in a dropdown list.
I setip a partial of the IP Address page RenderPartial on the device page and I get this error:
Here is the question, How do I fix this error:
The model item passed into the dictionary is of type PcnWeb.Models.Device, but this dictionary requires a model item of type PcnWeb.Models.IPAddress.
Here are my models:
IP Address Model:
namespace PcnWeb.Models
{
public class IPAddress
{
public virtual ICollection<Device> Devices { get; set; }
[Key]
public int ipAddressRecId { get; set; }
public Nullable<int> ipOctet1 { get; set; }
public Nullable<int> ipOctet2 { get; set; }
public Nullable<int> ipOctet3 { get; set; }
public Nullable<int> ipOctet4 { get; set; }
public Nullable<int> smOctet1 { get; set; }
public Nullable<int> smOctet2 { get; set; }
public Nullable<int> smOctet3 { get; set; }
public Nullable<int> smOctet4 { get; set; }
}
}
And the Device Model:
namespace PcnWeb.Models
{
public class Device
{
[Key]
public int deviceRecId { get; set; }
public int ipAddressRecId { get; set; }
[Required, StringLength(64)]
[Unique]
public string Name { get; set; }
[StringLength(256)]
public string Comment { get; set; }
public virtual IPAddress IPAddress { get; set; }
}
}
I would have thought that it would be pretty easy to have the associated device creation page with an inline ipaddress creation page.
Here's the Device page:
#model PcnWeb.Models.Device
#{
ViewBag.Title = "Create a Device";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Device</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ipAddressRecId, "IPAddress")
</div>
<div class="editor-field">
#Html.DropDownList("ipAddressRecId", String.Empty)
#Html.ValidationMessageFor(model => model.ipAddressRecId)
</div>
#{
Html.RenderPartial("~/Views/IP_Address/_Create.cshtml");
}
<div class="editor-label">
#Html.LabelFor(model => model.Name, "Name")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Comment, "Comment")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Comment)
#Html.ValidationMessageFor(model => model.Comment)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Here is the IP Address Partial:
EDIT: Sorry I forgot to include this
#model PcnWeb.Models.IPAddress
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>IPAddress</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ipOctet1, "ipOctet1")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ipOctet1)
#Html.ValidationMessageFor(model => model.ipOctet1)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ipOctet2, "ipOctet2")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ipOctet2)
#Html.ValidationMessageFor(model => model.ipOctet2)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ipOctet3, "ipOctet3")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ipOctet3)
#Html.ValidationMessageFor(model => model.ipOctet3)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ipOctet4, "ipOctet4")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ipOctet4)
#Html.ValidationMessageFor(model => model.ipOctet4)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.smOctet1, "smOctet1")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.smOctet1)
#Html.ValidationMessageFor(model => model.smOctet1)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.smOctet2, "smOctet2")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.smOctet2)
#Html.ValidationMessageFor(model => model.smOctet2)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.smOctet3, "smOctet3")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.smOctet3)
#Html.ValidationMessageFor(model => model.smOctet3)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.smOctet4, "smOctet4")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.smOctet4)
#Html.ValidationMessageFor(model => model.smOctet4)
</div>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
So from all this, it looks great to me, the validation works client side. I'll have to write some javascript to hide the IP address partial unless they select new from the dropdown list.
Here is the question again, How do I fix this error:
The model item passed into the dictionary is of type PcnWeb.Models.Device, but this dictionary requires a model item of type PcnWeb.Models.IPAddress.
This errors means that there is mismatch between model type in your partial view and type of model passed to this view. But as i can see you trying to render the partial view without model passing.
Your code example worked for me.
So, to solve this problem you can do the next steps.
Ensure that the view path is right;).
Try so 'send' model to your partial view explicity like
main view
#model PcnWeb.Models.Device
//some code here
#Html.Partial("path_to_view", Model)
partial view.
#model PcnWeb.Models.Device
#Html.DropdownListFor(x=>x.ipAddressRecId, YourDlistSource) //or anything you need
this works for me.
Alternatively if you need edit submodel in partial view you can do this
#model PcnWeb.Models.Device
//some code here
#Html.Partial("path_to_view", Model.IPAddress)//pass the submodel to partial view
then your partial view must be with another type.
#model PcnWeb.Models.IPAddress
//some code here
Answer on comment. To resolve object reference exception try initialize your submodel in constructor.
public class Device
{
///properties
public Device()
{
IPAddress = new IPAddress();
}
}

MVC3 Embed Models within Models

I am encountering many problems (validation, serialisation) when using models that embed other models and just wanted to know whether anybody has received issue with this kind of thing before. For example I have an enquiry form which has the following model attached:
Model
namespace
{
public class EnquiryStudent
{
public enquiry enquiry { get; set; }
public student student { get; set; }
}
}
The enquiry and student models (embedded inside enquiry student) are auto generated from the database (I am using database first approach).
Now for example a problem I get here is that I use a view to display a form based on the above model:
View
#using (#Html.BeginForm())
{
<div>
<fieldset>
<fieldset>
<legend>Enquiry</legend>
<div class="editor-label">
#Html.LabelFor(m => m.student.firstname)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.student.firstname)
#Html.ValidationMessageFor(m => m.student.firstname)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.student.surname)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.student.surname)
#Html.ValidationMessageFor(m => m.student.surname)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.student.email)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.student.email)
#Html.ValidationMessageFor(m => m.student.email)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.enquiry.subject)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.enquiry.subject)
#Html.ValidationMessageFor(m => m.enquiry.subject)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.enquiry.enquiry_text)
</div>
<div class="editor-field">
#Html.TextAreaFor(m => m.enquiry.enquiry_text)
#Html.ValidationMessageFor(m => m.enquiry.enquiry_text)
</div>
</fieldset>
<p>
<input type="submit" class="rounded-button-gray-shades" value="Send" />
</p>
</fieldset>
</div>
}
and the client side validation works great for everything except the enquiry text (an attribute of the embedded enquiry within EnquiryStudent model, i.e. it doesn't validate this client side only server side??
Now even stranger if I either change the enquiry_text to be a TextBoxFor instead of TextAreaFor it works great.
Or if I make a text area for another entity by adding a dummy field to the EnquiryStudent model (not embedded in another model) it works fine.
Can somebody please shed some light as like I said this is not the only quirk that I have been having with this kind of model relationship.
Thanks,

How to use #model IEnumerable<> and #model <> in the same page (List / Create) MVC3

Lets say i have a class 'Location', and I'm using MVC3 to create a scaffolding list(index.cshtml). The index html page uses #model IEnumerable. If i want to add a new location to the list, i press create, and i end up with a new html page using #model Project.Models.Location.
Instead of going to a new page, i would like to have the create page as a popup using modal in boostrap(twitter). I've tried using partialview without any luck. Whatever i do i get the dictionary error message.
The model item passed into the dictionary is of type 'System.Data.Entity.Infrastructure.DbQuery`1[Project.Models.Location]', but this dictionary requires a model item of type 'Project.Models.Location'.
The modal(bootstrap) itself is working when i'm not fetching the partialview. The partialview itself is exactly the same as the generated create(CRUD).
EDIT: I rewrote the whole thing.
Index:
#model IEnumerable<LoLStats.Models.Location>
#{
ViewBag.Title = "Index";
}
#foreach (var item in Model) {
<ul>
<li>#Html.DisplayFor(modelItem => item.LocationName)</li>
</ul>
}
<a class="btn" data-toggle="modal" href="#myModal" >Create</a>
<div class="modal hide" id="myModal">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Modal header</h3>
</div>
<div class="modal-body">
<p>#Html.Partial("_createLocation")</p>
</div>
<div class="modal-footer">
Close
Save changes
</div>
</div>
_createLocation:
#model LoLStats.Models.Location
#{
ViewBag.Title = "Create"; }
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
Location
<div class="editor-label">
#Html.LabelFor(model => model.LocationName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.LocationName)
#Html.ValidationMessageFor(model => model.LocationName)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset> }
You could make a view model that has two properties:
The IEnumerable for the parent view
A singular object for the modal
Then strongly type both views to the view model and you should be all set.
Edit
ViewModel:
public class MyViewModel()
{
public IEnumerable<LoLStats.Models.Location> Locations {get; set;}
public LoLStats.Models.Location Location {get; set;}
}
Index:
#model myNamespace.MyViewModel
...
#foreach (var item in Model.Locations) {
<ul>
<li>#Html.DisplayFor(modelItem => item.Locations.LocationName)</li>
</ul>
...
_createLocation:
#model myNamespace.MyViewModel
...
<div class="editor-label">
#Html.LabelFor(model => model.Location.LocationName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Location.LocationName)
#Html.ValidationMessageFor(model => model.Location.LocationName)
</div>
...
Hopefully that's enough detail to get you going
try this in the Index view:
... <p>#Html.Partial("_createLocation", Model.FirstOrDefault())</p>
on the other hand, maybe this will make more sence:
... <p>#Html.Partial("_createLocation", new LoLStats.Models.Location())</p>
hope it works for you.
Your pop-up would need to have a model defined as Project.Models.Car (singular item). After successfully saving the single item you need to update your original list view. Which you can do using jQuery by inserting a new item into the DOM or just by refreshing the page by navigating to the action that gave you the list to begin with.

Partialview not returning model for a sortedlist

I am very, very new to MVC, so please bear with my question, I have to work with the following structure.
I have the following models, Facility and Address. Facility contains a SortedList of Address, I have reduced the number of properties for clarity,
public class AppFacility
{
public AppFacility()
{
this.Addresses = new SortedList<string, AppAddress>();
}
public SortedList<string, AppAddress> Addresses { get; set; }
[DisplayNameAttribute("Facility ID")]
public int FacilityID { get; set; }
[Required(ErrorMessage = "Facility Name is a required field")]
[DisplayNameAttribute("Facility Name")]
public string FacilityName { get; set; }
[DisplayNameAttribute("Doing Business As")]
public string Dba { get; set; }
[DisplayNameAttribute("Nbr. Of Employees")]
public int NbrOfEmployees { get; set; }
}
public class AppAddress
{
[DisplayNameAttribute("City")]
public string City { get; set; }
[DisplayNameAttribute("State")]
public string State { get; set; }
[DisplayNameAttribute("Street Name")]
public string StreetName { get; set; }
}
Controller:
[HttpPost]
public ActionResult FacilityCreate(AppFacility objFacility)
{
facilityManager = new Manager.AppFacilityManager();
if (facilityManager.InsertAppFacility(objFacility))
{
return RedirectToAction("FacilityInfo", new { id = objFacility.FacilityID });
}
return View((AppFacility)Session["FacilityObject"]);
}
View:
FacilityCreate
#model Model.CORE.BO.AppFacility
<table width="100%" align="center" border="0" class="SectionTables">
<tr>
<td class="SubtitleHeader">
Facility
</td>
</tr>
<tr>
<td class="SubtitleHeader1">
Enter Facility Information
</td>
</tr>
<tr>
<td align="center">
#Html.Partial(p_CreateEditAppFacility, Model)
</td>
</tr>
Partial view:
p_CreateEditAppFacility:
This contains the p_CreateEditAddress partial view as well.
#model Model.CORE.BO.AppFacility
#using (Html.BeginForm("FacilityCreate", "Form", FormMethod.Post,
new { id = "saveForm", name = "saveForm" }))
{
#Html.ValidationSummary(true)
<div class="main">
<fieldset>
<legend>Operator Information</legend>
<div class="editor-label">
#Html.Label("Facility Name (Business Name of Operator to Appear): ")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FacilityName)
#Html.ValidationMessageFor(model => model.FacilityName)
</div>
<div class="editor-label">
#Html.Label("Owner's Business Name (If different from Business Name of Operator): ")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Dba)
#Html.ValidationMessageFor(model => model.Dba)
</div>
<div class="editor-label">
#Html.Label("No of Employees:")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.NbrOfEmployees)
#Html.ValidationMessageFor(model => model.NbrOfEmployees)
</div>
</fieldset>
<fieldset>
<legend>Address Information</legend>
#{
int i = 0;
foreach (KeyValuePair<string, Model.CORE.BO.AppAddress> addressRow in Model.Addresses)
{
<div class="editor-field">
#Html.Partial(p_CreateEditAddress, addressRow.Value, new ViewDataDictionary(Html.ViewDataContainer.ViewData) { TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = string.Format("objFacility.Addresses[{0}]", i) } })
</div>
i++;
}
}
</fieldset>
<p>
<input id="SaveFacility" name="SaveInfo" type="submit" value="Save Information" />
</p>
</div>
}
PartialView:
p_CreateEditAddress
#model Model.CORE.BO.AppAddress
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.StreetName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.StreetName)
#Html.ValidationMessageFor(model => model.StreetName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.City)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.City)
#Html.ValidationMessageFor(model => model.City)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.State)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.State)
#Html.ValidationMessageFor(model => model.State)
</div>
My question is that in the Controller the objFacility.Addresses does not get the values entered for the model AppAddress, it is always null. The AppFacility gets populated though.
The html behind looks like this for p_CreateEditAddress
<div class="editor-field">
<input class="text-box single-line"
id="objFacility_Addresses_0__StreetName"
name="objFacility.Addresses[0].StreetName" type="text" value="" />
<span class="field-validation-valid"
data-valmsg-for="objFacility.Addresses[0].StreetName" data-valmsg-replace="true"></span>
</div>
Please help.
You just need to change your Partial View call to take the correct prefix. You don't give it the Model name as the default model binder will be creating an instance of your class and looking for fields that match the names of the items in the Request.Form collection. It knows nothing about the variable name you've gave your model, just the properties of your Model's class.
Try this (I put line breaks in for readability):
#Html.Partial(p_CreateEditAddress, addressRow.Value,
new ViewDataDictionary(Html.ViewDataContainer.ViewData) {
TemplateInfo = new System.Web.Mvc.TemplateInfo {
HtmlFieldPrefix = string.Format("Addresses[{0}]", i)
}
})
I guess the generated HTML is not proper for model binding.
It should not be objFacility.Addresses[0].StreetName but Addresses[0].StreetName.

MVC 3 Validation - Only show error messages after lost focus or submit?

I have setup the entities in my MVC 3 app with DataAnnotations (required, stringlength, etc) and the MVC page is showing validation error messages appropriately. But, the error messages are shown as soon as the page loads on a new form before the user has had the chance to enter an invalid value.
I had used JQuery validation a few years ago and was able to only show the error messages after the user lost focus on a field or attempted to submit the form. In fact, I think it was the default behavior.
Is there anyway to do the same in MVC 3 using DataAnnotations?
EDIT: Here is the HTML
<div class="form horizontal floated w50p">
<h3>Billing Information</h3>
#using(Html.BeginForm()){
<div class="item">
<div class="label">
<label>* First Name</label></div>
<div class="value">#Html.TextBoxFor(x => x.Name)</div>
<div class="value">#Html.ValidationMessageFor(x => x.Name)</div>
</div>
<div class="item">
<div class="label">
<label>* Address 1</label></div>
<div class="value">#Html.TextBoxFor(x => x.Street1)</div>
<div class="value">#Html.ValidationMessageFor(x => x.Street1)</div>
</div>
<div class="item">
<div class="label">
<label>Address 2</label></div>
<div class="value">#Html.TextBoxFor(x => x.Street2)</div>
</div>
<div class="item">
<div class="label">
<label>Address 3</label></div>
<div class="value">#Html.TextBoxFor(x => x.Street3)</div>
</div>
<div class="item">
<div class="label">
<label>City</label></div>
<div class="value">#Html.TextBoxFor(x => x.City)</div>
<div class="value">#Html.ValidationMessageFor(x => x.City)</div>
</div>
<div class="item">
<div class="label">
<label>State/Province/Region</label></div>
<div class="value">#Html.TextBoxFor(x => x.StateProv)</div>
<div class="value">#Html.ValidationMessageFor(x => x.StateProv)</div>
</div>
<div class="item">
<div class="label">
<label>Zip / Postal Code</label></div>
<div class="value">#Html.TextBoxFor(x => x.PostalCode)</div>
<div class="value">#Html.ValidationMessageFor(x => x.PostalCode)</div>
</div>
<div class="item">
<div class="label">
<label>* Contact Phone</label></div>
<div class="value">#Html.TextBoxFor(x => x.ContactPhone)</div>
<div class="value">#Html.ValidationMessageFor(x => x.ContactPhone)</div>
</div> <input type="submit" value="Submit" />
}
The default behavior is exactly what you describe (errors should appear only after a field loses focus or form is submitted). So, there must be something wrong with your view or controller. Specifically, it sounds like the validator thinks the user is posting back even on the first view of the form. The first view of the form should be a GET not a POST. If you paste your controller code, that might help us diagnose it better.
You mean like enabling client validation? Sure, that's easy. Just:
create a view model
decorate it
create controller
create a view
include proper jquery scripts
So let's go ahead and follow those steps.
View model:
public class ProductViewModel
{
[Required] // <-- you could use any data annotation attributes you like
public string Name { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new ProductViewModel();
return View(model);
}
[HttpPost]
public ActionResult Index(ProductViewModel model)
{
return View(model);
}
}
View:
#model ProductViewModel
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
#using (Html.BeginForm())
{
#Html.LabelFor(x => x.Name)
#Html.EditorFor(x => x.Name)
#Html.ValidationMessageFor(x => x.Name)
<input type="search" value="OK" />
}
Now leave the field blank and client validation will trigger assuming it is enabled in web.config (which it is by default when you create a new ASP.NET MVC 3 project using the default Visual Studio template):
<appSettings>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
If you want to handle custom validation attributes you could but it might be a little more painful. And once you get yourself confronted to real world applications and realize the weaknesses of declarative validation using attributes (data annotations) I would strongly recommend you checking out FluentValidation.NET.

Resources