Hi I have a PersonName class that look like this:
public class PersonName
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleName { get; set; }
}
I have a UserCreateModel that is composed of PersonName among other properties like this:
public class UserCreateModel
{
public PersonName FullName { get; set; }
public string UserName { get; set; }
...........
}
I have a Editor template in /Views/Shared/EditorTemplates/PersonName.cshtml that looks like this (trimmed down):
#model PersonName
First: #Html.EditorFor(m => m.FirstName)
Last: #Html.EditorFor(m => m.LastName)
Middle: #Html.EditorFor(m => m.Middle)
However when I do (short version) :
#model UserCreateModel
#{Html.BeginForm("Create", "User");}
#Html.EditorForModel()
#{Html.EndForm();}
The PersonName does not bind to the editor and does not show up at all. I even tried UIHint, but not sure what I am missing. Also how do I debug this issue?
Please help!
You have an editor template for the PersonName class (~/Views/Shared/EditorTemplates/PersonName.cshtml) but not for the UserCreateModel which is your main model. So you need to either write an editor template for the UserCreateModel class and use EditorForModel or specify the property using EditorFor like this:
#model UserCreateModel
#using(Html.BeginForm("Create", "User"))
{
#Html.EditorFor(x => x.FullName)
}
If it's the exact code you use, i think you should change Model in lambda with m
#model PersonName
First: #Html.EditorFor(m => m.FirstName)
Last: #Html.EditorFor(m => m.LastName)
Middle: #Html.EditorFor(m => m.Middle)
and not Model.Firstname, etc
you should check out this page. I think it is what you want. It is basically saying that you will have to create a custom template for the model. It is very thorough.
http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-4-custom-object-templates.html
hope this helps you
Related
This is baffling. In one of my MVC models, Project, I have two properties which both save the IDs for other models, like so.
public class Project
{
...
public int PlatformID { get; set; }
public int DepartmentID { get; set; }
...
}
I use a custom editor in which I edit these fields with a kendo dropdown, using their respective names and IDs as text and value fields.
<div class="col-md-11 details-item">
#Html.LabelFor(m => m.PlatformID, new { #class = "col-md-4 details-label" })
#(Html.Kendo().DropDownListFor(m => m.PlatformID)
.Name("PlatformID")
.DataValueField("ID")
.DataTextField("PlatformName")
.BindTo((System.Collections.IEnumerable)ViewData["Platforms"])
.HtmlAttributes(new { #class = "col-md-7 details-editor" })
)
</div>
<div class="col-md-11 details-item">
#Html.LabelFor(m => m.DepartmentID, new { #class = "col-md-4 details-label" })
#(Html.Kendo().DropDownListFor(m => m.DepartmentID)
.Name("DepartmentID")
.DataValueField("ID")
.DataTextField("Name")
.BindTo((System.Collections.IEnumerable)ViewData["Departments"])
.HtmlAttributes(new { #class = "col-md-7 details-editor" })
)
</div>
What's crazy is that PlatformID saves perfectly, but DepartmentID does not even get sent to the controller. If DepartmentID is the only thing I change while editing, it does not even register a change has been made. Otherwise, it gets sent as default 0.
The Department class is insanely simple.
public class Department
{
[Key]
public int ID { get; set; }
[DisplayName("Department Name")]
public string Name { get; set; }
}
PlatformID is essentially identical. Maybe I'm missing something stupid, or maybe there is evil afoot. Thanks for any help you can provide!
Figured out the answer - turns out having a DropDown in an editor template does not work if the item is nullable! So I just changed public int? DepartmentIDto public int DepartmentIDin my Project model, and life was good again.
I inherited a mvc app. This app uses entity framework with database first. It was made with no viewmodels and viewbags everywhere for the dropdowns and error messages. Now I am tasked with making many changes to it because you can not validate the related properties that are not in the main class among other things.
I am trying to create a viewmodel so I can display only necessary data, validate it and not be linked directly to the model. So far I get null for all my fields on a form using the viewmodel I created. I have tried to use automapper but get a mapping error: "Missing type map configuration or unsupported mapping"
Here is part of the controller:
public ActionResult ChangeOwner(int id = 0)
{
var combine = new combineValidationAssetViewModel();
Mapper.CreateMap<ToolingAppEntities1, combineValidationAssetViewModel>();
Mapper.CreateMap<combineValidationAssetViewModel, ToolingAppEntities1>();
Asset asset = db.Assets.Find(id);
Mapper.Map(combine, asset, typeof(combineValidationAssetViewModel), typeof(Asset));
.....
return View(combine);
}
Here is part of the view model:
public class combineValidationAssetViewModel
{
public Asset Assets { get; set; }
public Transaction Transactions { get; set; }
public LocationType LocationTypes { get; set; }
public ToolType ToolTypes { get; set; }
public OwnerType OwnerTypes { get; set; }
public int AssetId { get; set; }
public int fkToolTypeId { get; set; }
[Required]
[Display(Name = "Owner")]
public int fkOwnerId { get; set; }
[Required]
[Display(Name = "Location")]
public int fkLocationId { get; set; }
public int LocationTypeId { get; set; }
public int OwnerTypeId { get; set; }
Here is part of the view:
#model ToolApp.ViewModels.combineValidationAssetViewModel
.....
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Asset</legend>
#Html.HiddenFor(model => model.AssetId)
#Html.HiddenFor(model => model.CreatedByUser)
#Html.HiddenFor(model => model.CreateDate)
#Html.HiddenFor(model => model.SerialNumber)
#Html.HiddenFor(model => model.LocationTypeId)
<div class="editor-label">
#Html.LabelFor(model =>model.SerialNumber)
</div>
<div class="editor-field">
#Html.DisplayFor(model => model.SerialNumber)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.fkToolTypeId, "Tool Name")
</div>
<div class="editor-field">
#Html.DisplayFor(model => model.Description)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.fkOwnerId, "New Owner")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.fkOwnerId, new SelectList(ViewBag.fkOwnerId, "Value", "Text"), new{style="width:320px;height:25px;"})
#Html.ValidationMessageFor(model => model.fkOwnerId),
The form displays but it is null (no values in any of the fields displayed. I would like to map it manually so I understand it. Have tried the automapper but it's not working yet. I have tried some ideas from here and other websites but same result. I don't completely understand the linq to ef yet so my problem may lie there also.
This main controller has 10 different action results on it and is filled with data calls and viewbags. I'm looking for advice on the direction I should go. I need to get the thing working but also want to make changes to it that will move it in the direction of a viable mvc app. Main issue at the moment is how to connect the viewmodel with the dbcontext. I found the context at the top of the controller like this:
{ private ToolingAppEntities1 db = new ToolingAppEntities1();
followed by many includes...
any suggestions would be appreciated
You map into the wrong direction:
Mapper.Map(combine, asset,
typeof(combineValidationAssetViewModel), typeof(Asset));
This maps the empty combine object to asset. You should reverse it, and use a strong-typed (generic) overload:
var combine = Mapper.Map<combineValidationAssetViewModel>(asset);
I'm stuck and after looking this up for hours, I think I need more eyeballs.
The situation is the following:
It's an Asp.Net MVC3 with Entity Framework 4 project. And I have two classes. One ConfigurationFile and another one Action. There is a one-to-many relationship between the two. Here is a simplified view on the code:
public class ConfigurationFile
{
[Key, Required]
[Column(TypeName = "uniqueidentifier")]
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
[Column(TypeName = "uniqueidentifier")]
[Required]
public Guid ActionId { get; set; }
public virtual Models.Action Action { get; set; }
}
public class Action
{
[Key, Required]
[Column(TypeName = "uniqueidentifier")]
public Guid Id { get; set; }
[Required]
public string ActionValue { get; set; }
}
Then I want to create a new ConfigurationFile, and are my two controller methods (and at this point, this is 95% Visual Studio 10 generated code):
// db is my context class.
//
// GET: /Configuration/Create
public ActionResult Create()
{
ViewBag.ActionId = new SelectList(db.Actions, "Id", "ActionValue");
return View();
}
//
// POST: /Configuration/Create
[HttpPost]
public ActionResult Create(Models.ConfigurationFile configurationfile)
{
if (ModelState.IsValid)
{
configurationfile.Id = Guid.NewGuid();
db.ConfigurationFiles.Add(configurationfile);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ActionId = new SelectList(db.Actions, "Id", "ActionValue", configurationfile.ActionId);
return View(configurationfile);
}
And here is a snippet of my Create view:
#model MyProject.Areas.ConfigurationFile.Models.ConfigurationFile
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Configuration File</legend>
<div class="editor-label">
#Html.LabelFor(model => model.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.ActionId, "Action")
</div>
<div class="editor-field">
#Html.DropDownList("ActionId", String.Empty)
#Html.ValidationMessageFor(model => model.ActionId)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
When I open the Create page, I can clearly see that my dropdown for the Action class is fine (correct value -- the Action.Id -- and text -- Action.ActionValue -- ) but when I submit the form, I have the following error: "The parameter conversion from type 'System.String' to type 'MyProject.Models.Action' failed because no type converter can convert between these types."
Help please !!
Right now MVC has no way of connecting your dropdownlist from your view to the ActionId of your ConfigurationFile object.
I would try replacing this line:
#Html.DropDownList("ActionId", String.Empty)
for this
#Html.DropDownListFor(model => model.ActionId, ViewBag.ActionId)
Other than that, I can't think of what else you might have done wrong.
I hope that helps!
This is how I did to circumvent the problem. I just changed my controller this way:
Models.Action act = db.Actions.Find(configurationfile.ActionId);
ModelState.Clear();
configurationfile.Action = act;
TryValidateModel(configurationfile);
And after that, the validation was Ok. A bit hacky (and another possible hit on the DB), but at least, I can keep going.
Suppose, I have models:
public class Person
{
public sting Name {get;set;}
public List<Book> Books {get;set;}
}
public class Book
{
public sting NameBook {get;set;}
}
How represent view for Edit method based on Person model (MVC 3)?
You may try something along the lines of:
#model Person
#using (Html.BeginForm())
{
<div>
#Html.LabelFor(x => x.Name)
#Html.EditorFor(x => x.Name)
</div>
#Html.EditorFor(x => x.Book)
<button type="submit">Edit</button>
}
and then you will define an editor template for the Book type (~/Views/Shared/EditorTemplates/Book.cshtml) which will be rendered for each element of the Book property collection (which by the way you would have named Books in order to follow standard conventions) on your view model:
#model Book
<div>
#Html.LabelFor(x => x.NameBook)
#Html.EditorFor(x => x.NameBook)
</div>
As far as your controller actions are concerned, it's pretty standard stuff:
public ActionResult Edit(int id)
{
var person = _personRepository.Get(id);
return View(model);
}
[HttpPost]
public ActionResult Edit(Person person)
{
if (!ModelState.IsValid)
{
return View(person);
}
_personRepository.Update(person);
return RedirectToAction("Success");
}
I have the following model :
public class ContratoDetailsViewModel
{
[StringLength(50)]
[RegularExpression("^[a-z0-9_\\+-]+(\\.[a-z0-9_\\+-]+)*#[a-z0-9-]+(\\.[a-z0-9-]+)*\\.([a-z]{2,4})$")]
[DisplayName("E-Mail Adm.")]
public string emailAdm { get; set; }
}
public class ContratoDetailContainerViewModel
{
public ContratoDetailsViewModel contrato { get; set; }
public IList<ContratoModels.CCasinoViewModel> ccasinos { get; set; }
}
public class CCasinoViewModel
{
public short codigo { get; set; }
public List<SelectListItem> listCasinos { get; set; }
}
the following view :
#model ContratoModels.ContratoDetailContainerViewModel
#{
...
}
#using (Html.BeginForm(new { currentaction = ViewBag.mode }))
{
...
#Html.EditorFor(m => m.contrato.emailAdm, state1)<br />
#Html.EditorFor(m => m.ccasinos,"test")
<input type="submit" value="Save" />
}
in the folder "EditorTemplates" i have a template called "test.cshtml" :
#model List<ContratoModels.CCasinoViewModel>
#for (int i = 0; i < Model.Count(); i++)
{
#Html.DropDownListFor(m => m[i].codigo,Model[i].listCasinos)
}
My Controller post action is like this :
[HttpPost]
public ActionResult Details(ContratoModels.ContratoDetailContainerViewModel model, FormCollection form)
{
var contrato = model.contrato;
var casinos = model.ccasinos;
}
Before send the view ccasinos,codigo and listCasinos are initialised
when i am in debug mode i see the value of them... the form display work like a charm.
BUT ... when i submit the form the model.ccasinos is always null !! why ?
thank you very much for your reply.
note : I use a EditorFor with the child of my main model but if there is a better solution
for display and submit with MCV 3 I am interested ...
Try replacing:
#Html.EditorFor(m => m.ccasinos, "test")
with this:
#Html.EditorFor(m => m.ccasinos)
and then rename your test.cshtml template to CCasinoViewModel.cshtml and replace its contents with this:
#model CCasinoViewModel
#Html.DropDownListFor(x => x.codigo, Model.listCasinos)
Because the editor template is now named the same way as the type of the list, ASP.NET MVC will automatically render it for each element of this list so that you don't have to write loops.
Also you can safely remove the FormCollection argument from your action. It's completely useless when you are working with view models:
[HttpPost]
public ActionResult Details(ContratoModels.ContratoDetailContainerViewModel model)
{
var contrato = model.contrato;
var casinos = model.ccasinos;
...
}