I am trying to create a view that takes a view model, simply has an int property and a list of child model, with int; string; string properties.
What i am trying to do is everytime user clicks an "add line" button, i am creating a new child model in my view model list, letting user edit the properties and then want to post back to my controller but no matter what, it is always empty.
I know this must be simple but has me stumped for hours today? Is there some magic that needs to happen?
Any help appreciated.
My Controller:
using System.Web.Mvc;
using WebApplication1.Models;
namespace WebApplication1.Controllers
{
public class EmployeeUploadController : Controller
{
// GET: EmployeeUpload
public ActionResult EmployeeUpload()
{
var vm = new EmployeeBulkUpload { UploadedBy = 12345 };
vm.Employees.Add(new EmployeeBulkUploadItem { Id = 1, JobTitle = "Labourer", Name = "Jimmy" });
return View(vm);
}
[HttpPost]
[ActionName("EmployeeUpload")]
public ActionResult EmployeeUploadPost(EmployeeBulkUpload vm)
{
return View(vm);
}
}
}
My Model:
using System.Collections.Generic;
namespace WebApplication1.Models
{
public class EmployeeBulkUpload
{
public EmployeeBulkUpload()
{
Employees = new List<EmployeeBulkUploadItem>();
}
public int UploadedBy { get; set; }
public List<EmployeeBulkUploadItem> Employees { get; set; }
}
public class EmployeeBulkUploadItem
{
public int Id { get; set; }
public string Name { get; set; }
public string JobTitle { get; set; }
}
}
My "Container" view:
#using WebApplication1.Models
#model WebApplication1.Models.EmployeeBulkUpload
#{
ViewBag.Title = "EmployeeUpload";
}<div>
#using (Html.BeginForm("EmployeeUpload", "EmployeeUpload", FormMethod.Post))
{
<h2>EmployeeUpload</h2>
<button>Add New</button>
foreach(EmployeeBulkUploadItem emp in Model.Employees)
{
Html.RenderPartial("_EmployeeUploadItem", emp);
}
<input type="submit" name="SaveButton" value="Save" />
}
</div>
My Item Partial view:
#model WebApplication1.Models.EmployeeBulkUploadItem
<li>
#Html.LabelFor(x => x.Id)
#Html.EditorFor(x => x.Id)
#Html.LabelFor(x => x.Name)
#Html.EditorFor(x => x.Name)
#Html.LabelFor(x => x.JobTitle)
#Html.EditorFor(x => x.JobTitle)
</li>
I can load with one record and post straight back but is always empty in my Controller EmployeeUploadPost method?
Related
First of all, I know this question has been asked many, many times. I've read countless articles and Stack Overflow answers. I've tried to figure this problem out for four days and I think I need help if someone doesn't mind.
I have two databases. The employee database has a field called "DisplayName" -- the second database has a relationship with the first and they work together great. I'm able to call the two databases perfectly in another application.
You can see the in the picture Index Page
that I have a list of people. I want a dropdown below it that lists all display names in the database so employees can add themselves to the list. You'll see a dropdown in the image but it's not populated.
Seems simple. But geez. Part of a problem I'm having is my home controller already has a function to populate the list in the picture so I can't do another on that page. I've tried a lot of suggestions on a lot of sites. I get IEnumerable errors or display reference errors....
Here's my controller (again - it has nothing in it that helps the dropdown):
namespace SeatingChart.Controllers
{
public class HomeController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET: Employee
public ActionResult Index()
{
var lists = db.BreakModels
.Include("Employee")
.Include("TimeEntered")
.Include("TimeCleared")
.Include("DisplayName")
.Select(a => new HomeIndexViewModels
{
Employee = a.Employee,
DisplayName = a.EmployeeModels.DisplayName,
TimeEntered = a.TimeEntered,
TimeCleared = a.TimeCleared.Value,
Id = a.EmployeeModels.Id,
});
return View(lists);
}
View:
#model IEnumerable<SeatingChart.Models.HomeIndexViewModels>
#{
Layout = null;
}
#Html.Partial("_Header")
<div class="container_lists">
<div class="container_break col-md-8">
<h5 style="text-align:center">Break List</h5>
<table class="table-bordered col-lg-12">
#if (Model != null)
{
foreach (var item in Model)
{
if (item.TimeCleared == null)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.DisplayName)
</td>
<td>
BV
</td>
<td>
#item.TimeEntered.ToString("HH:mm")
</td>
</tr>
}
}
}
</table>
#using (Html.BeginForm())
{
<div class="row site-spaced">
<div class="col-3">
#Html.DropDownList("DisplayName", new SelectList(new List<string>() { "---Dispatcher---" }), new { #class = "required " })
</div>
</div>
<div class="col-3">
<input type="submit" value="Submit" class="site-control" />
</div>
}
</div>
</div>
ViewModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
namespace SeatingChart.Models
{
public class HomeIndexViewModels
{
//Break Model
public int BreakId { get; set; }
public int Employee { get; set; }
public DateTime TimeEntered { get; set; }
public DateTime? TimeCleared { get; set; }
//Employee Model
public int Id { get; set; }
public string DisplayName { get; set; }
public string DisplayNames { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool NotActive { get; set; }
public int Force { get; set; }
public string EmployeeList { get; set; }
}
}
I hope this is clear enough. I've tried so many different ways with so much code - the errors are different with everything I've tried.
Thanks in advance for your patience and help!
You can add to your viewmodel
public List<SelectListItem> Employees { get; set; }
Then you can populate this list with controller then in view just call it with:
#Html.DropDownListFor(m => m.Id, Model.Employees, new { #class = "form-control", required = "required" })
Update - how to populate list. Should work (but not tested code).
public List<SelectListItem> GetEmployeeForDropdown(List<HomeIndexViewModels> list)
{
List<SelectListItem> empList = new List<SelectListItem>();
try
{
if (list != null && list.Count > 0)
{
foreach (var item in list)
{
empList.Add(new SelectListItem { Text = item.DisplayName, Value = item.Id.ToString() });
}
}
else
{
empList.Add(new SelectListItem { Text = "No items", Value = string.Empty });
}
}
catch (Exception ex)
{
//handle exceptions here
}
return empList;
}
Edit: Remember to use your model in view!
I have read the tutorials and prepared a list of checkboxes for the page. When the form is submitted, the Selected property only get the value false.
Is there something I missed?
The Model
public class SelectStudentModel
{
public int StudentID { get; set; }
public string CardID { get; set; }
public string Name { get; set; }
public bool Selected { get; set;}
}
The ViewModel
public class SelectStudentViewModel
{
public List<SelectStudentModel> VMList;
public SelectStudentViewModel()
{
VMList = SelectStudentModel.GETStudent();
}
}
The View
#using Student.Models
#model SelectStudentViewModel
#using (Html.BeginForm("AddStudent", "SectionStudent", FormMethod.Post, new { #role = "form" }))
{
#{ for (int i = 0; i < Model.VMList.Count(); i++)
{
<tr>
<td>#Html.CheckBoxFor(m => m.VMList[i].Selected)</td>
<td>#Html.DisplayFor(model => model.VMList[i].Name)</td>
</tr>
}
}
<input type="submit" value="submit" />
}#* end form *#
The Controller for posted data
[HttpPost]
public ActionResult AddStudent(SelectStudentViewModel model)
{
foreach (SelectStudentModel m in model.VMList)
{
Console.Write(m.Selected.ToString());
}
return PartialView("StudentSelectForm", model);
}
VMList is a field in your SelectStudentViewModel model. You need to change it to a property (with a getter/setter) so the DefaultModelBinder can set the values
public class SelectStudentViewModel
{
public List<SelectStudentModel> VMList { get; set; } // change
public SelectStudentViewModel()
{
VMList = SelectStudentModel.GETStudent();
}
}
Side note: Suggest you change #Html.DisplayFor(model => model.VMList[i].Name) to #Html.LabelFor(m => m.VMList[i].Selected, Model.MList[i].Name) so that you get a label associated with the checkbox
I am using autogenerated entity model classes and than i used partial class with metadata to put validations on auto genetrated classes like below.
public class tblDepartmentCustom
{
[Key]
public int DepartmentId { get; set; }
[Required(ErrorMessage = "Department name is required")]
public string DepartmentName { get; set; }
}
[MetadataType(typeof(tblDepartmentCustom))]
public partial class tblDepartmentMaster
{
}
The original class that was generated by entity framework is given below.
public partial class tblDepartmentMaster
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public tblDepartmentMaster()
{
this.tblDesignationMasters = new HashSet<tblDesignationMaster>();
}
public int DepartmentId { get; set; }
public string DepartmentName { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<tblDesignationMaster> tblDesignationMasters { get; set; }
}
So the problem here is that whenever i try to validated model state it comes out to be true.below is the code.
#model EmployeeManager.Models.tblDepartmentCustom
#{
ViewBag.Title = "InsertDepartment";
Layout = "~/Views/Shared/_AdminLayout.cshtml";
}<div class="col-md-4">
#using (Html.BeginForm("InsertDepartment", "Departments", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<span class="error-class">#ViewBag.FoundError</span>
<br />
<label>Department Name</label>
#Html.TextBoxFor(m => m.DepartmentName, new { #class = "form-control" })
<br />
<input type="submit" class="btn btn-info" value="Add Department" />
}
</div>
And the action below.
[HttpGet]
public ActionResult InsertDepartment()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
[ActionName("InsertDepartment")]
public ActionResult InsertDepartmentPost()
{
using (PMSEntities dc = new PMSEntities())
{
tblDepartmentMaster dm = new tblDepartmentMaster();
TryUpdateModel(dm);
if(ModelState.IsValid)
{
dc.tblDepartmentMasters.Add(dm);
dc.SaveChanges();
return View("_Success");
}
else
{
ViewBag.FoundError = "Department name is required.";
return View();
}
}
}
In order for partial classes to work, both partials must have the same namespace. You don't have to move the actual files around your file structure, just edit the namespace of tblDepartmentCustom to match that of tblDepartmentMaster.
My question is about asp.net-MVC:
my problem is: when I click on Submit button in Index View, Address Fields from my Partial view is null...this is my code:
I have a Person Class :
public class Person
{
public Person()
{
Address = new Address();
}
public int Id { get; set; }
public string Name { get; set; }
public string Family { get; set; }
public Address Address { get; set; }
}
and Address class :
public class Address
{
public string City { get; set; }
public string Street { get; set; }
}
I was Created an Index view such this. In my view there is a partial view :
#using (Html.BeginForm("Index", "Home"))
{
<label>Name: </label>
#Html.EditorFor(m => m.Name)
<label>Family</label>
#Html.EditorFor(m => m.Family)
#Html.Partial("GetAddress", Model.Address)
<input type="submit" value="Submit" title="Submit" />
}
and my partial view is :
<label>City :</label>
#Html.EditorFor(m => m.City)
<br />
<label>Street: </label>
#Html.EditorFor(m => m.Street)
this is Image of Error :
http://8pic.ir/images/eog6owv9h9bbi156tp0r.png
Try by adding HtmlFieldPrefix when you are calling your partial.
#Html.Partial("GetAddress", Model.Address, new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "Address" } })
This way you will name your fileds properly and model binder will bind them correctly.
i want to create users with special features in mvc. when user is going to create i want to assign some special feature to each user like particular user having his own house, having his own car using checkbox selection. the particular feature is reside in different table named feature. then how can i add those features with user while creating the user.
i have created a view model named ViewModelUserWithFeature
public class ViewModelUserWithFeature
{
public User User { get; set; }
public Feature Feature { get; set; }
public List<Feature> feature { get; set; }
public IEnumerable<User> IUser { get; set; }
private UserDbContext userDbContext;
private IUserService userService;
public void ViewUserList()
{
userService = new RoleService(userDbContext);
IUser = userService.GetUsers();
}
public void AddNewUser(User userAdd)
{
userService = new UserService(userDbContext);
User = userService.AddUser(userAdd);
userService.SaveUser();
}
}
here is my view in which i want to two textboxes and a list of features which are going to select by checkbox and attached with the user.
#model App.ViewModel.ViewModelUserWithFeature
#using (Html.BeginForm("Create", "User", FormMethod.Post))
{
<div>
#Html.TextBoxFor(m => m.User.UserName)
#Html.ValidationMessageFor(m => m.UserName)
</div>
<div>
#Html.TextBoxFor(m => m.User.UserAddres)
#Html.ValidationMessageFor(m => m.UserAddres)
</div>
#for(int i=0; i < Model.Feature; i++)
{
<div class="cb"><input type="checkbox" name="checkbox"></div>
<div class="per-content">
<label for="1"> Model.Feature.FeatureName</div>
}
<div>
<button type="submit" id="btn-rd">Submit</button>
</div>
}
Controller
[HttpPost]
public ActionResult Create(User user)
{
ViewModelUserWithFeature viewModelUserWithFeature = new ViewModelUserWithFeature(usertDbContext);
if (ModelState.IsValid)
{
viewModelUserWithFeature.AddNewUser(user);
}
return RedirectToAction("Index", viewModelUserWithFeature);
}
not able to achieve that what i have tried so far i have mentioned . please help. thanks in advance.
Use view models to represent what you display and edit
public class FeatureVM
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
public class UserVM
{
public string Name { get; set; }
public string Address { get; set; }
public List<FeatureVM> Features { get; set; }
}
Controller
public ActionResult Create()
{
UserVM model = new UserVM();
model.Features = // map all available features
return View(model);
}
[HttpPost]
public ActionResult Create(UserVM model)
{
}
View
#model UserVM
#using(Html.BeginForm())
{
#Html.LabelFor(m => m.Name)
#Html.TextBoxFor(m => m.Name)
#Html.ValidationMessageFor(m => m.Name)
.....
for(int i = 0; i < Model.Features.Count; i++)
{
#Html.HiddenFor(m => m.Features[i].ID)
#Html.CheckBoxFor(m => m.Features[i].IsSelected)
#Html.LabelFor(m => m.Features[i].IsSelected, Model.Features[i].Name)
}
<input type="submit" value="Create" />
}
try with this, in you Model of Feature add a new property
public bool isFeatureOf { get; set; }
also in your model for the method AddNewUser change it to
public void AddNewUser(User userAdd,List<Feature> features)
{
userService = new UserService(userDbContext);
User = userService.AddUser(userAdd);
userService.SaveUser();
//featureService = new FeatureService(yourdbcontext)
foreach (Feature item in features)
{
//save to db
featureService.SaveFeature(item,User.Id);
//i don't know if in your database you already have a table,colum or something to map the features by user
}
}
then in your view
for(int index=0; index < Model.Features.Count(); index++)
{
#Html.HiddenFor(m=>Model.Features[index].NameFeature)
#Html.Raw(Model.Features[index].NameFeature)
#Html.CheckBoxFor(m=>Model.Features[index].isFeatureOf)
}
also in your view you'll need to change this
<div>
#Html.TextBoxFor(m => m.User.UserName)
#Html.ValidationMessageFor(m => m.UserName)
</div>
<div>
#Html.TextBoxFor(m => m.User.UserAddres)
#Html.ValidationMessageFor(m => m.UserAddres)
</div>
to:
<div>
#Html.TextBoxFor(m =>Model.User.UserName)
#Html.ValidationMessageFor(m => Model.User.UserName)
</div>
<div>
#Html.TextBoxFor(m => m.User.UserAddres)
#Html.ValidationMessageFor(m =>Model.User.UserAddres)
</div>
in your controller change your param to get the whole Model like this
[HttpPost]
public ActionResult Create(ViewModelUserWithFeature model)
{
if (ModelState.IsValid)
{
model.AddNewUser(model.User,model.Features);
}
return RedirectToAction("Index", viewModelUserWithFeature);
}
hope this can help you