ViewModel is null on second AJAX Post - c#

I have PartialView which display pricing information and allow users to enter promocode. when clicking the button on _ListItem.cshtml on the first time, the HttpPost method manage to grab the OrderDetailsViewModel.Details and process in the controller. on the second click of the button, it couldn't capture the data in the Model.
Here are my codes.
NewPurchaseViewModel
public class NewPurchaseViewModel{
public NewPurchaseViewModel()
{
Details = new OrderDetailsViewModel();
Shipping = new Address();
Billing = new Address();
}
public string Email { get; set; }
public string Name { get; set; }
...
public OrderDetailsViewModel Details { get; set; }
}
OrderDetailsViewModel
public class OrderDetailsViewModel{
public OrderDetailsViewModel()
{
Items = new List<ItemsOrderViewModel>();
Shipping = 0.00M;
}
public string Promocode { get; set; }
public string SubTotal{ get; set; }
public string Total{ get; set; }
public string Discount{ get; set; }
public string Shipping{ get; set; }
public List<ItemsOrderViewModel> Items { get; set; }
}
Info.cshtml
#model ~.ViewModels.NewPurchaseViewModel
...
#Html.Partial("~/Views/CheckOut/_ListItem.cshtml", Model.Details, new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "Details"} })
_ListItem.cshtml
#model Nutric.hive.eStore.ViewModels.OrderDetailsViewModel
#using (Ajax.BeginForm("Verify", "PromoCode", null, new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "divPromoCode",
InsertionMode = InsertionMode.Replace
}))
{
<table>
...
<tbody>
#for (var i = 0; i < Model.Items.Count; i++)
{
<tr>
<td>#Model.Items[i].ProductPackage.Name</td>
<td class="qty">#Model.Items[i].Quantity</td>
<td class="price">#Model.Items[i].ItemTotal</td>
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.Id)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.Name)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.Description)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.ImageURL)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.Status)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.RetailPrice)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.TaxCode)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.PackageCode)
#Html.HiddenFor(Model => Model.Items[i].Quantity)
#Html.HiddenFor(Model => Model.Items[i].ItemTotal)
</tr>
}
...
<tr>
<td colspan="3">
<div class="col-lg-10">
<div class="input-group">
#Html.TextBoxFor(Model => Model.Promocode, new { #class = "form-control", maxlength = 15, placeholder = "Type Voucher Code Here...", type = "text" })
<span class="input-group-btn">
<input type="submit" class="btn btn-secondary" value="Apply !">
</span>
</div>
</div>
</td>
</tr>
</tbody>
</table>
#Html.HiddenFor(Model => Model.Discount)
#Html.HiddenFor(Model => Model.Shipping)
#Html.HiddenFor(Model => Model.Subtotal)
#Html.HiddenFor(Model => Model.Total)
}
#section Script{
#Scripts.Render("~/bundles/jqueryval")
#Scripts.Render("~/bundles/jquery")
}
PromoCodeController.cs
[HttpPost]
public ActionResult Verify(NewPurchaseViewModel postModel)
{
OrderDetailsViewModel odvm = postModel.Details;
//some logic here to check the promo code.
return PartialView("~/Views/CheckOut/_ListItem.cshtml",odvm );
}

Related

List of objects is empty in my model when posting

When I'm posting my form my list of objects is always empty and I can't figure out why. I searched and read through the dozen similar questions and still no go. The fields in my model are posted back with the exception of the TimeAdds list. Any help is appreciated.
My controller
public ActionResult TimeAdd()
{
TimeAddModel model = new TimeAddModel();
model.StartDate = DateTime.Now;
model.EndDate = DateTime.Now;
model.TypeId = 1;
model.TimeAdds = new List<TimeAdd>();
return View(model);
}
[HttpPost]
public ActionResult TimeAdd(TimeAddModel model)
{
if (Request.Form["dateRange"] != null) {
ModelState.Clear();
TimeAdd t = new TimeAdd();
t.Id = Guid.NewGuid();
t.TypeId = model.TypeId;
model.TimeAdds.Add(t);
}
else {
//save
}
return View(model);
}
My Class
public class TimeAdd
{
[Key]
public Guid Id { get; set; }
public int TypeId { get; set; }
}
My Model
public class TimeAddModel
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public int TypeId { get; set; }
public List<TimeAdd> TimeAdds { get; set; }
public TimeAddModel()
{
this.TimeAdds = new List<TimeAdd>();
}
}
and my page
#model metrosales.Models.TimeAddModel
#using metrosales.Data.TimeOff.Model
<div class="container excess">
#using (Html.BeginForm("TimeAdd", "Service", FormMethod.Post)) {
<div class="row">
<div class="col-sm-6">
#Html.LabelFor(model => model.StartDate)
#Html.TextBoxFor(x => x.StartDate, "{0:yyyy-MM-dd}", new { #class = "form-control", #type = "date", #id = "StartDate", #name = "StartDate" })
</div>
<div class="col-sm-6">
#Html.LabelFor(model => model.EndDate)
#Html.TextBoxFor(x => x.EndDate, "{0:yyyy-MM-dd}", new { #class = "form-control", #type = "date", #id = "EndDate", #name = "EndDate" })
</div>
</div>
<input type="submit" class="btn text-center" name="dateRange" value="Submit1" />
<input type="submit" class="btn text-center" name="submit" value="Submit2" />
}
<div>
<br />
<table id="tblExcess" class="table" cellpadding="0" cellspacing="0">
<tr>
<th>ID</th>
<th>Start</th>
</tr>
#for (var i = 0; i < Model.TimeAdds.Count(); i++) {
<tr>
<td>#Html.HiddenFor(model => model.TimeAdds[i].Id)</td>
<td>#Html.TextBoxFor(model => model.TimeAdds[i].TypeId)</td>
</tr>
}
</table>
</div>
To be able the MVC perform the data binding correctly it is necessary to prepare the context in the TimeAdd view.
Therefore, to provide an index for each item in the TimeAdds list make following changes in the TimeAdd.cshtml:
#Html.Hidden("TypeId", Model.TypeId)
#for (var i = 0; i < Model.TimeAdds.Count(); i++)
{
#Html.Hidden("TimeAdds[" + i + "].Id", Model.TimeAdds[i].Id)
#Html.Hidden("TimeAdds[" + i + "].TypeId", Model.TimeAdds[i].TypeId)
<tr>
<td>#Html.TextBoxFor(model => Model.TimeAdds[i].Id)</td>
<td>#Html.TextBoxFor(model => Model.TimeAdds[i].TypeId)</td>
</tr>
}

Working with EditorTemplates and radio buttons

I am showing data in tabular format. the table is generated automatically when working with EditorFor and EditorTemplates.
in each row of table i am showing ID, Name, Country dropdown, checkboxes for hobbies selection and radio button for sex selection.
all are working fine but i am not being able to bind radio buttons for sex.
i am not being able to understand what i am missing for which i am getting error.
please have a look at my code and give me direction what to change for radio buttons.
my full code
controller code
public class HomeController : Controller
{
public ActionResult Index()
{
StudentListViewModel osvm = new StudentListViewModel();
osvm.Sex = osvm.GetSex();
osvm.Countries = osvm.GetCountries();
return View(osvm);
}
[HttpPost]
public ActionResult Index(StudentListViewModel oStudentListViewModel)
{
return View(oStudentListViewModel);
}
}
viewmodel
public class StudentListViewModel
{
//public List<Country> Country { get; set; }
public List<SelectListItem> Countries { get; set; }
public IList<Student> Students { get; set; }
public List<Sex> Sex { get; set; }
public StudentListViewModel()
{
Students = new List<Student>
{
new Student
{
ID=1,Name="Keith",CountryID="0",SexID="F",
Hobbies= new List<Hobby>
{
new Hobby{ID=1,Name="Football",Checked=true},
new Hobby{ID=2,Name="Hocky",Checked=false},
new Hobby{ID=3,Name="Cricket",Checked=false}
}
},
new Student
{
ID=2,Name="Paul",CountryID="2",
Hobbies= new List<Hobby>
{
new Hobby{ID=1,Name="Football",Checked=false},
new Hobby{ID=2,Name="Hocky",Checked=true},
new Hobby{ID=3,Name="Cricket",Checked=false}
}
},
new Student
{
ID=3,Name="Sam",CountryID="3",
Hobbies= new List<Hobby>
{
new Hobby{ID=1,Name="Football",Checked=false},
new Hobby{ID=2,Name="Hocky",Checked=false},
new Hobby{ID=3,Name="Cricket",Checked=true}
}
}
};
}
public List<Sex> GetSex()
{
Sex = new List<Sex>
{
new Sex{ID="M",SexName="Male"},
new Sex{ID="F",SexName="Female"}
};
return Sex;
}
public List<SelectListItem> GetCountries()
{
Countries = new List<SelectListItem>
{
new SelectListItem{Value="1",Text="India"},
new SelectListItem{Value="2",Text="UK"},
new SelectListItem{Value="3",Text="USA"}
};
return Countries;
}
}
Model class
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public string CountryID { get; set; }
public string SexID { get; set; }
public IList<Hobby> Hobbies { get; set; }
}
public class Hobby
{
public int ID { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
}
public class Sex
{
public string ID { get; set; }
public string SexName { get; set; }
}
Main View Index.cshtml
#model EditorTemplateSample.Models.StudentListViewModel
#{
ViewBag.Title = "Home Page";
}
<br /><br />
#using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
<div class="form-group">
<div class="col-md-12 table-responsive">
<table class="table table-bordered table-hover">
<tr>
<th>
ID
</th>
<th>
Name
</th>
<th>
Country
</th>
<th>
Hobbies
</th>
<th>
Sex
</th>
</tr>
<tbody>
#Html.EditorFor(m => m.Students, new { Countries = Model.Countries, Sex = Model.Sex })
</tbody>
</table>
</div>
</div>
}
EditorTemplates\Student.cshtml
#model EditorTemplateSample.Models.Student
<tr>
<td>
#Html.HiddenFor(m => m.ID)
#Html.DisplayFor(m => m.ID)
</td>
<td>
#Html.TextBoxFor(m => m.Name)
</td>
<td>
#Html.DropDownListFor(m => m.CountryID,
new SelectList((List<SelectListItem>)ViewData["Countries"], "Value", "Text", Model.CountryID), "-- Select Country--")
<td>
<td>
#Html.EditorFor(m => m.Hobbies)
<td>
<td>
#Html.EditorFor(m => ((EditorTemplateSample.Models.Sex) ViewData["Sex"]).ID)
<td>
</tr>
EditorTemplates\Hobby.cshtml
#model EditorTemplateSample.Models.Hobby
<div class="checkbox">
#Html.HiddenFor(m => m.ID)
#Html.HiddenFor(m => m.Name)
#Html.CheckBoxFor(m => m.Checked)
#Html.LabelFor(m => m.Checked, Model.Name)
</div>
EditorTemplates\Sex.cshtml
#model EditorTemplateSample.Models.Sex
<td>
<div class="checkbox">
#Html.HiddenFor(m => ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).ID)
#Html.HiddenFor(m => ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).SexName)
#Html.RadioButtonFor(m => ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).ID, ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).ID)
#Html.LabelFor(m => ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).ID, ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).SexName)
</div>
</td>
#Html.EditorFor(m => m.Students, new { Countries = Model.Countries, Sex = Model.Sex }) the above way i pass Sex model data to Student.cshtml file
from Student.cshtml file i try to bind ID #Html.EditorFor(m => ((EditorTemplateSample.Models.Sex) ViewData["Sex"]).ID)
in EditorTemplates\sex.cshtml file
#model EditorTemplateSample.Models.Sex
<td>
<div class="checkbox">
#Html.HiddenFor(m => ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).ID)
#Html.HiddenFor(m => ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).SexName)
#Html.RadioButtonFor(m => ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).ID, ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).ID)
#Html.LabelFor(m => ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).ID, ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).SexName)
</div>
</td>
guide me how could i pass my sex data from main index view to sex view in EditorTemplates folder.
Edit
in main view i add this line
#Html.EditorFor(m => m.Students, new { Countries = Model.Countries, MainModel = Model, Sex = Model.Sex })
in student.cshtml i edit line like #Html.EditorFor(m => ((EditorTemplateSample.Models.StudentListViewModel)ViewData["MainModel"]).Sex, new { Sex = (List<EditorTemplateSample.Models.Sex>)ViewData["Sex"] })
in sex.cshtml for radio button generation i changed line likes
<div class="checkbox">
#Html.HiddenFor(m => m.ID)
#Html.HiddenFor(m => m.SexName)
#Html.RadioButtonFor(m => m.ID,Model.ID)
#Html.LabelFor(m => m.ID, Model.SexName)
</div>
but still no luck. badly stuck due to lack of control over asp.net mvc EditorTemplates now radio buttons are coming but all are selected by default which is wrong. see the latest UI.
please help me to get out of this problem. thanks
Your Student class contains a property string SexID which is what you are wanting to bind the selected radio button value to. But your EditorTemplate is for a model that is typeof Sex, and you Student model does not contain a property which is typeof Sex (and nor should it).
Using an EditorTemplate in this case makes no sense - your binding to a simple property, not a complex object or collection of objects. The radio buttons should be generated in your Student.cshtml template.
#model EditorTemplateSample.Models.Student
<tr>
<td>
#Html.HiddenFor(m => m.ID)
#Html.DisplayFor(m => m.ID)
</td>
<td>#Html.TextBoxFor(m => m.Name)</td>
<td>#Html.DropDownListFor(m => m.CountryID, new SelectList((List<SelectListItem>)ViewData["Countries"], "Value", "Text", Model.CountryID), "-- Select Country--")</td>
<td>#Html.EditorFor(m => m.Hobbies)</td>
<td>
#foreach(var sex in (List<Sex>)ViewData["Sex"])
{
<label>
#Html.RadioButtonFor(m => m.SexID, sex.ID, new { id = "" })
<span>#sex.SexName</span>
</label>
}
</td>
</tr>

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

c# object created in main view passed to partial view empty

C#, .Net 4.5, MVC 5
Referring to XX1 and XX2 below (Main View):
The object is initialized in the main controller. (Both the header and the Detial.)
I have added breakpoints at both XX1 and XX2 to confirm that that the initialized values are still there.
No problem with XX1. The initialized values are there and it is
passed to and received by the controller.
XX2 have a problem. The initialized values are still there and is
passed to, but a null object is somehow received by, the controller.
Why do the controller for the detail not pick up the passed parameters for it.
Model:
public class SampleNonRoutine
{
public HeaderNonRoutine SampleHeader { get; set; }
public List<DetailNonRoutine> SampleDetail { get; set; }
public string Comments { get; set; }
}
public class HeaderNonRoutine
{
public string Division { get; set; }
public string Name { get; set; }
public string Telephone { get; set; }
[DisplayName("Sample Title")]
public string SampleTitle { get; set; }
[DisplayName("Retain Sample for")]
public int RetainSample { get; set; }
}
public class DetailNonRoutine
{
public int ID { get; set; }
[DisplayName("Sample Reference #")]
public string SampleReference { get; set; }
[DisplayName("Sample Test")]
public string SamplesTested { get; set; }
[DisplayName("Sample Assays")]
public string SampleAssays { get; set; }
}
Controller:
For Parent View
public ActionResult NonRoutineSamples(string SaveSend)
{
SampleNonRoutine sample = new SampleNonRoutine();
sample.SampleHeader = new HeaderNonRoutine();
sample.SampleDetail = new List<DetailNonRoutine>();
sample.Comments = "Toets";
for (int i = 0; i < 10; i++)
{
sample.SampleDetail.Add(new DetailNonRoutine { ID = i + 1, SampleReference = "", SamplesTested = "", SampleAssays = "" });
}
return View(sample);
}
For Partial Views:
Header (Partial View):
public ActionResult _HeaderNonRoutineSamples(HeaderNonRoutine model)
{
//...some code
PersonDetail pDetail = _db.ListPerson(_MePerson.First().Number.ToString());
model.Name = pDetail.Name + " " + pDetail.Surname;
model.Telephone = pDetail.PhoneWork;
model.Division = pDetail.Division;
model.RetainSample = 30;
return PartialView(model);
}
Detail:
public ActionResult _DetailNonRoutineSamples(List<DetailNonRoutine> model)
{
return PartialView(model);
}
Views:
Main
#model LaboratorySampleRegistration.Models.SampleNonRoutine
#{
ViewBag.Title = "NonRoutineSamples";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<h4>Non-Routine Samples</h4>
<table>
<tr>
<td>
#Html.Action("_HeaderNonRoutineSamples", "Home", Model.SampleHeader) XX1
</td>
</tr>
<tr>
<td>
#Html.Action("_DetailNonRoutineSamples", "Home", Model.SampleDetail) XX2
</td>
</tr>
<tr>
<td>
<div>
#Html.LabelFor(model => model.Comments, htmlAttributes: new { #class = "control-label col-md-2" })
<div>
<span>
#Html.TextAreaFor(model => model.Comments, new { #class = "SampleRoutineComments" })
#Html.ValidationMessageFor(model => model.Comments, "", new { #class = "text-danger" })
</span>
</div>
</div>
</td>
</tr>
<tr>
<td>
<hr style="border-top:1px solid black !important;" />
<p>
<input id="SaveSend" name="SaveSend" type="submit" value="Send to Laboratory" class="btn btn-default" />
</p>
</td>
</tr>
</table>
}

Create and Index in same view using partial view

Ok I give up after several hours of troubleshooting, but I am sure you all will see a solution right away. I have three models Person, Course and CoursePreference. The CoursePreference model has two foreign keys PersonId and CourseId.
What I want to do:
I want to create a view where the user can add course preferences in a top Create section, and when they click Add, the form would post and refresh the List in the same view. Essentially I am combining Index and Create in one view. So I created an Index View and a partial view called _CreatePartial in the CoursePreference folder.
The problem:
The view displays fine, but with two problems. 1) The CoursePreferenceId field shows a dropdownlist. I want it to be a hidden field since it's an identity. I copied the code exactly from the scaffolded Create View, which hides the Id correctly. Dont know why it's not working in the _CreatePartial view? 2) Most importantly, my _CreatePartial will not add any course preference. It looks as if the form is posting but no record is added.
What gives?
Here are the models, controllers and views:
---------------------------
Models (stripped down versions)
---------------------------
public class CoursePreference
{
public int CoursePreferenceId { get; set; }
public Nullable<int> CourseId { get; set; }
public Nullable<int> PersonId { get; set; }
public virtual Course Course { get; set; }
public virtual Person Person { get; set; }
}
public class Person
{
public int PersonId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public virtual ICollection<CoursePreference> CoursePreferences { get; set; }
}
public class Course
{
public int CourseId { get; set; }
public Nullable<int> ProgramId { get; set; }
public string Name { get; set; }
public virtual ICollection<CoursePreference> CoursePreferences { get; set; }
}
------------------
Controllers
------------------
public ActionResult _CreatePartial()
{
ViewBag.CourseId = new SelectList(db.Courses, "CourseId", "Name");
ViewBag.PersonId = new SelectList(db.People, "PersonId", "LastName");
return View("_CreatePartial");
}
public ActionResult Index()
{
ViewBag.CourseId = new SelectList(db.Courses, "CourseId", "Name");
ViewBag.PersonId = new SelectList(db.People, "PersonId", "LastName");
var coursepreferences = db.CoursePreferences.Include(c => c.Course).Include(c => c.Person);
return View(coursepreferences.ToList());
}
---------------------------
Index View
---------------------------
#model IEnumerable<MyProj.Models.CoursePreference>
#{ ViewBag.Title = "Index";
}
<h4>Add Course Preferences</h4>
<div>
#Html.Partial("~/Views/CoursePreference/_CreatePartial.cshtml", new MyProj.Models.CoursePreference())
</div>
<br />
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Course.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Person.LastName)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Course.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Person.LastName)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.CoursePreferenceId }) |
#Html.ActionLink("Details", "Details", new { id=item.CoursePreferenceId }) |
#Html.ActionLink("Delete", "Delete", new { id=item.CoursePreferenceId })
</td>
</tr>
}
</table>
---------------------------
_CreatePartial View
---------------------------
#model MyProj.Models.CoursePreference
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>CoursePreference</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.CoursePreferenceId, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CoursePreferenceId)
#Html.ValidationMessageFor(model => model.CoursePreferenceId)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CourseId, "CourseId", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("CourseId", String.Empty)
#Html.ValidationMessageFor(model => model.CourseId)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PersonId, "PersonId", new { #class = "control-label col-md-2" }
<div class="col-md-10">
#Html.DropDownList("PersonId", String.Empty)
#Html.ValidationMessageFor(model => model.PersonId)
</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>
Nothing is saved because you don't have a controller method to handle the post. Implement this in your controller:
[HttpPost]
public ActionResult Index(CoursePreference pref)
{
// Insert code to fetch database context into variable "db"
db.CoursePreferences.Add(pref);
db.SaveChanges();
return RedirectToAction("Index");
}
As for the hidden ID-field, you should use:
#Html.HiddenFor(model => model.CoursePreferenceId)
-not EditorFor or LabelFor.

Categories