Can you please help me with my code?
I have 2 models:
public class Author
{
public int Id { get; set; }
public string AuthorName { get; set; }
public IEnumerable<Book> Books { get; set; }
}
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
public int YearPublish { get; set; }
public int Price { get; set; }
public int? AuthorId { get; set; } //FK
public Author Author { get; set; } //navigation property
}
And trying to make an Edit function.
Controller code:
// GET: Books/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return HttpNotFound();
}
Book bookEdit = dbBooks.Books.Include(a => a.Author).Where(a => a.AuthorId == id).Single();
return View(bookEdit);
}
// POST: Books/Edit/5
[HttpPost]
public ActionResult Edit(Book book)
{
if (ModelState.IsValid)
{
// TODO: Add update logic here
//book.Author = null; //AuthorName cant be edited
dbBooks.Entry(book).State = EntityState.Modified;
dbBooks.SaveChanges();
return RedirectToAction("Index");
}
return View(book);
}
And my View:
<div class="form-horizontal">
<h4>Book</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="form-group">
#Html.LabelFor(model => model.Author.AuthorName, "AuthorName", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Author.AuthorName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Author.AuthorName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Title, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.YearPublish, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.YearPublish, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.YearPublish, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Price, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Price, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Price, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
And when i try just to click Save button (even if i dont do any changes) an error occured:
A referential integrity constraint violation occurred: The property value(s) of 'Author.Id' on one end of a relationship do not match the property value(s) of 'Book.AuthorId' on the other end.
Error is in dbBooks.Entry(book).State = EntityState.Modified;
If I try to add book.Author = null; it is logically than my AuthorNames` start to disappear...
Also I was trying to put something in Html.HiddenFor, but maybe I was doing something wrong, but nothing changed.
Well, you couldn't modify AuthorName in Book's editor view. You need another view for Author to modify it.
book.Author - cannot be null, becouse its navigation property and can only be like readonly mode. So just remove editormodel for author.authorname
Modified books list:
Book bookEdit = dbBooks.Books.ToList().Where(a => a.Id == id).SingleOrDefault();
Look what you need to do, i got it now. You just need selectList of all Author's and in a view name will be auto selected on AuthorId book's field.
Make new one form-group with
<div class="form-group">
#Html.LabelFor(model => model.AuthorId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#(Html.DropDownListFor(p => p.AuthorId, (SelectList)ViewBag.Authors, new { #class = "form-control" }))
#Html.ValidationMessageFor(model => model.AuthorId, "", new { #class = "text-danger" })
</div>
</div>
And in Your Edit action:
// GET: Books/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return HttpNotFound();
}
Book bookEdit = dbBooks.Books.Where(a => a.Id == id).Single();
// UPDATED
var list = dbBooks.Books.Select(x => new { ID = x.Id, AuthorName = x.AuthorName });
ViewBag.Authors = new SelectList(list, "Id", "AuthorName");
// UPDATED
return View(bookEdit);
}
Again many thanks!
But using DropDownList dont allow me to edit Author`s name.. But that solution is rly good.
I need to edit all 4 fields like name, title, year and price.
I try to make something like composite model:
public class BookAuthorCompositeModel
{
public Author author { get; set; }
public Book book { get; set; }
}
Change my get method:
// GET: Books/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return HttpNotFound();
}
var compModel = new BookAuthorCompositeModel();
compModel.book = dbBooks.Books.ToList().Where(a => a.Id == id).SingleOrDefault();
compModel.author = dbBooks.Authors.ToList().Where(x => x.Id == id).SingleOrDefault();
return View(bookEdit);
}
and it displays all I need (i tap "edit" and see information about name, title, year and price).
My view is:
#model BookStorage.Models.BookAuthorCompositeModel
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Book</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.book.Id)
<div class="form-group">
#Html.LabelFor(model => model.author.AuthorName, "AuthorName", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.author.AuthorName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.author.AuthorName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.book.Title, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.book.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.book.Title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.book.YearPublish, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.book.YearPublish, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.book.YearPublish, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.book.Price, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.book.Price, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.book.Price, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
but something wrong is in Post method:
[HttpPost]
public ActionResult Edit(Book book)
{
if (ModelState.IsValid)
{
dbBooks.Entry(BAmodel.book).State = EntityState.Modified;
dbBooks.Entry(BAmodel.author).State = EntityState.Modified;
dbBooks.SaveChanges();
return RedirectToAction("Index");
}
return View(book);
}
dbBooks.SaveChanges(); leads to error..
I think using this kind of ViewModel helps to edit all rows, but it doesnt sade data to DB...
Maybe you could help with this please?
Related
I am fairly new to coding, and am working on a personal MVC project. I am trying to implement a DropDown that uses values submitted by the user. I believe I have almost everything written correctly, but when I go to update an entity, I get the following error:
The ViewData item that has the key 'WrestlerId' is of type 'System.Int32' but must be of type 'IEnumerable'.
Any assistance with this would be appreciated, and I will edit/update to help figure this out.
Edit: The error itself happens in the View on the following line of code:
#Html.DropDownListFor(x => Model.WrestlerId, Model.Wrestlers, htmlAttributes: new { #class = "form-control" })
Model
public class TitleEdit
{
public int TitleId { get; set; }
public string TitleName { get; set; }
[Display (Name = "Favorite")]
public bool IsStarred { get; set; }
[Display(Name ="Date Established")]
public DateTime DateEstablished { get; set; }
[Display(Name = "Current Champion")]
public int? WrestlerId { get; set; }
public string WrestlerName { get; set; }
public IEnumerable<SelectListItem> Wrestlers { get; set; }
}
View
#model Models.TitleCRUD.TitleEdit
#{
ViewBag.Title = "Edit";
}
<h2>Updating Title</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.TitleId)
<div class="form-group">
#Html.LabelFor(model => model.TitleName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TitleName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.TitleName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.IsStarred, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<div class="checkbox">
#Html.EditorFor(model => model.IsStarred)
#Html.ValidationMessageFor(model => model.IsStarred, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.DateEstablished, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.DateEstablished, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.DateEstablished, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.WrestlerId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(x => Model.WrestlerId, Model.Wrestlers, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Wrestlers, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div id="linkColor">
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Controller
//GET: Edit
public ActionResult Edit(int id)
{
var service = CreateTitleService();
var detail = service.GetTitleById(id);
var wrestlerList = new WrestlerRepo();
var model = new TitleEdit
{
TitleId = detail.TitleId,
TitleName = detail.TitleName,
IsStarred = detail.IsStarred,
DateEstablished = detail.DateEstablished,
WrestlerId = detail.WrestlerId
};
model.Wrestlers = wrestlerList.GetWrestlers();
return View(model);
}
//POST: Edit
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, TitleEdit model)
{
if (!ModelState.IsValid)
{
var wrestlerList = new WrestlerRepo();
model.Wrestlers = wrestlerList.GetWrestlers();
return View(model);
}
if (model.TitleId != id)
{
ModelState.AddModelError("", "ID Mismatch");
return View(model);
}
var service = CreateTitleService();
if (service.UpdateTitle(model))
{
TempData["SaveResult"] = "The title has been updated!";
return RedirectToAction("Index");
}
ModelState.AddModelError("", "The title could not be updated.");
return View(model);
}
GetWrestlers()
public IEnumerable<SelectListItem> GetWrestlers()
{
using (var ctx = new ApplicationDbContext())
{
List<SelectListItem> wrestlers = ctx.Wrestlers.AsNoTracking()
.OrderBy(n => n.RingName)
.Select(n =>
new SelectListItem
{
Value = n.WrestlerId.ToString(),
Text = n.RingName
}).ToList();
var wrestlerTip = new SelectListItem()
{
Value = null,
Text = "Select a Wrestler"
};
wrestlers.Insert(0, wrestlerTip);
return new SelectList(wrestlers, "Value", "Text");
}
}
I have create four models, then i have created a viewmodel that contains first four, i have created a view with controller that show this viewmodel;
When i created Actioresult Create (http post), i on't know how pass entire model.
viewModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Avvisi.Models
{
public class PianoCompletoViewModels
{
public Guid Id { get; set; }
public DatiPiano DatiPiano { get; set; }
public DescrizionePiano descrizionePiano { get; set; }
public ImpresaBeneficiaria impresaBeneficiaria { get; set; }
public ResponsabilePiano responsabilePiano { get; set; }
public ProspettoFinanziario prospettoFinanziario { get;set; }
}
}
controller:
public ActionResult Create()
{
string UserID = Convert.ToString(User.Identity.GetUserId());
int AvvisoID = Convert.ToInt32(Session["AvvisoID"]);
ViewBag.FKCompartoID = new SelectList(db.Comparto, "CompartoID",
"Nome");
ViewBag.PianoID = new SelectList(db.DescrizionePiano,
"DescrizioneID", "PresentImpBenef_RelContAziend");
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(PianoCompletoViewController PianoView)
{
if (ModelState.IsValid)
{
return View();
}
return View();
}
I expect that when i click on submit there is a model filled wiht property(other mmodels) For Example:
PianoView.model1.xxx
PianoView.model2.xxx
PianoView.model3.xxx
PianoView.model4.xxx
cshtml view:
#model Avvisi.Models.PianoCompletoViewModels
#{
ViewBag.Title = "Create";
}
<h2>Dati Piano</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Dati Piano</h4>
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="row" style="margin-left:0px;margin-right:0px">
<div class="form-group">
#Html.LabelFor(model => model.DatiPiano.NomePiano, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.DatiPiano.NomePiano, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.DatiPiano.NomePiano, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.descrizionePiano.PresentImpBenef_RelContAziend, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.descrizionePiano.PresentImpBenef_RelContAziend, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.descrizionePiano.PresentImpBenef_RelContAziend, "", new { #class = "text-danger" })
</div>
</div>
<div class="row">
<div class="form-group">
#Html.LabelFor(model => model.impresaBeneficiaria.CompanyName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.impresaBeneficiaria.CompanyName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.impresaBeneficiaria.CompanyName, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.responsabilePiano.Nome, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.responsabilePiano.Nome, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.responsabilePiano.Nome, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" style="width:95%;background: #dedede;" />
</div>
</div>
</div>
}
I don't understand why it no let me to choose right properties(Models).
Can you help me, please???
You have to use html control name like this:
<input name="descrizionePiano.someProperty">
Then surely you have got value for it.
When I run the program I get a System.NullReferenceException on x.Album.Title == "Disintegration" in:
RuleFor(x => x.Contents)
.NotNull()
.When(x => x.Album.Title == "Disintegration");
How can I program this so that Contents is not accepted as Null when Album.Title == "Disintegration?
Model
[Validator(typeof(ReviewValidation))]
public class Review
{
public int ReviewID { get; set; }
[Display(Name = "Album")]
public int AlbumID { get; set; }
public virtual Album Album { get; set; }
public string Contents { get; set; }
[DataType(DataType.EmailAddress)]
public string ReviewerEmail { get; set; }
}
Validation
public class ReviewValidation : AbstractValidator<Review>
{
public ReviewValidation()
{
RuleFor(x => x.Contents)
.NotNull()
.When(x => x.Album.Title == "Disintegration");
}
}
Controller
public class ReviewsController : Controller
{
private StoreContext db = new StoreContext();
public ActionResult Create()
{
ViewBag.AlbumID = new SelectList(db.Albums, "AlbumID", "Title");
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Review review)
{
if (ModelState.IsValid)
{
db.Reviews.Add(review);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.AlbumID = new SelectList(db.Albums, "AlbumID", "Title", review.AlbumID);
return View(review);
}
}
View
#model MVCMusicStore.Models.Review
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Review</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.AlbumID, "AlbumID", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("AlbumID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.AlbumID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Contents, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Contents, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Contents, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ReviewerEmail, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ReviewerEmail, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ReviewerEmail, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Your view needs to pass the Album.Title back to the POST action in order to validate it. At the moment (from the code you posted), the Album property is null since it was never assigned an actual album, then in the view code where you post to the action, you didn't include the title.
Anyway, the exception you are getting is because Album prop is null in your code, and you are trying to validate its title.
What you could do also is to change the validation code, to only validate when album is not null
When(x => x.Album != null, () =>
{
RuleFor(x => x.Contents).NotNull().When(x => x.Album.Title == "Disintegration");
});
I have a model that has a complex type as a property. I have created a custom DisplayEditor for the complex child type, and it is being bound correctly when the page is loaded. When the page is posted after edits have been made, the Dependents type is being set to null. Here is the code for the Employee model that represents the child Dependents property:
[Display(Name = "Dependents")]
[DataType(DataType.MultilineText)]
public List<Dependent> Dependents { get; set; }
Here is the Dependent model:
[Serializable]
public class Dependent : Person
{
public Dependent()
{
Deduction deduction = new Deduction(this) { Amount = Constants.DependentDeductionAmount };
this.Deduction = deduction;
}
[Key]
[HiddenInput(DisplayValue = false)]
public int DependentId { get; set; }
[Required]
[Display(Name = "Dependent Type")]
public DependentType DependentType { get; set; }
[Required]
public override double DeductionAmount => Constants.DependentDeductionAmount;
}
The 2 edit action methods on the employee controller (I've tried TryUpdateModel, doesn't work):
public ViewResult Edit(int employeeId)
{
if (employeeId < 0) throw new ArgumentOutOfRangeException(nameof(employeeId));
Employee employee = _employeeRepository.Employees.FirstOrDefault(e => e.EmployeeId == employeeId);
bool result = TryUpdateModel(employee, new FormValueProvider(ControllerContext));
return View(employee);
}
[HttpPost]
public ActionResult Edit(Employee employee)
{
if (employee == null) throw new ArgumentNullException(nameof(employee));
if (ModelState.IsValid)
{
employee.Changed = true;
employee.Dependents.ForEach(d => d.Changed = true);
_employeeRepository.SaveEmployee(employee);
TempData["message"] = $"{employee} has been saved.";
return RedirectToAction("Index");
}
else {
// there is something wrong with the data values
return View(employee);
}
}
Here is the Edit.cshtml:
#model Paylocity.HR.Domain.Entities.Employee
#{
ViewBag.Title = $"{"Edit"} {Model}";
}
<div class="panel panel-default">
<div class="panel-heading">
<h3>#ViewBag.Title</h3>
</div>
#using (Html.BeginForm("Edit", "Employee"))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr/>
#Html.ValidationSummary(true, "", new {#class = "text-danger"})
<h4>Employee</h4>
<div class="form-group">
#Html.LabelFor(model => model.FirstName, htmlAttributes: new {#class = "control-label col-md-2"})
<div class="col-md-10">
#Html.EditorFor(model => model.FirstName, new {htmlAttributes = new {#class = "form-control"}})
#Html.ValidationMessageFor(model => model.FirstName, "", new {#class = "text-danger"})
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.LastName, htmlAttributes: new {#class = "control-label col-md-2"})
<div class="col-md-10">
#Html.EditorFor(model => model.LastName, new {htmlAttributes = new {#class = "form-control"}})
#Html.ValidationMessageFor(model => model.LastName, "", new {#class = "text-danger"})
</div>
</div>
<hr/>
#Html.EditorFor(model => model.Dependents, "Dependents")
#Html.HiddenFor(model => model.EmployeeId)
</div>
<div class="panel-footer">
<input type="submit" value="Save" class="btn btn-primary"/>
#Html.ActionLink("Cancel and return to List", "Index", null, new {#class = "btn btn-default"})
</div>
}
</div>
Here is the Dependent.cshtml EditorTemplate:
#model IEnumerable<Dependent>
#using Paylocity.HR.Domain.Entities
#foreach (var dep in Model)
{
<h4>Dependent</h4>
<div class="form-group">
#Html.LabelFor(m => dep.FirstName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(m => dep.FirstName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(m => dep.FirstName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => dep.LastName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(m => dep.LastName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => dep.LastName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => dep.DependentType, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EnumDropDownListFor(m => dep.DependentType, new { #class = "form-control" })
#Html.ValidationMessageFor(m => dep.DependentType, "", new { #class = "text-danger" })
</div>
</div>
<hr />
}
The employee object binds correctly and is updateable, it's only the dependents child type that isn't being bound correctly. The HTML is displaying the correct ID's/names for the Dependent form fields (I believe?). Do I need to implement some sort of custom binder code, or am I missing something obvious here?
This is my first question on SO, I hope I provided enough information.
Change the model in the Dependent.cshtml template to #model Dependent (it cant be IEnumerable<T>) and remove the foreach loop which is generating name attributes which have no relationship to your model (and duplicate id attributes which is invalid html)
It also needs be located in the /Views/Shared/EditorTemplates/ or /Views/yourControllerName/EditorTemplates/ folder
#model Dependent
...
#Html.EditorFor(m => m.FirstName, new { htmlAttributes = new { #class = "form-control" } })
...
#Html.EditorFor(m => m.LastName, new { htmlAttributes = new { #class = "form-control" } })
etc. Then in the main view, use
#Html.EditorFor(model => model.Dependents)
The EditorFor() method accepts IEnumerable<T> and will generate the correct html for each item in the collection including the correct name attributes with indexers
I think your problem is the way you generating the list of item.
use index instead of foreach.
hence your Dependent.cshtml EditorTemplate
do something like:
#for(int i = 0; i < Model.Count(); i++)
{
<h4>Dependent</h4>
<div class="form-group">
#Html.LabelFor(m => Model[i].FirstName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(m => Model[i].FirstName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(m => Model[i].FirstName, "", new { #class = "text-danger" })
</div>
</div>
// rest field follow same pattern
}
for more info about binding list, checkout this post
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
I want to make a DropDownList with a numeric rating from 1-5. I have a rating model and I want to apply these dropdown values to WaitTime, Attentive and Outcome.
Can I just set these values in the view and use the model? If so how would i go about doing this?
My Model Class:
public class Ratings
{
//Rating Id (PK)
public int Id { get; set; }
public string UserId { get; set; }
//Medical Practice (FK)
public int MpId { get; set; }
public MP MP { get; set; }
//User ratings (non-key values)
[Required] //Adding Validation Rule
public int WaitTime { get; set; }
[Required] //Adding Validation Rule
public int Attentive { get; set; }
[Required] //Adding Validation Rule
public int Outcome { get; set; }
}
My View:
<div class="form-group">
#Html.LabelFor(model => model.WaitTime, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.WaitTime, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.WaitTime, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Attentive, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Attentive, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Attentive, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Outcome, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Outcome, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Outcome, "", new { #class = "text-danger" })
</div>
</div>
Use this instead of EditorFor for each field:
#Html.DropDownListFor(model => model.Outcome, new SelectList(Enumerable.Range(1, 5)))
Here is the working fiddle - https://dotnetfiddle.net/daB2DI
In short, lets say your model is -
public class SampleViewModel
{
[Required] //Adding Validation Rule
public int WaitTime { get; set; }
[Required] //Adding Validation Rule
public int Attentive { get; set; }
[Required] //Adding Validation Rule
public int Outcome { get; set; }
}
And your controller actions are -
[HttpGet]
public ActionResult Index()
{
return View(new SampleViewModel());
}
[HttpPost]
public JsonResult PostData(SampleViewModel model)
{
return Json(model);
}
Your Get CSHTML should be -
#model HelloWorldMvcApp.SampleViewModel
#{
ViewBag.Title = "GetData";
}
<h2>GetData</h2>
#{
var items = new List<SelectListItem>();
for (int i = 1; i < 6; i++)
{
var selectlistItem = new SelectListItem();
var code = 0;
selectlistItem.Text = (code + i).ToString();
selectlistItem.Value = (code + i).ToString();
items.Add(selectlistItem);
}
}
#using (Html.BeginForm("PostData","Home"))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>SampleViewModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.WaitTime, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.WaitTime, items, "--Select--", new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.WaitTime, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Attentive, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.Attentive, items, "--Select--", new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Attentive, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Outcome, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.Outcome, items, "--Select--", new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Outcome, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
When you run the code, you should see page like below -
And when you select some values and click on create, you should get those values in PostData action.
try to adapt this to your project
in your controller:
ViewBag.ParentID = new SelectList(departmentsQuery, "NewsMID", "Title", selectedDepartment);
return View(model);
in your view put this
#Html.DropDownList("ParentID")