ASP MVC 5 Not getting a collection posted back to the controller - c#

So far I can't figure out what is not working with my ViewModel and controller, since I have followed some examples on StackOverflow but for some reason my model is not filling up. Here is my Get and Post Controller Methods
public ActionResult Registrar(DateTime FechaTarja, long HaciendaId, long[] Pedidos)
{
if(Pedidos != null && Pedidos.Length > 0)
{
List<RegistroPedidoTarjaViewModel> data = new List<RegistroPedidoTarjaViewModel>();
foreach (long Pedido in Pedidos)
{
data.Add(new RegistroPedidoTarjaViewModel {
PedidoEmbarqueId = Pedido
});
}
return View(new RegistroTarjaViewModel {
HaciendaId = HaciendaId,
FechaTarja = FechaTarja,
Pedidos = data
});
}
return RedirectToAction("Index", new { FechaTarja = FechaTarja.ToString("yyyy-MM-dd"), HaciendaId = HaciendaId });
}
[HttpPost]
public ActionResult Registrar(RegistroTarjaViewModel model)
{
if (ModelState.IsValid)
{
}
return View(model);
}
My View Model
public class RegistroTarjaViewModel
{
[Required]
public long HaciendaId { get; set; }
[Required]
public DateTime FechaTarja { get; set; }
public List<RegistroPedidoTarjaViewModel> Pedidos { get; set; }
public long? ContenedorId { get; set; }
}
My Razor View
#model Project.ViewModels.RegistroTarjaViewModel
#{
ViewBag.Title = "Registrar Nueva Tarja";
}
<div class="row">
<div class="col-xs-12 col-md-12">
<h1>#ViewBag.Title</h1>
</div>
</div>
#using (Html.BeginForm())
{
#Html.HiddenFor(model => model.FechaTarja)
#Html.HiddenFor(model => model.HaciendaId)
<div class="row">
<div class="col-xs-12 col-md-12">
</div>
</div>
for (int i = 0; i < Model.Pedidos.Count(); i++)
{
#Html.HiddenFor(model => model.Pedidos[i].PedidoEmbarqueId)
<div class="row">
<div class="col-xs-12 col-md-12">
<div class="form-group">
#Html.LabelFor(model => model.Pedidos[i].Embarcadas, htmlAttributes: new { #class = "control-label" })
#Html.TextBoxFor(model => model.Pedidos[i].Embarcadas, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Pedidos[i].Embarcadas, "", new { #class = "text-danger" })
</div>
</div>
</div>
}
<input type="submit" value="Registrar Tarja" class="btn btn-primary" />
}
I am getting posted back the HaciendaId and FechaTarja properties, but not the Pedidos List, what am I missing to include on the Controller?
EDIT
Here is an example of the HTML output of the textbox
<input class="form-control" data-val="true" data-val-number="El campo Embarcadas debe ser un número." data-val-required="El campo Embarcadas es obligatorio." id="Pedidos_0__Embarcadas" name="Pedidos[0].Embarcadas" type="text" value="0">

Your code is working. Here is how I tested -
View is same as yours.
Controller
public class HomeController : Controller
{
public ActionResult Registrar()
{
List<RegistroPedidoTarjaViewModel> data = new List<RegistroPedidoTarjaViewModel>();
data.Add(new RegistroPedidoTarjaViewModel {PedidoEmbarqueId = 1, Embarcadas = "One"});
data.Add(new RegistroPedidoTarjaViewModel {PedidoEmbarqueId = 2, Embarcadas = "Two"});
data.Add(new RegistroPedidoTarjaViewModel {PedidoEmbarqueId = 3, Embarcadas = "Three"});
data.Add(new RegistroPedidoTarjaViewModel {PedidoEmbarqueId = 4, Embarcadas = "Four"});
return View(new RegistroTarjaViewModel
{
HaciendaId = 1,
FechaTarja = DateTime.Now,
Pedidos = data
});
}
[HttpPost]
public ActionResult Registrar(RegistroTarjaViewModel model)
{
if (ModelState.IsValid)
{
}
return View(model);
}
}
View Models
public class RegistroTarjaViewModel
{
[Required]
public long HaciendaId { get; set; }
[Required]
public DateTime FechaTarja { get; set; }
public List<RegistroPedidoTarjaViewModel> Pedidos { get; set; }
public long? ContenedorId { get; set; }
}
public class RegistroPedidoTarjaViewModel
{
public long PedidoEmbarqueId { get; set; }
public string Embarcadas { get; set; }
}
Posted Result

Related

How can I add Blog post with PostId and CategoryId in PostCategory?

i have a question. Im doing Blog on mvc core. I created 3 entities.Post, Category and PostCategory. When i want to create blog post, I wanna add on PostCategory tables with PostId and CategoryId.I done for EditPost its working but I didnt for CreatePost method. I need help. Let me show my codes.
Its my entities.
public class Post : IEntity
{
public int Id { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public DateTime DateTime { get; set; }
public string ImageUrl { get; set; }
public List<PostCategory> PostCategories { get; set; }
}
public class Category : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public List<PostCategory> PostCategories { get; set; }
}
public class PostCategory:IEntity
{
public int PostId { get; set; }
public Post Post { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
I done settings in BlogContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PostCategory>()
.HasKey(c => new { c.CategoryId, c.PostId });
}
public DbSet<Post> Posts { get; set; }
public DbSet<Category> Categories { get; set; }
I wanna show PostService in EfCorePostDal.(I think i should be fix here)
public void Create(Post entity, int [] categoryIds)
{
using (var context = new BlogContext())
{
var post = context.Posts
.Include(x => x.PostCategories)
.ThenInclude(x => x.Category)
.FirstOrDefault();
if (post != null)
{
post.Title = entity.Title;
post.Text = entity.Text;
post.ImageUrl = entity.ImageUrl;
post.DateTime = entity.DateTime;
post.PostCategories = categoryIds.Select(categoryId => new PostCategory()
{
CategoryId = categoryId,
PostId = entity.Id
}).ToList();
}
context.SaveChanges();
}
}
Its my AdminController.I try to take categoryIds.When i check in debug.I can do it.I created in PostModel in WebUI by the way
public ActionResult CreatePost()
{
ViewBag.Categories = _categoryService.GetAll();
return View(new PostModel() { });
}
[HttpPost]
public async Task<ActionResult> CreatePost(PostModel model, IFormFile file,int[] categoryIds)
{
if (ModelState.IsValid)
{
var entity = new Post
{
Title = model.Title,
Text = model.Text,
DateTime = model.DateTime,
ImageUrl = model.ImageUrl,
};
if (file != null)
{
entity.ImageUrl = file.FileName;
var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot\\img", file.FileName);
using (var stream = new FileStream(path, FileMode.Create))
{
await file.CopyToAsync(stream);
}
}
if (_postService.Create(entity,categoryIds))
{
return RedirectToAction("ListPosts", "Admin");
}
return View(model);
}
ViewBag.Categories = _categoryService.GetAll();
return View(model);
}
My PostModel
public class PostModel
{
public int Id { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public DateTime DateTime { get; set; }
public string ImageUrl { get; set; }
public List<Category> SelectedCategories { get; set; }
}
and Its my Createpost.cshtml
<form asp-controller="Admin" asp-action="CreatePost" method="post" enctype="multipart/form-data">
<div asp-validation-summary="All" class="text-danger"></div>
<input type="hidden" name="Id" value="#Model.Id" />
<div class="row">
<div class="col-md-8">
<div class="form-group row">
<label asp-for="Title" class="col-md-2 col-form-label"></label>
<div class="col-md-10">
<input asp-for="Title" value="" class="form-control" />
</div>
</div>
<div class="form-group row">
<label asp-for="DateTime" class="col-md-2 col-form-label"></label>
<div class="col-md-10">
<input asp-for="DateTime" value="" class="form-control" />
</div>
</div>
<div class="form-group row">
<label asp-for="ImageUrl" class="col-md-2 col-form-label"></label>
<div class="col-md-10">
<input type="file" name="file" value="" />
</div>
</div>
</div>
<div class="col-md-4">
#foreach (var item in (List<Category>)ViewBag.Categories)
{
<div class="form-check">
<input type="checkbox"
name="categoryIds"
value="#item.Id"
class="form-check-input"
id="category#(item.Id)">
<label class="form-check-label" for="category#(item.Id)">#item.Name</label>
</div>
}
</div>
<div class="col-md-12">
<div class="form-group row">
<div class="col-md-12">
<textarea asp-for="Text" class="form-control"></textarea>
</div>
</div>
</div>
</div>
<div class="form-group row">
<div class="col-md-12">
<button type="submit" class="btn btn-success btn-block">Share</button>
</div>
</div>
Finally what I should ? When i add post its not working. (Im new developer sorry for my basic error)
According to your description and codes, I found you update the post instead of creating a new post record.
I suggest you could try to below codes instead to create the new post record.
Notice: I directly use the dbcontext with DI in the CreatePost method, you could modify the Create method by yourself according to my codes.
[HttpPost]
public async Task<ActionResult> CreatePost(PostModel model, IFormFile file, int[] categoryIds)
{
int[] test = new int[] {1,2,3,4,5 };
if (ModelState.IsValid)
{
var entity = new Post
{
Title = model.Title,
Text = model.Text,
DateTime = model.DateTime,
ImageUrl = model.ImageUrl,
};
var reparePart1 = test.Select(categoryId => new PostCategory()
{
CategoryId = categoryId,
Post = entity
}).ToList();
entity.PostCategories = reparePart1;
_dbContext.Add(entity);
_dbContext.SaveChanges();
if (file != null)
{
entity.ImageUrl = file.FileName;
//var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot\\img", file.FileName);
//using (var stream = new FileStream(path, FileMode.Create))
//{
// await file.CopyToAsync(stream);
//}
}
//Create(entity, categoryIds);
return View(model);
}
//ViewBag.Categories = _categoryService.GetAll();
return View(model);
}

Many to Many relationship in ASP.NET CORE 3.1 by repository pattern

I've made many to many relationship in ASP.NET Core and there are two tables Category and Subject
This is Category Model
public class Category
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public List<CategorySubject> CategorySubjects { get; set; } = new List<CategorySubject>();
}
This is subject model
public class Subject
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Exam_Time { get; set; }
public List<CategorySubject> CategorySubjects { get; set; }
}
This is CategorySubject Model
public class CategorySubject
{
public int CategoryId { get; set; }
public int SubjectId { get; set; }
public Category Category { get; set; }
public Subject Subject { get; set; }
}
This is part of DatabaseContext
public DbSet<CategorySubject> CategorySubjects { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<CategorySubject>().HasKey(pt => new { pt.CategoryId, pt.SubjectId });
modelBuilder.Entity<CategorySubject>().HasOne(pt => pt.Category)
.WithMany(pt => pt.CategorySubjects).HasForeignKey(p => p.CategoryId);
modelBuilder.Entity<CategorySubject>().HasOne(pt => pt.Subject)
.WithMany(pt => pt.CategorySubjects).HasForeignKey(p => p.SubjectId);
}
I made one helper class by the name of Helper
public class Helpers:Profile
{
public Helpers()
{
CreateMap<Subject, SubjectViewModel>().ReverseMap();
CreateMap<SubjectViewModel, Subject>();
CreateMap<Category, CategoryViewModel>().ReverseMap();
}
}
this is category service:
public void Insert(Category category)
{
_context.Categories.Add(category);
}
public void Update(Category category)
{
_context.Categories.Update(category);
}
This is CategoryController :
// GET: CategoryController/Create
public IActionResult Create()
{
var subjectFromRepo = _categorySubject.Subject.GetAll();
var selectList = new List<SelectListItem>();
foreach (var item in subjectFromRepo)
{
selectList.Add(new SelectListItem(item.Name, item.Id.ToString()));
}
var vm = new CategoryViewModel()
{
Subjects = selectList
};
return View(vm);
}
// POST: CategoryController/Create
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(CategoryViewModel vm )
{
try
{
Category category = new Category()
{
Name = vm.Name
};
foreach(var item in vm.SelectedSubjects)
{
category.CategorySubjects.Add(new CategorySubject()
{
SubjectId = Int32.Parse(item)
});
}
_categorySubject.Category.Insert(category);
_categorySubject.Save();
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
// GET: CategoryController/Edit/5
public IActionResult Edit(int id)
{
var category = _categorySubject.Category.GetCategoryById(id);
var subjects = _categorySubject.Subject.GetAll();
var selectsubjects = category.CategorySubjects.Select(x => new Subject()
{
Id = x.Subject.Id,
Name = x.Subject.Name
});
var selectlist = new List<SelectListItem>();
subjects.ForEach(i => selectlist.Add(new SelectListItem(i.Name, i.Id.ToString(),
selectsubjects.Select(x => x.Id).Contains(i.Id))));
var vm = new CategoryViewModel()
{
Id= category.Id,
Name = category.Name,
Subjects = selectlist
};
return View(vm);
}
// POST: CategoryController/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(CategoryViewModel vm)
{
try
{
var category = _categorySubject.Category.GetCategoryById(vm.Id);
category.Name = vm.Name;
var selectedSubjects = vm.SelectedSubjects;
var existingSubjects = category.CategorySubjects.Select(x => x.SubjectId.ToString()).ToList();
var toAdd = selectedSubjects.Except(existingSubjects).ToList();
var toRemove = existingSubjects.Except(selectedSubjects).ToList();
var CategorySubjects = category.CategorySubjects.Where(x => !toRemove.Contains(x.SubjectId.ToString())).ToList();
foreach (var item in toAdd)
{
category.CategorySubjects.Add(new CategorySubject()
{
SubjectId = Int32.Parse(item),
CategoryId = Int32.Parse(item)
});
}
_categorySubject.Save();
return RedirectToAction("Index", "Category");
}
catch
{
return View();
}
}
This is Create.cshtml of Category :
<div class="style-form">
<h2 class="text-center mt-3 mb-lg-3">Create New Category</h2>
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="row">
<div class="col-md-3 col-lg-3 col-sm-3"></div>
<div class="col-md-6 col-lg-6 col-sm-6">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><strong>Name:</strong></span>
</div>
<input asp-for="Name" class="form-control input-hover" placeholder="Enter Name.." />
<span asp-validation-for="Name" class="text-danger"></span>
</div><br />
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><strong>Subject:</strong></span>
</div>
<select asp-for="SubjectId" class="form-control input-hover" asp-items="#Model.Subjects ">
<option value="">Please choose a Subject...</option>
</select>
<span asp-validation-for="SubjectId" class="text-danger"></span>
</div><br />
</div>
</div>
<div class="row">
<div class="col-md-3 col-lg-3 col-sm-3"></div>
<div class="col-md-6 col-lg-6 col-sm-6">
<div class="form-group">
<button type="button" class="btn btn-backToList">
<a asp-action="Index">Back to List</a>
</button>
<button type="submit" class="btn btn-create">Create</button>
</div>
</div>
</div>
</form>
There when I click on the create new category button I can get data of subject form drop down list, but when I want to submit it I face this error:
NullReferenceException: Object reference not set to an instance of an object.
AspNetCore.Views_Category_Create.b__20_0() in Create.cshtml, line 27
<select asp-for="SubjectId" class="form-control input-hover" asp-items="#Model.Subjects ">
I think there is an exception thrown in the Create (POST) method, it then goes to the catch, which returns a view without a model
catch
{
return View();
}
The next exception comes while rendering the page trying to bind to #Model.Subjects where Model is null.
Remove try/catch or handle the catch to find if there is any exception.

SaveChanges() duplicates records. one null record and the other one is fine

I'm using ASP.NET MVC 5 and Entity Framework.
I have these three model classes:
public class SubCat
{
public int Id { get; set; }
public string Description { get; set; }
[ForeignKey("Category")]
public int CategoryId { get; set; }
public Category Category { get; set; }
}
public class Item
{
public Item()
{
Images = new HashSet<Image>();
}
public int Id { get; set; }
public string Description { get; set; }
[ForeignKey("SubCat")]
public int? SubCatId { get; set; }
public SubCat SubCat { get; set; }
//public byte? Image { get; set; }
[ForeignKey("Images")]
public int? ImageId { get; set; }
public ICollection<Image> Images { get; set; }
public int ItemPoints { get; set; }
public int? PointsValue { get; set; }
public string Status { get; set; }
[Display(Name = "Delivery")]
public string DeliveryChoice { get; set; }
public bool? Approved { get; set; }
public bool? Bartered { get; set; }
}
public class Image
{
public int Id { get; set; }
[DisplayName("Upload File")]
public string Picture { get; set; }
[NotMapped]
public HttpPostedFileBase ImageFile { get; set; }
[ForeignKey("Item")]
public int? ItemId { get; set; }
public Item Item { get; set; }
}
When I add a new item, I need to give it a subcategory id in order to show it in the view and here is how I add a new item:
The form:
#using (Html.BeginForm("ItemSubmit", "Orders", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="form-group">
#Html.LabelFor(m => m.Item.Description, new { #class = "col-md-2 control-label" })
#Html.TextBoxFor(m => m.Item.Description, new { #class = "form-control" })
</div>
<br />
<div class="form-group">
<div class="col-md-6">
#Html.DropDownListFor(m => m.Category.Id, new SelectList(Model.Categories, "Id", "Description"), "Select Category", new { #class = "form-control", #id = "cats" })
</div>
<div class="col-md-6">
<select id="subs" disabled class="dropdown form-control" name="subCategory">
<option>Select Category First</option>
</select>
</div>
<br />
<br />
<br />
<div class="form-group">
#Html.LabelFor(m => m.Item.Status, new { #class = "col-md-2 control-label" })
#Html.DropDownListFor(m => m.Item.Status, new SelectList(Model.Items, "Status", "Status"), "", new { #class = "form-control" })
</div>
<br />
<div class="form-group">
#Html.LabelFor(m => m.Item.DeliveryChoice, new { #class = "col-md-2 control-label" })
#Html.DropDownListFor(m => m.Item.DeliveryChoice, new SelectList(Model.Items, "DeliveryChoice", "DeliveryChoice"), "", new { #class = "form-control" })
</div>
<div class="form-group">
<div class="col-md-1"></div>
<h3><span class="label label-primary">How many points would you suggest for your Item?</span></h3>
<div class="col-md-3"></div>
#Html.TextBoxFor(m => m.Item.ItemPoints, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Image.Picture, htmlAttributes: new { #class = "control-label col-md-2" })
<input type="file" name="ImageFile" required>
</div>
<br />
<br />
<div class="row">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</div>
}
and the controller:
[HttpPost]
public ActionResult ItemSubmit(Item item, int subCategory, Image imageModel, Order order, ApplicationUser user)
{
if (ModelState.IsValid)
{
var sub = _context.SubCats.SingleOrDefault(m => m.Id == subCategory);
//Item
item.SubCatId = sub.Id;
//Image
string fileName = Path.GetFileNameWithoutExtension(imageModel.ImageFile.FileName);
string extension = Path.GetExtension(imageModel.ImageFile.FileName);
fileName = fileName + DateTime.Now.ToString("yymmssfff") + extension;
imageModel.Picture = "~/Image/" + fileName;
fileName = Path.Combine(Server.MapPath("~/Image/"), fileName);
imageModel.ImageFile.SaveAs(fileName);
//Order
order.OrderDate = DateTime.Now;
order.TransactionType = "Item Submit";
order.UserId = User.Identity.GetUserId();
order.Approved = false;
order.ItemId = item.Id;
_context.Items.Add(item);
_context.Images.Add(imageModel);
_context.Orders.Add(order);
_context.SaveChanges();
}
return RedirectToAction("Index","Home");
}
but when _context.SaveChanges() is called, ef tend to save two copies of the item! one with a subcatid and one wihout a subcatid, and the funny thing is that the image takes the itemid of the item without the subcatid, so when i try to show the item on the view by the image it doesnt show because the item shown must be related to the subcat chosen.
here is the jquery i use in the view:
$(document).ready(function () {
$(".mybtn").on('click', function () {
var id = $(this).attr('data-id');
$("#images").html('');
$.get("/Items/GetSubById", { ID: id }, function (data) {
$("#sub-categories-btns").html(``);
for (var i = 0; i < data.length; i++) {
$("#sub-categories-btns").append(`
<button id="button" type="button" data-id="`+ data[i].Id + `" class="btn btn-success my-subcategory-btn">
`+ data[i].Description +`
</button>
`);
}
});
});
$("#sub-categories-btns").delegate(".my-subcategory-btn", 'click', function () {
var subId = $(this).attr('data-id');
$("#images").html('');
$.get("/Items/GetImgaesBySubCategoryId", { ID: subId }, function (data) {
for (var i = 0; i < data.length; i++) {
$("#images").append(`
<div class="form-group col-md-4">
<button data-id="`+ data[i].ItemId +`" name="item" class="ItemInfo img-btn"><img src="`+ data[i].Picture +`" width="200" height="200" /></button>
</div>
`);
}
});
});
$("#images").delegate('.img-btn', 'click', function () {
var imgId = $(this).attr('data-id');
window.location.href = "/Items/Buy?item=" + imgId;
});
});
This is the json i send to the view:
public JsonResult GetSubById(int? ID)
{
return Json(_context.SubCats.Where(p => p.CategoryId == ID), JsonRequestBehavior.AllowGet);
}
public JsonResult GetImgaesBySubCategoryId(int? ID)
{
var data = _context.Images.Where(m => m.Item.SubCatId == ID).ToList();
data.ForEach(img => img.Picture = img.Picture.Replace("~", ""));
return Json(data, JsonRequestBehavior.AllowGet);
}
How do i stop it from duplicating??
I actually found the solution a while ago but i forgot to post here!
i dont know how to explaing it but when the image is being created it filled the item navigation property with a new item with the same information of the item sent by the form!
So i just added these two lines before the uploading image code in the ItemSubmit Controller
imageModel.Item = null;
imageModel.ItemId = item.Id;
i also had a problem in the model that is sent from the view model so i commented the if(ModelState.IsValid)
hope this would help others

Dynamically created forms submits null to controller (MVC)

I'm creating multiple forms in a for in my view, the problem comes when I send to the controller on submit and it comes null.
here an example.
#model List<Project.ViewModels.ValidForm>
#if (Model != null)
{
for (int i = 0; i < Model.Count; i++)
{
<div>
#using (Html.BeginForm("Method", "Controller", FormMethod.Post, new { id = "valForm" + #Model[i].Id }))
{
<div class="col-md-4">
<label>Data 1</label><br />
#Html.TextBoxFor(m => m[i].Data1, new { #class = "form-control" })
</div>
<div class="col-md-4">
<label>Data 2 options</label><br />
#Html.TextBoxFor(m => m[i].Data2.Option1, new { #class = "form-control" })
</div>
<div>
<button type="submit" class="btn btn-success">Save this form</button>
</div>
}
</div>
}
}
And there are the viewmodels.
public class ValidForm{
public int data1 { get; set; }
public Data2 data2 {get;set;}
}
public class Data2{
public int option1 {get;set;}
public int option2 {get;set;}
}
and the controller.
[HttpPost]
public ActionResult validaciones(validform vm){
//do something.
return view();
}
The reason you get null is that your model is an array of ValidForm and not just one.
Change your controller to:
[HttpPost]
public ActionResult validaciones(ValidForm[] vms){
ValidForm vm = ValidForm[0];
//do something with vm.
return view();
}
If you want only one form at a time you can do this: (I've added Name = "PropName", mind the capital N in Name)
Then you're post action should expect a single VM
<div class="col-md-4">
<label>Data 1</label><br />
#Html.TextBoxFor(m => m[i].Data1, new { #class = "form-control", Name="Data1" })
</div>
For data2 the name needs to be Data2.option1, ect...
Fully working example:
HomeController:
public class HomeController : Controller
{
public class ValidForm
{
public string Id { get; set; }
public int data1 { get; set; }
public Data2 data2 { get; set; }
}
public class Data2
{
public int option1 { get; set; }
public int option2 { get; set; }
}
public ActionResult Index()
{
var model = new ValidForm[2] { new ValidForm { }, new ValidForm {data2 = new Data2{}} };
return PartialView(model);
}
[HttpPost]
public ActionResult Tester(ValidForm model)
{
return View("Index", new ValidForm[1] { model });
}
}
View:
#model MvcApplication1.Controllers.HomeController.ValidForm[]
#if (Model != null)
{
for (int i = 0; i < Model.Count(); i++)
{
<div>
#using (Html.BeginForm("Tester", "Home", FormMethod.Post, new { id = "valForm" + #Model[i].Id }))
{
<div class="col-md-4">
<label>Data 1</label><br />
#Html.TextBoxFor(m => m[i].data1, new { #class = "form-control", Name = "data1"})
</div>
<div class="col-md-4">
<label>Data 2 options</label><br />
#Html.TextBoxFor(m => m[i].data2.option1, new { #class = "form-control", Name = "data2.option1"})
</div>
<div>
<button type="submit" class="btn btn-success">Save this form</button>
</div>
}
</div>
}
}
Also note that the input boxes can only be numbers because you have all ints.

Validation in ViewModel not working

I have ViewModel which contains some proprty of class. Code below.
public class ViewModel
{
public Doctor VmDoctor { get; set; }
public Patient VmPatient { get; set; }
public List<Visit> VmVisit { get; set; }
public List<Hours> hours { get; set; }
public List<Hours> hours2 { get; set; }
public Schedule schedule { get; set; }
public bool BlockBtn { get; set; }
public Test test { get; set; }
}
In this case important property is Patient VmPatient. This is a model which has been generated by Database Model First. He has validation.Code below.
public partial class Patient
{
public Patient()
{
this.Visits = new HashSet<Visit>();
}
public int PatientID { get; set; }
[Required(ErrorMessage = "Podaj imię.")]
public string name { get; set; }
[Required(ErrorMessage = "Podaj nazwisko.")]
public string surname { get; set; }
[Required(ErrorMessage = "Podaj pesel.")]
[RegularExpression(#"^\(?([0-9]{11})$", ErrorMessage = "Nieprawidłowy numer pesel.")]
public string pesel { get; set; }
[Required(ErrorMessage = "Podaj miasto.")]
public string city { get; set; }
[Required(ErrorMessage = "Podaj kod pocztowy.")]
public string zipCode { get; set; }
[Required(ErrorMessage = "Podaj e-mail.")]
[EmailAddress(ErrorMessage = "Nieprawidłowy adres e-mail")]
public string email { get; set; }
[Required(ErrorMessage = "Podaj telefon komórkowy.")]
[RegularExpression(#"^\(?([0-9]{9})$", ErrorMessage = "Nieprawidłowy numer telefonu.")]
public string phone { get; set; }
public virtual ICollection<Visit> Visits { get; set; }
}
and i have Main Index where return my ViewModel because, display two Models in the same View. Code below
public ActionResult Index(int id)
{
ViewModel _viewModle = new ViewModel();
schedule = new Schedule();
if(Request.HttpMethod == "Post")
{
return View(_viewModle);
}
else
{
idDr = id;
_viewModle.schedule = schedule;
_viewModle.BlockBtn = _repository.BlockBtn(schedule);
_viewModle.VmDoctor = db.Doctors.Find(idDr);
_viewModle.hours = _repository.GetHours();
foreach (var item in _viewModle.hours)
{
_viewModle.hours2 = _repository.GetButtonsActiv(item.hourBtn, item.count, idDr, schedule);
}
}
if (_viewModle == null)
{
return HttpNotFound();
}
return View(_viewModle);
}
inside View Index i display my objects and rendered partial _FormPatient.Code below.
#model Dentist.Models.ViewModel
<div class="container-select-doctor">
<div class="row">
<div class="text-left">
<div class="row">
<div class="content">
<div class="profileImage">
<div class="imageContener"><img style="margin:1px;" src="#Url.Content("~/Images/" + System.IO.Path.GetFileName(#Model.VmDoctor.image))" /></div>
</div>
<div class="profileInfo">
<div class="profileInfoName">#Model.VmDoctor.name #Model.VmDoctor.surname</div>
<div class="profileInfoSpeciality">#Model.VmDoctor.specialty</div>
</div>
</div>
</div>
</div>
#ViewBag.firstDay<br />
#ViewBag.lastDay<br />
<div class="text-middle">
<div class="content">
<div id="partialZone">
#Html.Partial("_TableSchedule")
</div>
</div>
</div>
<div class="text-right">
<div class="content">
#Html.Partial("_FormPatient")
</div>
</div>
</div>
</div>
and last step is form which has been rendered inside Main Index by #Html.partial.code below
#model Dentist.Models.ViewModel
#using (Html.BeginForm("Create","Patient"))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<font color="red">#ViewBag.Pesel</font>
<div class="form-horizontal">
<div class="form-group">
#Html.LabelFor(model => model.VmPatient.email, htmlAttributes: new { #class = "control-label col-md-2" }, labelText: "E-mail:")
<div class="col-md-10">
#Html.TextBoxFor(model => model.VmPatient.email, new { htmlAttributes = new { #class = "form-control" } })
#*<input class="form-control" id="email" name="email" type="text" value="">*#
#Html.ValidationMessageFor(model => model.VmPatient.email, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.VmPatient.phone, htmlAttributes: new { #class = "control-label col-md-2" }, labelText: "Telefon kom.:")
<div class="col-md-10">
#Html.TextBoxFor(model => model.VmPatient.phone, new { maxlength = 9 })
#*<input class="form-control" maxlength="9" id="phone" name="phone" type="text" value="" />*#
#Html.ValidationMessageFor(model => model.VmPatient.phone, "", 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>
}
Please pay attention that this form redirect to another Controller where data will be validate and save to database. Method where data from FORM will be validate and save. code below
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Patient pat)
{
ViewModel vm = new ViewModel();
DentistEntities db = new DentistEntities();
if (ModelState.IsValid)
{
db.Patients.Add(pat);
db.SaveChanges();
}
return RedirectToAction("Index", "Visit", new { id = VisitController.idDr });
}
Conclusion How can i get validation for this form! I have observed that,every time modelstate.isvalid return false.. I dont have any ideas so I would like to ask you for help.
Best regards.
I would suggest that you do this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Patient pat)
{
ViewModel vm = new ViewModel();
DentistEntities db = new DentistEntities();
if (ModelState.IsValid)
{
db.Patients.Add(pat);
db.SaveChanges();
}
vm.VmPatient = pat;
return View(vm);
}
Render the view again, but this time the validation error messages should appear on the page (via the ValidationMessageFor() calls in the view). That, at least you can see why the validation has failed.
Alternatively, you could interrogate the modelstate e.g.
foreach (ModelState modelState in ViewData.ModelState.Values) {
foreach (ModelError error in modelState.Errors) {
string error = error.ErrorMessage;
}
}

Categories