Get DisplayAttribute of Parent Property - asp.net-mvc

I have a View Model that is defined as follows:
public class VariableViewModel
{
public string TemplateName { get; set; }
public bool Enabled { get; set; }
public string Value { get; set; }
}
I am using this model in other View models:
public class CreateViewModel
{
[Display(Name="First Name")]
public VariableViewModel FirstName { get; set; }
[Display(Name="Last Name")]
public VariableViewModel LastName { get; set; }
}
I have an editor template defined for VariableViewModel:
#model VariableViewModel
#Html.HiddenFor(model => model.TemplateName)
#Html.HiddenFor(model => model.Enabled)
#Html.LabelFor(model => model.Value)
#Html.TextBoxFor(model => model.Value)
An editor template for my CreateViewModel:
#model CreateViewModel
#Html.EditorFor(model => model.FirstName)
#Html.EditorFor(model => model.LastName)
Right now my editor template is creating the label as follows:
<label for="FirstName">Value</label>
Is it possible to modify LabelFor in a way that it looks at the DisplayAttribute of the parent property to determine what it should use instead of having it be Value? I want my labels to look like:
<label for="FirstName">First Name</label>
<label for="LastName">Last Name</label>
The problem is that the Display attribute is not on the property I am creating the label for, but on the containing object. Is there a way to accomplish what I want?

In your editor template simply use the following for the label:
#Html.LabelFor(model => model.Value, ViewData.ModelMetadata.DisplayName)
We are passing the parent display name as value for the label.

Related

MVC. Code first field with HiddenInput Attribute has a visible Label generated

Using MVC5 I Have the following defined in the model
[HiddenInput(DisplayValue = false)]
public DateTime Created { get; set; }
I do not want this field to appear on any of the MVC generated views. However I am getting a label generated for this hidden field when scaffolding the views, as shown below.
What is the correct way to use this attribute so that the field and it's label are not output?
[HiddenInput(DisplayValue = false)]
public int Id { get; set; }
will be rendered as
<input id="Id" name="Id" type="hidden" />
when using Html.EditorForModel() or Html.EditorFor(m => m.Id)
Check what is render in your UI, type="hidden" or something else.
You can simply use #Html.HiddenFor inside view as shown :-
Model :-
public DateTime Created { get; set; }
View :-
#Html.HiddenFor(model=>model.Created)
I know this is an old post but I'm experiencing the same thing. My view is set up as
<div class="form-group row">
#Html.LabelFor(model => model.Summary.LeaID, htmlAttributes: new { #class = "col-form-label col-md-3" })
<div class="col-md-9">
#Html.EditorFor(model => model.Summary.LeaID, new { htmlAttributes = new { #class = "form-control"}})
#Html.ValidationMessageFor(model => model.Summary.LeaID, "", new { #class = "text-danger" })
</div>
</div>
And my Model is
[HiddenInput(DisplayValue = false)]
public string LeaID { get; set; }
When the view renders, I get the label (LabelFor) yet the EditorFor is hidden correctly.
If your only problem is that the label is showing, then annotate the model property with a Display attribute:
[HiddenInput(DisplayValue = false)]
[Display(Name ="")]
public string LeaID { get; set; }
If you do that, the label will not show but still you will have white space on the screen for where the LeaID segment was supposed to appear. Alternatively, you can manually change the view as Kartikeya Khosla has suggested.

MVC View ViewModel HttpPost return value is always NULL

I'm passing a ViewModel back from my View to the Controller via a form HttpPost. However, the values returned are always NULL.
ViewModel
public class vmCompanyAddress
{
public StatelyTechAdmin.Models.Company Company { get; set; }
public StatelyTechAdmin.Models.CompanyAddress Address { get; set; }
public SelectList Counties { get; set; }
}
Company Class Model
public class Company
{
[Key]
public virtual long CompanyId { get; set; }
[Required]
[Display(Name = "Company Name")]
public virtual string Name { get; set; }
public virtual DateTime CreatedDate { get; set; }
public virtual IEnumerable<CompanyAddress> CompanyAddresses { get; set; }
}
CompanyAddress Class Model
public class CompanyAddress
{
[Key]
public virtual long CompanyAddressId { get; set; }
[Required]
public virtual long CompanyId { get; set; }
[ForeignKey("CompanyId")]
public virtual Company Company { get; set; }
[Required]
public virtual int CopmanyAddressTypeId { get; set; }
[ForeignKey("CopmanyAddressTypeId")]
public virtual CompanyAddressType CompanyAddressType { get; set; }
[Display(Name = "Address 1")]
public virtual string Address1 { get; set; }
[Display(Name = "Address 2")]
public virtual string Address2 {get; set; }
[Display(Name = "Town")]
public virtual string Town { get; set; }
[Display(Name = "City")]
public virtual string City { get; set; }
[Required]
public virtual long CountyId { get; set; }
[ForeignKey("CountyId")]
[Display(Name = "County")]
public virtual County County { get; set; }
[Required]
[Display(Name = "Postal Code")]
public virtual string PostalCode { get; set; }
public virtual DateTime CreatedDate { get; set; }
}
Controller (get):
// GET: /Company/Create
public ActionResult Create()
{
vmCompanyAddress vm = new vmCompanyAddress();
vm.Counties = new SelectList(db.County, "CountyId", "Name", -1);
//vm.Address = new CompanyAddress();
//vm.Company = new Company();
return View(vm);
}
Controller (post):
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(vmCompanyAddress company)
{
if (ModelState.IsValid)
{
db.Companies.Add(company.Company);
//Amend Address Company & Address Type before save to DB
company.Address.CompanyId = company.Company.CompanyId;
company.Address.CopmanyAddressTypeId = 1;
db.CompanyAddress.Add(company.Address);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(company);
}
View (create)
#model StatelyTechAdmin.ViewModels.vmCompanyAddress
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Company</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Company.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Company.Name)
#Html.ValidationMessageFor(model => model.Company.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Company.CreatedDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Company.CreatedDate)
#Html.ValidationMessageFor(model => model.Company.CreatedDate)
</div>
#* Invoice Address *#
<div class="editor-label">
#Html.LabelFor(model => model.Address.Address1)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Address.Address1)
#Html.ValidationMessageFor(model => model.Address.Address1)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Address.Address2)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Address.Address2)
#Html.ValidationMessageFor(model => model.Address.Address2)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Address.Town)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Address.Town)
#Html.ValidationMessageFor(model => model.Address.Town)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Address.City)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Address.City)
#Html.ValidationMessageFor(model => model.Address.City)
</div>
#*<div class="editor-label">
#Html.LabelFor(model => model.Address.County)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Address.CountyId, Model.Counties)
</div>*#
<div class="editor-label">
#Html.LabelFor(model => model.Address.PostalCode)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Address.PostalCode)
#Html.ValidationMessageFor(model => model.Address.PostalCode)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
Can anyone please offer any advice as to why my return ViewModel values are NULL when all fields are populated?
I've checked in Google Chrome browser using the Network Record feature and all values ARE posted back in JSON format.
Many thanks.
------------ EDIT ---------------
Here's part of what I can see from the Google Chrome Network Monitor
Company.Name:ABC123
Company.CreatedDate:2014/05/13 00:00:00
....
So it is definitely being returned.
I was able to reproduce your issue and was confused because I know that the default MVC Model Binder understands complex types. I stripped away most of the code and just tried to do it with the Company object, which still failed. I then noticed that in vmCompanyAddress that the name of the class was also the name of the property:
public class vmCompanyAddress
{
public StatelyTechAdmin.Models.Company Company { get; set; }
I changed the name of the property to something different from the class name and it started working:
public class vmCompanyAddress
{
public StatelyTechAdmin.Models.Company TheCompany { get; set; }
We had the same problem today. The accepted answer in this question is only a dirty workaround for the actual problem.
ClassName and PropertyName in a form model can be the same, there is no limitation in the model binder. The limitation is the parameter of the action in your controller. You must not name the parameter like a property with complex type in your form model. Cause the binder will try to bind the HTTP POST form value of company to this paramter in your controller. It will not work for you, cause the binder tries to bind the values of a Company Type to CompanyAddress type.
To fix your problem, you simply have to rename the parameter company to companyAddressModel - or anything which is not a property in your model class.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CompanyAddress company)
change to:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CompanyAddress companyAddressModel)
See here for more information about model binding: http://aspnetmvc.readthedocs.org/projects/mvc/en/latest/models/model-binding.html
MVC will try to bind request data to the action parameters by name.
MVC will look for values for each parameter using the parameter name
and the names of its public settable properties. [...] In addition to route values
MVC will bind data from various parts of the request and it does so in
a set order. Below is a list of the data sources in the order that
model binding looks through them:
Form values: These are form values that go in the HTTP request using the POST method.
Route values: The set of route values provided by routing.
Query strings: The query string part of the URI.
A good example from ASP.NET WebAPI documentation, which is using the same technique:
HttpResponseMessage Put(int id, Product item) { ... }
Here the Id property of Product is mapped to the id parameter in the controller. Which will work, cause in the action the same primitive data type is used as in the model class.
Have not tried this myself but had a lot of similar issues a long time ago that I solved with custom ModelBinder:s which I do not recommend.
I guess your data does not look like: { Company: {...}, Address: {...} }?
I think the solution is to have MVC to understand the structure of the data using templates and EditorFor(). See http://lostechies.com/jimmybogard/2011/09/07/building-forms-for-deep-view-model-graphs-in-asp-net-mvc/ for a good example!
Ensure your ViewModel is exposing properties and not just fields.
This works:
public DAL.Models.Account acct {get;set;}
This doesn't:
public DAL.Models.Account acct;

Binding SelectedItem from dropdownlist to model object in MVC 4

I'm very new with MVC, so bear with me, but I can't seem to bind a value from a SelectList to an instance of the selected object during a postback in MVC 4.
Suppose I have to create a Teacher as a member of a School. I have a ViewModel class defined as such:
public class RegisterTeacherModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
[Display(Name = "Email address")]
public string Email { get; set; }
[Required]
[Display(Name = "School")]
public School SelectedSchool { get; set; }
[ScaffoldColumn(false)]
public Guid UserId
{
get;
set;
}
public SelectList PossibleSchools
{
get;
private set;
}
public RegisterTeacherModel(IRepository<School> schoolRepo)
{
PossibleSchools = new SelectList(schoolRepo, "Id", "Name");
}
}
And my View:
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>RegisterTeacherModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Email)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SelectedSchool)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.SelectedSchool, Model.PossibleSchools)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
And finally, my Controller method:
[HttpPost, ActionName("Create")]
public ActionResult CreateTeacher(RegisterTeacherModel teacherModel)
{
if (ModelState.IsValid)
{
try
{
...
}
}
}
But when I receive the RegisterTeacherModel object back in my Create method in the Controller, SelectedSchool is always null. I must be missing something in the way the model binder re-creates the object references on postback. Can anyone point me in the right direction?
I think you touched on it with your second post: Try pointing the initial code to Model.SelectedSchool.<IdProperty>
<div class="editor-field">
#Html.DropDownListFor(model => model.SelectedSchool.**<IdProperty>**, Model.PossibleSchools)
</div>
Well, I found a workaround. I still don't know if I'm missing something, but instead of using a School object in my ViewModel, I replaced it with the SelectedSchoolId as such:
public class RegisterTeacherModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
[Display(Name = "Email address")]
public string Email { get; set; }
[Required]
[Display(Name = "School")]
public int SelectedSchoolId { get; set; }
...
}
And change my View dropdown to use this instead:
<div class="editor-field">
#Html.DropDownListFor(model => model.SelectedSchoolId, Model.PossibleSchools)
</div>
And then in my controller, when creating the real model objects I can simply pull the School object from the School repository and associate it with the real Teacher object.

Edit foreign key table with asp.net mvc and entity framework

I have a entity Kajak:
public class Kajak
{
[HiddenInput(DisplayValue = false)]
[Key]
public int KajakID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<KajakImage> KajakImages { get; set; }
}
Which has a 1 to many relationship to KajakImages:
public class KajakImage
{
[Key]
public int ImageID { get; set; }
public string Name { get; set; }
public string Data { get; set; }
public string MimeType { get; set; }
public int FK_KajakID { get; set; }
[ForeignKey("FK_KajakID")]
public Kajak Kajak { get; set; }
}
I would like to create an edit view, where it is possible to do edit both in the same window.
I can't really wrap my head around it. So far I have come to this:
public ViewResult Edit(int kajakID)
{
Kajak kajak = _kajakRepository.Kajaks.FirstOrDefault(p => p.KajakID == kajakID);
return View(kajak);
}
But I have no idea on how to creater the "editorfor" kajakimages.
#model Timskajakker.Domain.Entities.Kajak
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_AdminLayout.cshtml";
}
<h2>
Redigér kajak</h2>
#using (Html.BeginForm("Edit", "AdminKajak"))
{
#Html.LabelFor(model => model.Name)
#Html.EditorFor(model => model.Name)
foreach (var kajakImage in Model.KajakImages)
{
#Html.EditorFor(model => model.KajakImages.Name) // doesn't work.. what to do here??
}
<input type="submit" value="Gem" />
#Html.ActionLink("Fortryd og vend tilbage", "Index")
}
I am using asp.net mvc 4 and Entity Framework.
I would like to create an edit option for the Kajak properties except id and the name property from all attached kajakimages
I think the best solution is to create a custom EditorTemplate. You have to create a partial view file with the name KajakImage.cshtml (must be the name of the model to be rendered) in a folder called EditorTemplates under Views/YourControllerName (or in a Shared folder):
#model Timskajakker.Domain.Entities.KajakImage
#Html.HiddenFor(model => model.ImageID)
#Html.LabelFor(model => model.Name)
#Html.EditorFor(model => model.Name)
And then just render this template in your Kajak edit view by using:
#using (Html.BeginForm("Edit", "AdminKajak"))
{
#Html.LabelFor(model => model.Name)
#Html.EditorFor(model => model.Name)
#Html.EditorFor(model => model.KajakImages)
<input type="submit" value="Gem" />
#Html.ActionLink("Fortryd og vend tilbage", "Index")
}
You don't need to write a foreach loop because ASP.MVC will render the partial view once per item automatically when you pass in a collection of KajakImages.
#Html.HiddenFor(model => model.ImageID) is here to ensure that you get KajakImage objects instantiated by the model binder on post request which have the correct original key value set together with the Name property. (The remaining properties of the KajakImage collection will be invalid/empty/null because you don't have form fields for them in your view.)

Why is my ViewModel empty on [HttpPost]? .NET MVC 3

I'm trying my hardest to use ViewModels correctly in my web application, but I'm running into various problems. One of which, is if I set a breakpoint just after I post using a Create action, my viewModel hasn't stored any of my form values. I must be doing something wrong, but I've tried a few things. Including the code below, where I name the form items the same as the viewModel fields to see if that helps.
I'm also wondering what exactly properties in your viewmodel should represent. I've seen people use different things in blog posts and whatnot.
If the view is going to render a select list, I'm under the impression the viewmodel should hold an IEnumerable SelectListItem for this as below. Yet I've seen people use IEnumerable Entity instead, to represent the type the select list represents.
Can anybody shed some light on this for me? I scrapped my entire business logic last night so I could start a fresh and try and do it correctly.
My ViewModel:
public class ServerCreateViewModel
{
public int Id { get; set; }
// CompanyName represents a field in the Company model. I did this to see if
// it would help with model binding. Beforehand it was Companies to represent the type. I've done the same for the rest of them, so I wont comment on this again.
public IEnumerable<SelectListItem> CompanyName { get; set; }
// Represents the Game model.
public IEnumerable<SelectListItem> GameTitle { get; set; }
//Represents the Location model, etc...
public IEnumerable<SelectListItem> City { get; set; }
public IEnumerable<SelectListItem> NumberOfPlayers { get; set; }
public IEnumerable<SelectListItem> CurrencyAbbreviation { get; set; }
}
My Controller action:
public ActionResult Create()
{
var viewModel = new ServerCreateViewModel();
viewModel.CompanyName = new SelectList(_dataService.Companies.All(), "Id", "CompanyName");
viewModel.GameTitle = new SelectList(_dataService.Games.All(), "Id", "GameTitle");
viewModel.City = new SelectList(_dataService.Locations.All(), "Id", "City");
viewModel.NumberOfPlayers = new SelectList(_dataService.ServerPlayers.All(), "Id", "NumberOfPlayers");
return View(viewModel);
}
[HttpPost]
public ActionResult Create(FormCollection collection, ServerCreateViewModel viewModel)
{
try
{ // I put a breakpoint in here to check the viewModel values.
// If I dont pass the viewModel into the constructor, it doesnt exist.
// When I do pass it in, its empty.
return Content("Success");
}
catch
{
return Content("Fail");
}
}
My View:
#model GameserverCompare.ViewModels.Server.ServerCreateViewModel
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Server</legend>
#Html.HiddenFor(m => m.Id)
<div class="editor-label">
#Html.LabelFor(model => model.CompanyName)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => Model.CompanyName, Model.CompanyName)
#Html.ValidationMessageFor(model => model.CompanyName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.GameTitle)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => Model.GameTitle, Model.GameTitle)
#Html.ValidationMessageFor(model => model.GameTitle)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.City)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => Model.City, Model.City)
#Html.ValidationMessageFor(model => model.City)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.NumberOfPlayers)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => Model.NumberOfPlayers, Model.NumberOfPlayers)
#Html.ValidationMessageFor(model => model.NumberOfPlayers)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
Since you're using SelectList properties in the form model, you will need to have a different model to represent the selected values in those lists:
public class ServerCreatePostbackModel
{
public int Id { get; set; }
// CompanyName represents a field in the Company model.
public string CompanyName { get; set; }
// Represents the Game model.
public string GameTitle { get; set; }
//Represents the Location model, etc...
public string City { get; set; }
public int NumberOfPlayers { get; set; }
public string CurrencyAbbreviation { get; set; }
}
Have your HttpPost action take one of these as its argument.
Oh, and be sure to use HiddenFor for the Id property, so it gets sent back with the other data.

Resources