MVC4: Html.DropDownList not saving value to Model Entity? - c#

I have created a View in my MVC4 application for Creating Users. In this View, a User can have a property set for either Organization or for Sponsor, but not both. My current code is correctly displaying all of the Organizations/Sponsors depending upon what is being shown based upon a Switch selection, but when I make a selection in either DropDownList and save the new user, all the DropDownLists return is a Null value for the User.
Users Model (Partial):
[GridColumn(Title = "Org.", SortEnabled = true, Width = "100")]
public int? MemberOrgId { get; set; }
[NotMappedColumn]
public int? SponsorOrgId { get; set; }
[ForeignKey("MemberOrgId")]
[NotMappedColumn]
public virtual MemberOrganizations Organization { get; set; }
[ForeignKey("SponsorOrgId")]
[NotMappedColumn]
public virtual SponsorOrganizations Sponsor { get; set; }
Create (VIEW):
#model PROJECT.Models.Users
#{
ViewBag.Title = "Create";
Layout = "~/Areas/Admin/.../.../.../_AdminLayout.cshtml";
string cancelEditUrl = "/Admin/UserController/";
}
#using (Html.BeginForm("Create", "UserController", FormMethod.Post, new { enctype = "multipart/form-data" })) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.RegisteredDate)
<div class="container">
<div class="row">
<div class="editor-label">
#Html.LabelFor(model => model.Email)
</div>
<div class="editor-field" style="margin-bottom: 15px">
#Html.TextBoxFor(model => model.Email, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Email)
</div>
</div>
<input type="checkbox" value="12345" name="Sponsor-Organization" checked class="userCreate-BSSwitch"/>
<div style="margin-bottom: 15px">
<div class="row switchOn">
<div class="editor-label">
#Html.LabelFor(model => model.MemberOrgId, "Organization")
</div>
<div class="editor-field">
#Html.DropDownList("OrganizationId", null, String.Empty, new { #class = "form-control", #id = "OrgIdDropDown" })
#Html.ValidationMessageFor(model => model.MemberOrgId)
</div>
</div>
<div class="row switchOff">
<dliv class="editor-label">
#Html.LabelFor(model => model.SponsorOrgId, "Sponsor")
</dliv>
<div class="editor-field" >
#Html.DropDownList("SponsorId", null, String.Empty, new { #class = "form-control", #id = "SponsorIdDropDown" })
#Html.ValidationMessageFor(model => model.SponsorOrgId)
</div>
</div>
</div>
<div class="row" id="submitRow">
<div class="btn-group ">
<button type="submit" value="Save" class="btn btn-success">Create User</button>
</div>
Cancel
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
<script type="text/javascript">
jQuery(document).ready(function () {
setTimeout(function () { $("#alert").alert('close'); }, 5000);
$('.switchOff').addClass('hide');
});
$.fn.bootstrapSwitch.defaults.onText = 'Member';
$.fn.bootstrapSwitch.defaults.offText = 'Sponsor';
$.fn.bootstrapSwitch.defaults.offColor = 'info';
$.fn.bootstrapSwitch.defaults.animate = false;
//$.fn.bootstrapSwitch.defaults.size = 'large';
$(document).ready(function () {
$('input:checkbox[name="Sponsor-Organization"]').bootstrapSwitch();
});
$('input:checkbox[name="Sponsor-Organization"]').on('switchChange.bootstrapSwitch', function (event, state) {
var checked = state;
if (checked) {
$('.switchOn').removeClass('hide');
$('.switchOff').addClass('hide');
$('#SponsorIdDropDown').val("");
}
else {
$('.switchOff').removeClass('hide');
$('.switchOn').addClass('hide');
$('#OrgIdDropDown').val("");
}
});
$(document).ready(function () {
$(".btn-danger").click(function () {
var cancel = confirm("Are you sure? Entered data will be lost.")
if (cancel != true) {
event.preventDefault(); // cancel the event
}
});
});
//$('input:checkbox[name="Sponsor-Organization"]').on('switchChange.bootstrapSwitch', function(event, state) {
</script>
Controller (Create GET):
//
// GET: /Admin/
public ActionResult Create()
{
ViewBag.headerTitle = "Create a User";
ViewBag.OrganizationId = new SelectList(db.MemberOrganizations, "Id", "Name");
ViewBag.SponsorId = new SelectList(db.SponsorOrganizations, "Id", "Name");
Users newUser = new Users();
newUser.RegisteredDate = DateTime.Now;
newUser.LastVisitDate = DateTime.Now;
newUser.ProfilePictureSrc = null;
return View(newUser);
}
Controller (Create HTTP-Post):
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Users users)
{
ViewBag.headerTitle = "Create a User";
if (ModelState.IsValid)
{
WebSecurity.CreateUserAndAccount(users.Email, "defaultPassword");
Users user2 = db.Users.Where(u => u.Email == users.Email).FirstOrDefault();
user2.Enabled = true;
user2.Password = Membership.GeneratePassword(15, 7);
user2.ForumUsername = users.Name;
user2.RegisteredDate = DateTime.Now;
user2.ReceiveSystemEmails = true;
db.Entry(user2).State = EntityState.Modified;
db.SaveChanges();
string[] roleNames = new string[] { "role1", "role2", "role3" };
System.Web.Security.Roles.AddUserToRoles(users.Email, roleNames);
return RedirectToAction("Index");
}
}
Anyone have some thoughts on the matter? I've tried a few different suggestions I found in other questions, but nothing has worked as of yet. This is my first MVC application, so I feel as if I may be overlooking something very basic.

for a field to be tied to the model it has to be in a "for" helper (except display). try changing your drop downs like this
#Html.DropDownListFor(x => x.OrganizationId, null, String.Empty, new { #class = "form-control"})
#Html.DropDownListFor(x => x.SponsorId, null, String.Empty, new { #class = "form-control" })
assuming your users model has fields OrganizationId and SponsorId those fields will be tied to the drop down (set them on the get and the drop down will be set and the drop down value will be passed back to the controller through the post)
Edit
I would suggest passing your drop down list through your model. to your model add
public SelectList OrganizationList { get; set; }
public SelectList SponsorList { get; set; }
then on your controller (in the get)
newUser.OranizationList = new SelectList(db.MemberOrganizations, "Id", "Name");
newUser.SponsorList = new SelectList(db.SponsorOrganizations, "Id", "Name");
then on your view
#Html.DropDownListFor(x => x.MemberOrgId, Model.OrganizationList, new { #class = "form-control" })

public class MyModel
{
public MyModel()
{
this.myDDLList = new List<SelectListItem>();
}
public List<SelectListItem> myDDLList { get; set; }
public int ddlID { get; set; }
}
public ActionResult Index()
{
MyModel model = new MyModel();
using (YourEntities context = new YourEntities())
{
var list = context.YourTable.ToList();
foreach (var item in list)
{
model.myDDLList.Add(new SelectListItem() { Text = item.NameField, Value = item.ValueField.ToString() });
}
}
return View(model);
}
#Html.DropDownListFor(x => x.ddlID, Model.myDDLList)

I resolved the issue by using ViewData[] to pass a pre-filled select list from my controller to my view:
// GET: /Admin/
public ActionResult Create()
{
ViewBag.headerTitle = "Create a User";
ViewData["Organization"] = new SelectList(db.MemberOrganizations, "Id", "Name");
ViewData["Sponsor"] = new SelectList(db.SponsorOrganizations, "Id", "Name");
Users newUser = new Users();
newUser.RegisteredDate = DateTime.Now;
newUser.LastVisitDate = DateTime.Now;
newUser.ProfilePictureSrc = null;
return View(newUser);
}
and then in my view simply read in the ViewData[] values into my Html.DropDownList's as separate SelectList's:
#Html.DropDownList("MemberOrgId", ViewData["Organization"] as SelectList, String.Empty, new { #class = "form-control", #id = "MemberOrgId" })
#Html.DropDownList("SponsorOrgId", ViewData["Sponsor"] as SelectList, String.Empty, new { #class = "form-control", #id = "SponsorOrgId" })

Related

How to send View Model only from Main Model in asp.net mvc?

I have using HTML.BeginForm for my MVC Form.Here is the html code
#model TodayViewModel
#{
ViewBag.Title = "Work Completed Today";
Layout = "~/Views/Shared/_LayoutBackend.cshtml";
}
#using (Html.BeginForm("Create", "CustomActivity", FormMethod.Post, new { role = "form", #id = "customActivityForm", #class = "activityForm" }))
{
<div>
<div class="myClass">
</div>
<div class="container" id="workordercategories">
#Html.LabelFor(m => m.CustomActivity.WorkOrderCategoriesName, new { #class = "" })
#Html.TextBoxFor(m => m.CustomActivity.WorkOrderCategoriesName, new { #class = "w100p mb0" })
#Html.ValidationMessageFor(m => m.CustomActivity.WorkOrderCategoriesName, "", new { #class = "text-danger" })
</div>
<div class="container" id="activity">
#Html.LabelFor(m => m.CustomActivity.Activity, new { #class = "" })
#Html.TextBoxFor(m => m.CustomActivity.Activity, new { #class = "w100p mb0" })
#Html.ValidationMessageFor(m => m.CustomActivity.Activity, "", new { #class = "text-danger" })
</div>
</div>
}
and my view model is
public class TodayViewModel
{
public IList<TodayListViewModel> TodaysVM { get; set; }
public CustomActivityViewModel CustomActivity { get; set; }
}
public class CustomActivityViewModel
{
[Required, Display(Name = "Work Category Name")]
public string WorkOrderCategoriesName { get; set; }
[Required]
public string Activity { get; set; }
}
while submitting form the Controller Method is:
[HttpPost]
public ActionResult Create(TodayViewModel model)
{
// to do here
}
In Controller Method I have to use TodayViewModel in which there are two method one of them (TodaysVM) is always null. Is there any way to submit form so that I can use CustomActivityViewModel instead of TodayViewModel in Controller?? Right Now If I use CustomActivityViewModel the value in the controller is null.
If you can't use CustomActivityViewModel in the View try
[HttpPost]
public ActionResult Create(TodayViewModel model)
{
CustomActivityViewModel obj = model.CustomActivity;
//....
}

ASP.Net MVC Update entity met with "The entity could not be updated."

I'm fairly new to coding, and am trying to tie up the finishing touches to my personal MVC project. I have done breakpoints to see double-check that the types of the values I'm passing through are matching up, which I believe they are, but it's hard for me to navigate the breakpoints and understand what information I should be looking at.
When I do breakpoints, it skips the final If-statement shown below:
if (service.UpdatePromotion(model))
{
TempData["SaveResult"] = "The promotion has been updated!";
return RedirectToAction("Index");
}
If there is any other information needing to be posted, I'll update here if I can or in the comments. I really appreciate anyone who looks this over.
Controller
// GET: Edit
public ActionResult Edit(int id)
{
var service = CreatePromotionService();
var detail = service.GetPromotionById(id);
var model = new PromotionEdit
{
PromotionId = detail.PromotionId,
PromotionName = detail.PromotionName,
DateFounded = detail.DateFounded,
Website = detail.Website
};
return View(model);
}
// POST: Edit
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, PromotionEdit model)
{
if (!ModelState.IsValid) return View(model);
if (model.PromotionId != id)
{
ModelState.AddModelError("", "ID Mismatch");
return View(model);
}
var service = CreatePromotionService();
if (service.UpdatePromotion(model))
{
TempData["SaveResult"] = "The promotion has been updated!";
return RedirectToAction("Index");
}
ModelState.AddModelError("", "The promotion could not be updated.");
return View(model);
}
Services
public PromotionDetail GetPromotionById(int id)
{
using (var ctx = new ApplicationDbContext())
{
var entity = ctx.Promotions
.Single(e => e.PromotionId == id
&& e.OwnerId == _userId);
return new PromotionDetail
{
PromotionId = entity.PromotionId,
PromotionName = entity.PromotionName,
DateFounded = entity.DateFounded.Date,
Website = entity.Website,
CreatedUtc = entity.CreatedUtc
};
}
}
public bool UpdatePromotion(PromotionEdit model)
{
using (var ctx = new ApplicationDbContext())
{
var entity = ctx.Promotions.Single(e =>
e.PromotionId == model.PromotionId
&& e.OwnerId == _userId);
entity.PromotionName = model.PromotionName;
entity.IsStarred = model.IsStarred;
entity.DateFounded = model.DateFounded.Date;
entity.Website = model.Website;
return ctx.SaveChanges() == 1;
}
}
Model
public class PromotionDetail
{
[Display(Name = "Promotion ID")]
public int PromotionId { get; set; }
[Display(Name = "Promotion Name")]
public string PromotionName { get; set; }
[Display(Name = "Date Founded")]
public DateTime DateFounded { get; set; }
[Display(Name = "Website")]
public string Website { get; set; }
[Display(Name = "Date Created")]
public DateTimeOffset CreatedUtc { get; set; }
}
public class PromotionEdit
{
public int PromotionId { get; set; }
[Display (Name = "Promotion Name")]
public string PromotionName { get; set; }
public bool IsStarred { get; set; }
public DateTime DateFounded { get; set; }
public string Website { get; set; }
}
View
#model Models.PromotionEdit
#{
ViewBag.Title = "Edit";
}
<h2>Updating Promotion</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.PromotionId)
<div class="form-group">
#Html.LabelFor(model => model.PromotionName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PromotionName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.PromotionName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.DateFounded, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.DateFounded, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.DateFounded, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Website, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Website, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Website, "", 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")
}
Shout-out to curious.netter in the comments. I was being lazy with the update, not changing anything and clicking Submit. Since there were no changes to apply, the entity would not update, which would skip the last If statement.

Submit data with dynamically added partial view to the controller using ViewModels not working

I'm adding dynamically items to an Enquiry form. Used partial view to for adding/deleting the items but while submitting the main view the values are not bound. My question is how to do the same.
Have checked couple of similar questions here and here But could not find what's missing .
Using 2 ViewModels , for Main View ( Enquiry) and for partial view ( LineItems) and used BeginCollectionItem for dynamically adding items.
Code:
ViewModels
public class EnquiryVM
{
public int ID { get; set; }
[Required]
public string EnquiryNumber { get; set; }
public int ClientID { get; set; }
public IEnumerable<SelectListItem> Clients { get; set; }
public Client Client { get; set; }
public int ItemID { get; set; }
public List<EnquiryLineItem> LineItems { get; set; }
}
public class EnquiryLineItemVM
{
public int ID { get; set; }
[Required]
public string ItemDesc { get; set; }
public int Quantity { get; set; }
public int ManufacturerId { get; set; }
public IEnumerable<SelectListItem> ManufacturerList { get; set; }
}
Views :
Main:
#model ViewModel.EnquiryVM
#using (Html.BeginForm("Create", "Enquiries", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.EnquiryNumber, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-3">
#Html.EditorFor(model => model.EnquiryNumber, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.EnquiryNumber, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ClientID, "Client", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-3">
#Html.DropDownListFor(u => u.ClientID, (IEnumerable<SelectListItem>)Model.Clients, "--Select--")
#Html.ValidationMessageFor(model => model.ClientID, "", new { #class = "text-danger" })
</div>
</div>
<div id="LineItems">
// #using (Html.BeginForm()) // do we require again here since this will be like nested form? tested commenting still not working
// {
<div id="editorRowsLineitems">
#foreach (var item in Model.LineItems)
{
#Html.Partial("_CreateEnquiryItem", item)
}
</div>
#Html.ActionLink("Add Items", "CreateLineItem", null, new { id = "addItem", #class = "button" });
// }
</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")
<script type="text/javascript">
$(function () {
$('#addItem').on('click', function () {
$.ajax({
url: '#Url.Action("CreateLineItem")',
cache: false,
success: function (html) {
$("#editorRowsLineitems").append(html);
$("form").removeData("validator");
$("form").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse("form");
}
});
return false;
});
$('#editorRowsLineitems').on('click', '.deleteRow', function () {
$(this).closest('.editorRow').remove();
});
$('form').data('validator', null);
$.validator.unobtrusive.parse($('form'));
});
</script>
}
partial view :
#model ViewModels.EnquiryLineItemVM
<div class="editorRow">
#using (Html.BeginCollectionItem("ItemList"))
{
<table class="table">
<tr>
<td>
#Html.EditorFor(model => model.ItemDesc)
</td>
<td>
#Html.EditorFor(model => model.Quantity)
</td>
<td>
#Html.DropDownListFor(model => model.ManufacturerId, Model.ManufacturerList, "--Please Select--")
</td>
<td>
Delete
</td>
</tr>
</table>
}
Controller :
public ActionResult Create()
{
var viewModel = GetAllCategories();
return View(viewModel);
}
private EnquiryVM GetAllCategories()
{
var model = new EnquiryVM();
var clients = db.Clients.ToList();
model.Clients = clients.Select(s => new SelectListItem
{
Value = s.ID.ToString(),
Text = s.Name
});
var LineItems = new List<EnquiryLineItem>();
model.LineItems = LineItems;
return model;
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create( EnquiryVM enquiryVM)
{
var enquiry = new Enquiry();
enquiry.EnquiryNumber = enquiryVM.EnquiryNumber;
enquiry.ClientID = enquiryVM.ClientID;
enquiry.EnquiryLineItems = enquiryVM.LineItems; //line items are null
if (ModelState.IsValid)
{
db.Enquiries.Add(enquiry);
enquiryVM.ID = enquiry.ID;
foreach (var item in enquiry.EnquiryLineItems)
{
item.EnquiryID = enquiryVM.ID;
db.EnquiryLineItems.Add(item);
}
db.SaveChanges();
return RedirectToAction("Index");
}
var viewModel = GetAllCategories();
return View(enquiryVM);
}
How shall I map the dynamically added row's values to the ViewModel ( EnquiryVM ) so that I can insert it into the DB.
Thanks for your patience and time.
The name of your collection property is LineItems, therefore your code to generate its controls needs to be
#using (Html.BeginCollectionItem("LineItems")) // not ..("ItemList")
{
....
}
so that it generates inputs with name="LineItems[xxxx].ItemDesc" etc, rather than your current use which generates name="ItemList[xxxx].ItemDesc" (where xxxx is the Guid)
As a side note, the code in your POST method will throw an exception if ModelState is invalid because you return the view and have not repopulated the IEnumerable<SelectListItem> Clients property. Refer The ViewData item that has the key 'XXX' is of type 'System.Int32' but must be of type 'IEnumerable' for a detailed explanation.
In addition, the final 2 lines of your script to add items ($('form').data('validator', null); $.validator.unobtrusive.parse($('form')); should be removed (reparsing the validator is expensive and your doing it twice - once before you add the html (the 2 lines above) and once after you add the html

Object Reference Not Set DropDownListFor [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 7 years ago.
I'm still learning MVC and am confused. I am converting a windows application for CRUD with a database to an MVC website. I got an entire ViewModel's CRUD working that uses dropdownlist's already and the code is identical but throwing object reference not set errors with it in another page.
Controller
public ActionResult Create()
{
var shiftsEmployees = new ShiftsEmployeesViewModel();
var oEmployees = new CEmployees();
oEmployees.GetActiveEmployees();
shiftsEmployees.Employees = oEmployees;
return View(shiftsEmployees);
}
// POST: Shifts/Create
[HttpPost]
public ActionResult Create(ShiftsEmployeesViewModel shiftsEmployees)
{
try
{
shiftsEmployees.InsertShift();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
View
#model StatTracker.ASPMVC.ViewModels.ShiftsEmployeesViewModel
#{
ViewBag.Title = "Add Shift";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal text-center">
#Html.ValidationSummary(true, "", new {#class = "text-danger"})
<div class="form-group">
<div class="input-group date col-md-12">
#Html.LabelFor(model => model.Shift.Date, new {#class = "control-label"})
#Html.EditorFor(model => model.Shift.Date, new {#class = "form-control datepicker", placeholder = "Pick a date"})
#Html.ValidationMessageFor(model => model.Shift.Date, "", new {#class = "text-danger"})
#Html.LabelFor(model => model.Employee.FullName, new {#class = "control-label"})
#Html.DropDownListFor(model => model.Employee.Id, new SelectList(Model.Employees.Employees, "Id", "FullName", Model.Employee), null, null)
#Html.ValidationMessageFor(model => model.Employee.Id, "", new {#class = "text-danger"})
#Html.LabelFor(model => model.Shift.Hours, new {#class = "control-label"})
#Html.EditorFor(model => model.Shift.Hours, new {#class = "form-control", placeholder = "Hours worked"})
#Html.ValidationMessageFor(model => model.Shift.Hours, "", new {#class = "text-danger"})
</div>
</div>
<div class="form-group">
<div class="col-md-12">
<input type="submit" value="Add Shift" class="btn btn-default"/>
</div>
</div>
</div>
}
<div class="text-center col-md-12">
#Html.ActionLink("Back to List", "Index")
</div>
ViewModel:
namespace StatTracker.ASPMVC.ViewModels
{
public class ShiftsEmployeesViewModel
{
public CShift Shift { get; set; }
public CEmployee Employee { get; set; }
public CEmployees Employees { get; set; }
public void InsertShift()
{
CShift.InsertShift(hours: Shift.Hours, employeeid: Employee.Id, date: Shift.Date);
}
public void UpdateShift()
{
CShift.UpdateShift(hours: Shift.Hours, employeeid: Employee.Id, date: Shift.Date, shiftid: Shift.Id);
}
}
}
working code with same idea
controller
public ActionResult Create()
{
var oSalesEmployeeService = new SalesEmployeeServiceViewModel();
var oServices = new CServices();
oServices.GetServices();
var oEmployees = new CEmployees();
oEmployees.GetActiveEmployees();
oSalesEmployeeService.Employees = oEmployees;
oSalesEmployeeService.Services = oServices;
return View(oSalesEmployeeService);
}
// POST: Sales/Create
[HttpPost]
public ActionResult Create(SalesEmployeeServiceViewModel oSalesEmployeeService)
{
try
{
oSalesEmployeeService.InsertSale();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
viewmodel
using StatTracker.BL;
namespace StatTracker.ASPMVC.ViewModels
{
public class SalesEmployeeServiceViewModel
{
public CSale Sale { get; set; }
public CEmployees Employees { get; set; }
public CServices Services { get; set; }
public CEmployee Employee { get; set; }
public CService Service { get; set; }
public void InsertSale()
{
CSale.InsertSale(service: Service.Id, date: Sale.Date, employee: Employee.Id);
}
public void UpdateSale()
{
CSale.UpdateSale(service: Service.Id, date: Sale.Date, employee: Employee.Id, salesid: Sale.Id);
}
}
}
view
#model StatTracker.ASPMVC.ViewModels.SalesEmployeeServiceViewModel
#{
ViewBag.Title = "Add Sale";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal text-center">
#Html.ValidationSummary(true, "", new {#class = "text-danger"})
<div class="form-group">
<div class="input-group date col-md-12">
#Html.LabelFor(model => model.Sale.Date, new {#class = "control-label"})
#Html.EditorFor(model => model.Sale.Date, new {#class = "form-control datepicker", placeholder = "Pick a date"})
#Html.ValidationMessageFor(model => model.Sale.Date, "", new {#class = "text-danger"})
#Html.LabelFor(model => model.Employee.FullName, new {#class = "control-label"})
#Html.DropDownListFor(model => model.Employee.Id, new SelectList(Model.Employees.Employees, "Id", "FullName", Model.Employee), null, null)
#Html.ValidationMessageFor(model => model.Employee.Id, "", new {#class = "text-danger"})
#Html.LabelFor(model => model.Service.Description, new {#class = "control-label"})
#Html.DropDownListFor(model => model.Service.Id, new SelectList(Model.Services.Services, "Id", "Description", Model.Service), null, null)
#Html.ValidationMessageFor(model => model.Service.Id, "", new {#class = "text-danger"})
</div>
</div>
<div class="form-group">
<div class="col-md-12">
<input type="submit" value="Add Sale" class="btn btn-default"/>
</div>
</div>
</div>
}
<div class="text-center col-md-12">
#Html.ActionLink("Back to List", "Index")
</div>
You have not indicated exactly where the error is being thrown but there will be numerous reasons
Firstly, a view model is not just a holder for a series of data models. It should contain only those properties which you need to display/edit in the view. Currently if any properties of CShift or CEmployee have validation attributes (other that the Date and Hours properties), your model will be invalid.
Secondly, when you return the view, you need to return the model to the view and also assign the value of Employees which currently will be null (hence the exception when you access Model.Employees.Employees in the DropDownList() method).
Based on the view you have shown, your view model should be just (add validation attributes as required)
public class ShiftsEmployeesViewModel
{
public DateTime Date { get; set; }
public int Employee { get; set; }
public float Hours { get; set; }
public SelectList EmployeeList { get; set; }
}
Controller
public ActionResult Create()
{
ShiftsEmployeesViewModel model = new ShiftsEmployeesViewModel();
model.Employee = ? // set default selection
ConfigureCreateModel(model);
return View(model);
}
[HttpPost]
public ActionResult Create(ShiftsEmployeesViewModel model)
{
if (!ModelState.IsValid)
{
ConfigureCreateModel(model);
return View(model);
}
.... // map the view model properties to a new instance of the data model, save and redirect
}
private void ConfigureCreateModel(ShiftsEmployeesViewModel model)
{
var oEmployees = (new CEmployees()).GetActiveEmployees();
model.EmployeeList = new SelectList(oEmployees, "Id", "FullName");
}
View
#Html.LabelFor(m => m.Date)
#Html.TextBoxFor(m => m.Date)
#Html.ValidationMessageFor(m => m.Date)
#Html.LabelFor(m => m.Employee)
#Html.DropDownListFor(m => m.Employee, Model.EmployeeList, "-please select-")
#Html.ValidationMessageFor(m => m.Employee)
#Html.LabelFor(m => m.Hours)
#Html.EditorFor(m => m.Hours)
#Html.ValidationMessageFor(m => m.Shift.Hours)
Side note: Your current usage of SelectList(Model.Employees.Employees, "Id", "FullName", Model.Employee) where you use the 4th parameter is pointless. Your binding to a property, so attempting to set the value of the Selected is ignored (the selected option is based on the value of the property your binding to)
Yes, because model.Employee has not been initialized and therefore, model.Employee.Id will throw a null reference exception. After that You still need to initialize the shift object as it will also throw a null reference exception when model.Shift.Hours is accessed. Please see my suggestions below
You will have to initialize the remaining properties
public ActionResult Create()
{
var oEmployees = new CEmployees();
oEmployees.GetActiveEmployees();
var shiftsEmployees = new ShiftsEmployeesViewModel
{
Employee = new CEmployee(),
Shift = new CShift(),
Employees = oEmployees;
};
return View(shiftsEmployees);
}
Another option is to just always initialize them in the constructor of the view model (you just have to make sure that somewhere GetActiveEmployees is getting called)
public class ShiftsEmployeesViewModel
{
public ShiftsEmployeesViewModel()
{
this.oEmployees = new CEmployees();
this.Employee = new CEmployee();
this.Shift = new CShift();
}
}
Also change the implementation in your view for the dropdownlist
#Html.DropDownListFor(model => model.Employee.Id,
new SelectList(Model.Employees.Employees ?? new List<CEmployee>(),
"Id",
"FullName"))

Formatting Text in LabelFor() Control

I'm trying to format a price label so the price is always in the 0.00 format and I saw a similar question that used similar code as mine below. I'm getting an object reference not set to an instance of an object error and I know that it is because I am calling Model.Price but I'm very new to MVC and I don't understand how I set Model.Price before the page is loaded.
#Html.LabelFor(model => model.Price, "0.00", new { id = "priceLabel", Value = String.Format("{0:C}", Model.Price) })
Here is the Controller code:
[HttpGet]
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
// set the paper style list
List<SelectListItem> styles = new List<SelectListItem>();
ViewBag.paperStyle = new SelectList(styles, "Value", "Text");
// set the subject list
List<SelectListItem> subjects = new List<SelectListItem>();
ViewBag.subject = new SelectList(subjects, "Value", "Text");
// set the number of pages list
List<SelectListItem> numberOfPages = new List<SelectListItem>();
ViewBag.Urgency = new SelectList(urgency, "Value", "Text");
// set the document type list
List<SelectListItem> documentTypes = new List<SelectListItem>();
ViewBag.documentType = new SelectList(documentTypes, "Value", "Text");
// set the academic level list
List<SelectListItem> academicLevel = new List<SelectListItem>();
ViewBag.academicLevel = new SelectList(academicLevel, "Value", "Text");
// set the number of sources list
List<SelectListItem> numberOfSources = new List<SelectListItem>();
ViewBag.numberOfSources = new SelectList(numberOfSources, "Value", "Text");
// set the currency list
List<SelectListItem> currencies = new List<SelectListItem>();
currencies.Add(new SelectListItem() { Text = "$", Value = "USD", Selected = true });
currencies.Add(new SelectListItem() { Text = "£", Value = "GBP", Selected = false });
currencies.Add(new SelectListItem() { Text = "€", Value = "EUR", Selected = false });
ViewBag.currency = new SelectList(currencies, "Value", "Text");
return View();
}
[HttpPost]
public ActionResult Contact(WritingAppModel c)
{
if (ModelState.IsValid)
{
// send the email and process the payment
// if payment is ready then send email
if (isPaymentReady())
{
// send email
}
}
return View();
}
public class modelData
{
public string documentType { get; set; }
public string numberOfPages { get; set; }
public string urgency { get; set; }
}
public JsonResult getNewPrice(modelData dropdownValues)
{
// check for urgency first since that is the base price
if (dropdownValues.urgency != null)
{
currentPrice = Convert.ToDecimal(dropdownValues.urgency);
if (dropdownValues.documentType != null)
{
currentPrice = currentPrice + Convert.ToDecimal(dropdownValues.documentType);
if (dropdownValues.numberOfPages != null)
{
currentPrice = currentPrice * Convert.ToInt16(dropdownValues.numberOfPages);
}
}
}
// do something with value and return a decimal
return Json(new { currentPrice = currentPrice }, JsonRequestBehavior.AllowGet);
}
Here is the View code:
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<div class="row">
#Html.LabelFor(model => model.Name, "Name:")
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="row">
#Html.LabelFor(model => model.Email, "Email:")
#Html.TextAreaFor(model => model.Email, new { id = "email" })
#Html.ValidationMessageFor(model => model.Email)
</div>
<div class="row">
#Html.LabelFor(model => model.Topic, "Topic:")
#Html.EditorFor(model => model.Topic)
#Html.ValidationMessageFor(model => model.Topic)
</div>
<div class="row">
#Html.LabelFor(model => model.Subject, "Subject:")
#Html.DropDownListFor(model => model.Subject, (SelectList)ViewBag.subject, "--Select--", new { id = "subjectList" })
</div>
<div class="row">
#Html.LabelFor(model => model.Style, "Style:")
#Html.DropDownListFor(model => model.Style, (SelectList)ViewBag.paperStyle, "--Select--", new { id = "paperStyleList" })
</div>
<div class="row">
#Html.LabelFor(model => model.DocumentType, "Document Type:")
#Html.DropDownListFor(model => model.DocumentType, (SelectList)ViewBag.documentType, "--Select--", new { id = "documentTypeList" })
</div>
<div class="row">
#Html.LabelFor(model => model.AcademicLevel, "Academic Level:")
#Html.DropDownListFor(model => model.AcademicLevel, (SelectList)ViewBag.academicLevel, "--Select--", new { id = "academicLevelList" })
</div>
<div class="row">
#Html.LabelFor(model => model.NumberOfPages, "Number of Pages/Words:")
#Html.DropDownListFor(model => model.NumberOfPages, (SelectList)ViewBag.numberOfPages, "--Select--", new { id = "numberOfPagesList" })
</div>
<div class="row">
#Html.LabelFor(model => model.NumberOfSources, "Number of Sources:")
#Html.DropDownListFor(model => model.NumberOfSources, (SelectList)ViewBag.numberOfSources, "--Select--", new { id = "numberOfSourcesList" })
</div>
<div class="row">
#Html.LabelFor(model => model.Urgency, "Urgency:")
#Html.DropDownListFor(model => model.Urgency, (SelectList)ViewBag.urgency, "--Select--", new { id = "urgencyList" })
</div>
<div class="row">
#Html.LabelFor(model => model.Spacing, "Spacing:")
#Html.RadioButtonFor(model => model.Spacing, "Single") Single
#Html.RadioButtonFor(model => model.Spacing, "Double") Double
</div>
<div class="row">
#Html.LabelFor(model => model.Requirements, "Requirements:")
#Html.TextAreaFor(model => model.Requirements)
</div>
<div class="row">
#Html.DropDownListFor(model => model.Currency, (SelectList)ViewBag.currency, null, new { id = "currencyList" })
<h2>
#Html.LabelFor(model => model.Price, "{0:C}", new { id = "priceLabel" })
</h2>
<input type="submit" value="Submit" />
<input type="reset" value="Reset" />
</div>
}
I'm not sure what the controller looks like, but take this test version...
Controller
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new Product {Price = 9.99m};
return View(model);
}
}
I've created a Product class as an example..
public class Product
{
public decimal Price { get; set; }
}
Your view will then need to reference the model.
#model WebApplication2.Controllers.Product
#Html.LabelFor(model => model.Price, new { id = "priceLabel" })
#Html.TextBoxFor(model => model.Price, new { id = "priceLabel",
Value = String.Format("{0:C}", Model.Price) })
You can see from the controller where the model is built and passed to the view.
var model = new Product {Price = 9.99m};
return View(model);
The LabelFor will generate a label for the actual property, not the value for it.
EDIT: Based on comment.
If you only want to display the property...
#String.Format("{0:C}", Model.Price)
or. as Steve suggested...
Model:
[DisplayFormat(DataFormatString = "{0:C}")]
public decimal Price { get; set; }
View
#Html.DisplayFor(model => model.Price)

Categories