I am trying to get my partial view to display an object name as it iterates through a collection. This is no problem when I refresh the original page but as soon as I do an AJAX post and try to return the new object to the partial view, it cannot find a specific object.
I get a null object error on the FeeTypes object which should be included in the members object as Members.Fees(whateverno).FeeTypesG
Also the ajax post does not refresh the div on success because it never gets success back (or im doing it wrong) NOTE: It still posts everything correctly to the database and saves fine.
Models
public class Members
{
[Key]
public int MemberNo { get; set; }
[Display(Name = "Member Fees")]
public virtual ICollection<Fees> Fees { get; set; }
}
public class Fees
{
[Key]
public int FeeNo { get; set; }
public virtual FeeTypes FeeType { get; set; }
[Display(Name = "Fee Type")]
public int FeeTypesId { get; set; }
[Display(Name = "Frequency of Fee")]
public FeeOccurence FeeOccurence { get; set; }
[Display(Name = "Fee Amount")]
public float FeeAmount { get; set; }
[Display(Name = "Fee Status")]
public FeeStatus FeeStatus { get; set; }
[Display(Name = "Date received (for billing)")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
public DateTime? FeeDate { get; set; }
public virtual Members Members { get; set; }
public int MemberNo { get; set; }
}
public class FeeTypes
{
public int Id { get; set; }
[Display(Name = "Fee Name")]
public string FeeTypeName { get; set; }
}
Controller Action
public ActionResult AddFees(Fees fees)
{
string result;
Members members = db.Members.Find(fees.MemberNo);
ViewBag.Fees = db.Fees
.Include(x => x.FeeType)
.Where(x => x.Members.MemberNo.Equals(x.MemberNo));
ViewBag.FeeTypesId = new SelectList(db.FeeTypes, "Id", "FeeTypeName");
if (ModelState.IsValid)
{
db.Fees.Add(fees);
db.SaveChanges();
return PartialView("~/Views/Members/_MemberDetails.cshtml", members);
}
ViewBag.MemberNo = new SelectList(db.Members, "MemberNo", "FirstName", fees.MemberNo);
result = "Something went wrong!";
return Json(result, JsonRequestBehavior.AllowGet);
}
AJAX Post
saveBtn.click(function () {
var token = $('[name=__RequestVerificationToken]').val();
var postData = {
__RequestVerificationToken: token,
FeeOccurence: feeOccField.val(),
FeeAmount: amountField.val(),
FeeStatus: statusField.val(),
FeeTypesId: typeField.val(),
FeeDate: dateField.val(),
MemberNo: memberNo
};
$.ajax({
url: '/Members/AddFees',
type: 'POST',
data: postData,
success: function (members) {
alert(result);
$("#details").html(members);
},
error: function (result) {
alert(result);
},
traditional: true
});
Partial view
#using AccPortal.Models
#model AccPortal.Models.Members
<h5 class="md-fees-title">Fees</h5>
<table cellpadding="0" cellspacing="0" class="md-fees-table">
<thead>
<tr>
<th>#Html.LabelFor(model => model.Fees.First().FeeDate)</th>
<th>#Html.LabelFor(model => model.Fees.First().FeeOccurence)</th>
<th>#Html.LabelFor(model => model.Fees.First().FeeTypesId)</th>
<th>#Html.LabelFor(model => model.Fees.First().FeeStatus)</th>
<th>#Html.LabelFor(model => model.Fees.First().FeeAmount)</th>
<th></th>
</tr>
</thead>
<tbody>
#{ int i = 0; }
#foreach (var item in Model.Fees)
{
<tr data-id="#i">
#Html.HiddenFor(model => model.Fees.ElementAt(i).FeeNo, new { #class = "md-fee-value", name = "md-fee-id", data_type = "testid", id = "testid" })
<td class="md-fee-value" data-type="date">#String.Format("{0:d}", item.FeeDate)</td>
<td class="md-fee-value" data-type="occ">#item.FeeOccurence.GetDisplayName()</td>
//ERROR HAPPENS ON THE NEXT LINE WHEN IT CANT FIND FEETYPES
<td class="md-fee-value" data-type="type">#item.FeeType.FeeTypeName</td>
<td class="md-fee-value" data-type="status">#item.FeeStatus</td>
<td class="md-fee-value" data-type="amount">#item.FeeAmount</td>
<td>
<button type="button" class="md-button md-button--edit" title="edit">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
</button>
<button type="button" class="md-button md-button--remove" title="remove">
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
</button>
</td>
</tr>
i++;
}
</tbody>
Top level view
#using AccPortal.Models
#using Newtonsoft.Json
#model AccPortal.Models.Members
#{
ViewBag.Title = "Details";
#Styles.Render("~/Content/jqueryui")
}
<h2>Details</h2>
<div>
<h4>Members Details</h4>
<hr />
</div>
<div class="row">
<div class="col-md-7 md-fees" id ="details">
#Html.Partial("_MemberDetails")
</div>
</div>
The problem was with lazy loading in the end.
I was able to get the Fees to repopulate the bound Members.Fees.FeeType by adding
members = db.Members.Find(fees.MemberNo);
members.Fees = db.Fees.Include(x => x.FeeType).Where(x => x.Members.MemberNo.Equals(x.MemberNo)).ToList();
Related
I have a list of products that each one needs the amounts to return and the reason for return. I cannot change the model referenced (as it determines many of the values) How do I get the list of all the inputs to pass to the controller?
I tried to use javascript to send the information through but I have no real idea what I'm doing connecting razor to javascript.
My View:
..#model Snow_System.Models.mvcProductOrderModel
#{
ViewBag.Title = "Take in Order Return";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1 class="page-header pageHeading" style="text-align:center;">Take in Returned Stock</h1>
<hr />
<div class="TopInfoContainer">
<h2 class="SectionHeading">Order Information</h2>
<div class="TopInfoLeft">
<label for="ordID"> Order ID: </label> <span name="ordIDInfo"> #Model.ProductOrderID</span><br />
<label for="ordDate"> Date placed:</label> <span name="ordDate">#Model.DateOfOrder</span>
</div>
<div class="TopInfoRight">
<label for="ordLocation">Order Location:</label> <span name="ordLocation"> #Model.Client.HouseAddress</span><br />
</div>
</div>
<br />
<h2 class="SectionHeading">Product list</h2>
<table class="table">
<tr class="table-primary">
<th>
Name
</th>
<th>
Type
</th>
<th>
Quantity on Hand
</th>
<th>
Amount Ordered
</th>
<th>
Amount to Return
</th>
<th>
Reason for Return
</th>
<th>
Payback Amount
</th>
</tr>
#foreach (var item in Model.ProductOrderLines)
{
<tr class="table-dark" style="cursor:default;">
<td>
#Html.DisplayFor(modelItem => item.Product.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Product.ProductType)
</td>
<td>
#Html.DisplayFor(modelItem => item.Product.QuantityOnHand)
</td>
<td>
#Html.DisplayFor(modelItem => item.QuantityOrdered)
</td>
<td>
<input type="number" class="form-control return-amount" max="#item.QuantityOrdered" min="0" value="0" onchange="CalcSubtotal(#item.ProductID, this, #item.Product.SellingPrice)" />
</td>
<td>
<input type="text" placeholder="Reason for return" class="form-control return-reason" />
</td>
<td id="#item.ProductID">
</td>
</tr>
}
</table>
<div id="AuxilaryOptionsContainer">
</div>
<br />
<br />
<div onclick="document.getElementById('ReturnOrderModal').style.display='block'" class="OptionButton ContinueBtn btn btn-success" style="float:right"><span class="glyphicon glyphicon-ok-circle"></span> <br />Capture Return</div>
<div class="OptionButton BackBtn btn btn-danger" style="position: relative; bottom: 0; left: 0; "><span class="glyphicon glyphicon-remove-circle"></span><br />Cancel</div>
<script>
$(document).ready(function () {
$("#emailSuppliers .btn").click(function () {
$(this).button('toggle');
});
});
function CalcSubtotal(id, input, price) {
document.getElementById(id).innerHTML = "R" + input.value * price + ".00";
}
</script>
<div id="ReturnOrderModal" class="modal animate-opacity">
<div class="modal-content modal-background">
<header class="">
<h2>Confirm Return</h2>
</header>
<hr style="margin:0;" />
<div style="text-align:center; font-size:500%"><span class="glyphicon glyphicon-info-sign" style="margin-top:3%;"></span></div>
<div class="">
<p>Are you sure you want to return this order?</p>
</div>
<hr style="margin:0;" />
<footer class="" style="">
<div onclick="document.getElementById('ReturnOrderModal').style.display='none'" class="btn btn-outline-danger" style="width:25%; margin:2%; margin-top:2%;"><span class="glyphicon glyphicon-remove-circle"></span><br />No</div>
<div class="btn btn-outline-success" style="width:25%; margin:2%; margin-top:2%;"><span class="glyphicon glyphicon-ok-circle"></span><br />Yes</div>
</footer>
</div>
</div>
My Models
Product order line:
namespace Snow_System.Models
{
using System;
using System.Collections.Generic;
public partial class mvcProductOrderLineModel
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public mvcProductOrderLineModel()
{
this.ProductOrderReturnLines = new HashSet<mvcProductOrderReturnLineModel>();
}
public double Selling_Price { get; set; }
public int QuantityOrdered { get; set; }
public int QuantityDelivered { get; set; }
public System.DateTime OrderDate { get; set; }
public int ProductID { get; set; }
public int ProductOrderID { get; set; }
public virtual mvcProductModel Product { get; set; }
public virtual mvcProductOrderModel ProductOrder { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<mvcProductOrderReturnLineModel> ProductOrderReturnLines { get; set; }
}
}
Product order
namespace Snow_System.Models
{
using System;
using System.Collections.Generic;
public partial class mvcProductOrderModel
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public mvcProductOrderModel()
{
this.ProductOrderLines = new HashSet<mvcProductOrderLineModel>();
}
public int ProductOrderID { get; set; }
public System.DateTime DateOfOrder { get; set; }
public int ProductOrderStatusID { get; set; }
public int Client_ID { get; set; }
public virtual mvcClientModel Client { get; set; }
public virtual mvcProductOrderStatuModel ProductOrderStatu { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<mvcProductOrderLineModel> ProductOrderLines { get; set; }
}
}
Product order returns
namespace Snow_System.Models
{
using System;
using System.Collections.Generic;
public partial class mvcProductOrderReturnLineModel
{
public int ProductOrderID { get; set; }
public int ProductOrderReturnID { get; set; }
public int Quantity { get; set; }
public string ReasonForReturn { get; set; }
public int ProductID { get; set; }
public virtual mvcProductOrderLineModel ProductOrderLine { get; set; }
public virtual mvcProductOrderReturnModel ProductOrderReturn { get; set; }
}
}
I need inputs and the productID (Model.Product.ID) in the table to be passed to the controller.
Start by doing a for loop
#for (int i =0;i<Model.ProductOrderLines.Count;i++)
Then use the index to display and textbox
#Html.DisplayFor(modelItem => Model.ProductOrderLines[i].Product.Name)
#Html.TextboxFor(modelItem => Model.ProductOrderLines[i].QuantityOrdered)
When you receive the data back to the controller, it'll know under which index all the data are for (see for yourself by doing a view source). You can also receive a FormCollection collection and call TryUpdateModel to update the model.
You can use #Html.HiddenFor as solution of your purpose. Here is the steps to resolved your problem. You may take a look as your solution.
Model Class (Product Model And ProductItem)
public class ProductItem
{
public string Name { get; set; }
public string Type { get; set; }
public int QuantityOnHand { get; set; }
public int AmountOrdered { get; set; }
public int AmountToReturn { get; set; }
public string ReasonForReturn { get; set; }
public int PaybackAmount { get; set; }
}
public class ProductModel
{
public ProductModel()
{
ProductItems = new List<ProductItem>();
}
public List<ProductItem> ProductItems { get; set; }
}
Controller (ProductController)
public class ProductController : Controller
{
[HttpGet]
public IActionResult Product()
{
ProductModel productModel = GetProductModels();
return View(productModel);
}
[HttpPost]
public IActionResult Product(ProductModel productModel)
{
int productCount = productModel.ProductItems.Count;
return View(productModel);
}
private ProductModel GetProductModels()
{
ProductModel productModel = new ProductModel();
productModel.ProductItems.Add(new ProductItem() { Name = "Product_1", Type = "A", QuantityOnHand = 50, AmountOrdered = 20, AmountToReturn = 5, PaybackAmount = 110, ReasonForReturn = "Looking for something else!"});
productModel.ProductItems.Add(new ProductItem() { Name = "Product_2", Type = "B", QuantityOnHand = 50, AmountOrdered = 20, AmountToReturn = 5, PaybackAmount = 110, ReasonForReturn = "Looking for something else!" });
productModel.ProductItems.Add(new ProductItem() { Name = "Product_3", Type = "C", QuantityOnHand = 50, AmountOrdered = 20, AmountToReturn = 5, PaybackAmount = 110, ReasonForReturn = "Looking for something else!" });
productModel.ProductItems.Add(new ProductItem() { Name = "Product_4", Type = "D", QuantityOnHand = 50, AmountOrdered = 20, AmountToReturn = 5, PaybackAmount = 110, ReasonForReturn = "Looking for something else!" });
return productModel;
}`enter code here`
}
View (Product.cshtml)
you need add hidden html helper inside the for loop. Your contain should be under BeginForm helper. Use a button and type should be Summit. After clicking on the button, you will see the all list at controller post action.
#for (int i = 0; i < Model.ProductItems.Count; i++)
#Html.DisplayFor(p => p.ProductItems[i].Name, new { #id = "ItemName_" + i })
#Html.HiddenFor(p => p.ProductItems[i].Name, new { #id = "ItemName_" + i })
</td>
You can add more td as per your need. Here is just one
I am new to mvc architecture. I want to create a view with a form to store data to db and a division to show the details using a query.This view is using a viewmodel- Add_session_ViewModel.
The issue is that if I am including the viewmodel to view there is an error in display section and if I am including the list there is a error in form.
The codes are as follows:
CONTROLLER:
public ActionResult Add_session()
{
//display data
var query =( from a in db.Session_details_feedback
join b in db.Employee_Details_Feedback on a.Trainer_id equals b.Emp_id
select new
{
a.Session_date,
a.Session_name,
b.Emp_name
} ).ToList();
foreach (var item in query)
{
List<Add_session_ViewModel> sessionList = new List<Add_session_ViewModel>
{
new Add_session_ViewModel { Session_name=item.Session_name,Session_date=item.Session_date,emp_name=item.Emp_name}
};
ViewData.Model = sessionList;
return View(ViewData.Model);
}
VIEWMODEL:
public class Add_session_ViewModel : DbContext
{
public string Session_name { get; set; }
public int Trainer_id { get; set; }
public System.DateTime Session_date { get; set; }
public string emp_name { get; set; }
public IList<Add_session_ViewModel> Session_List { get; set; }
}
VIEW:
#using (Html.BeginForm("Add_session", "Home", FormMethod.Post, new { #class = "form-horizontal" }))
{
<div class="form-group">
#Html.TextBoxFor(x => x.Session_name, new { #class = "form-control", placeholder = " Enter Session name" })
</div>
<div class="form-group">
#Html.TextBoxFor(x => x.Session_date, new { #class = "form-control", placeholder = " Enter Session date" })
</div>
<div class="form-group">
#Html.DropDownListFor(x => x.Trainer_id, ViewBag.TrainerList as IEnumerable<SelectListItem>, "Select Trainer")
</div>
<div class="form-group">
<input id="add" type="submit" value="ADD" />
</div>
}
</div>
</div>
</center>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Session</th>
<th>Trainer</th>
</tr>
</thead>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Session_date)
</td>
<td>
#Html.DisplayFor(modelItem => item.Session_name)
</td>
<td>
#Html.DisplayFor(modelItem => item.emp_name)
</td>
</tr>
}
</table>
I have been looking for a solution for days, still didnt get any.
It would be appreciative if anyone can give me a solution.
Thank you.
my viewmodel is :
public class Add_session_ViewModel
{
public string Session_name { get; set; }
public int Trainer_id { get; set; }
public System.DateTime Session_date { get; set; }
public string emp_name { get; set; }
}
the view use all these properties through a form.
At the same time I need to get the data using the below query and get it displayed on the same view:
var query =( from a in db.Session_details_feedback
join b in db.Employee_Details_Feedback on a.Trainer_id equals b.Emp_id
select new
{
a.Session_date,
a.Session_name,
b.Emp_name
} ).ToList();
I have no ides how to bind the query and viewmodel to the view at the same time
First of all,
Remove the DbContext as base class to your view model,
public class Add_session_ViewModel
{
public string Session_name { get; set; }
public int Trainer_id { get; set; }
public System.DateTime Session_date { get; set; }
public string emp_name { get; set; }
public IList<Add_session_ViewModel> Session_List { get; set; }
}
Then try to map your LINQ query result to directly list of your view model.
public ActionResult Add_session()
{
Add_session_ViewModel model = new Add_session_ViewModel();
var result =(from a in db.Session_details_feedback
join b in db.Employee_Details_Feedback on a.Trainer_id equals b.Emp_id
select new Add_session_ViewModel //<= Note here
{
Session_date = a.Session_date,
Session_name = a.Session_name,
emp_name = b.Emp_name
}).ToList();
model.Session_List = result;
return View(model); //<= Return model to view instead of "ViewData"
}
And then your view must have a view model of
#model FeedBack_Form.Models.Add_session_ViewModel
And change your foreach loop to
#foreach (var item in Model.Session_List)
I have an Attendance program in which I want to assign Students to AttendanceTakers. I am using a table where the headers are the AttendanceTakers and the rows are Students and each cell has a RadioButton. It is basically a double array of RadioButtons. My problem is I can't get it to post.
My AttendanceTaker class
public class SessionAttendanceTaker
{
public int Id { get; set; }
[ForeignKey("Session")]
public int SessionId { get; set; }
public Session Session { get; set; }
[Display(Name="Attendance Taker")]
[ForeignKey("User")]
public string AttendanceTakerId { get; set; }
[Display(Name = "Attendance Taker")]
public User User { get; set; }
public List<Student> Students { get; set; }
}
And the Student that is in the course class
public class StudentSession
{
public int Id { get; set; }
[ForeignKey("Session")]
[DisplayName("Session")]
public int SessionId { get; set; }
public Session Session { get; set; }
[ForeignKey("Student")]
[DisplayName("Student")]
public int StudentId { get; set; }
public Student Student { get; set; }
[DisplayName("Credits Awarded")]
public int Credit { get; set; }
}
Student class
public class Student
{
public int Id { get; set; }
[ForeignKey("User")]
public string UserId { get; set; }
[DisplayName("Name")]
public virtual User user { get; set; }
public Student()
{
}
}
The View
#using (Html.BeginForm())
{
<div class="form-horizontal">
<table>
<thead>
<tr>
<th> Name </th>
#{
foreach (var attendanceTaker in Model.SessionAttendanceTakers)
{
<th>#attendanceTaker.User.LastName, #attendanceTaker.User.FirstName </th>
}
}
</tr>
</thead>
<tbody>
#{
//See https://stackoverflow.com/questions/7667495/mvc-radiobuttons-in-foreach to try and clean the foreach
foreach (var studentSession in Model.StudentSessions)
{
<tr>
<td>
#studentSession.Student.User.LastName, #studentSession.Student.User.FirstName
</td>
#foreach (var attendanceTaker in Model.SessionAttendanceTakers)
{
#Html.EditorFor(Model => Model.SessionAttendanceTakers, "StudentsToAttendanceTakersModel", "" + studentSession.StudentId, new { htmlAttributes = new { #class = "form-control" } })
}
</tr>
}
}
</tbody>
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Assign" class="btn btn-default" />
</div>
</div>
</div>
}
and EditorTemplate
#model IEnumerable<SessionAttendanceTaker>
#using Attendance.Models
<td>
#Html.RadioButtonFor(model => model, new { htmlAttributes = new { #class = "form-control" } })
</td>
As an aside I would love to get rid of the foreaches as per this post but since I don't know how many attendance takers or students there will be until runtime I can't figure out how to do that besides for just moving them to the Editor and I don't see a point to that.
Also the Controller
[HttpPost]
public ActionResult Assign(StudentsToAttendanceTakersModel model)
{
return RedirectToAction("Index");
}
I have a breakpoint on the return and the attendanceTakers is null and Student sessions has a count of 0.
Additionally, using FormCollection
public ActionResult Assign(FormCollection o)
only gives me the Students who's RadioButton was clicked but not the AttendanceTaker. If more info is needed let me know. Thanks.
EDIT
Model
public class StudentsToAttendanceTakersModel
{
public IEnumerable<StudentSession> StudentSessions { get; set; }
public IEnumerable<SessionAttendanceTaker> SessionAttendanceTakers { get; set; }
public StudentsToAttendanceTakersModel() { }
}
You're creating radio buttons which do not relate to your model, and you're trying to bind them to a complex object (SessionAttendanceTaker) - a radio button posts back a simple value (and you are not even giving the radio buttons a valid value - the 2nd parameter of RadioButtonFor() is the value).
You are editing data, so you should start by creating view models which represent what you want to display in the view.
public class StudentVM
{
public int ID { get; set; }
public string Name { get; set; }
[Required(ErrorMessage = "Please select an attendance taker")]
public int? SelectedAttendanceTaker { get; set; }
}
public class AttendanceTakerVM
{
public int ID { get; set; }
public string Name { get; set; }
}
public class StudentAttendanceTakersVM
{
public List<StudentVM> Students { get; set }
public IEnumerable<AttendanceTakerVM> AttendanceTakers { get; set; }
}
So that your view will be
#model StudentAttendanceTakersVM
....
#using (Html.BeginForm())
{
<table>
<thead>
<tr>
<th>Student</th>
#foreach(var taker in Model.AttendanceTakers)
{
<th>#taker.Name</th>
}
<th></th>
</tr>
</thead>
<tbody>
#for(int i = 0; i < Model.Students.Count; i++)
{
<tr>
<td>
#Model.Students[i].Name
#Html.HiddenFor(m => m.Students[i].ID)
#Html.HiddenFor(m => m.Students[i].Name)
</td>
#foreach(var taker in Model.AttendanceTakers)
{
<td>#Html.RadioButtonFor(m => m.Students[i].SelectedAttendanceTaker, taker.ID, new { #class = "form-control" })</td>
}
<td>#Html.ValidationMessageFor(m => m.Students[i].SelectedAttendanceTaker)</td>
</tr>
}
</tbody>
</table>
<input type="submit" ... />
}
Your GET method will then initialize an instance of you view model and pass it to the view, for example, for a 'Create' method
public ActionResult Create()
{
var students = db.Students.Select(x => new StudentVM
{
ID = x.Id,
Name = x.User.FirstName + " " + x.User.LastName // adjust as required
}).ToList();
var attendanceTakers = db.SessionAttendanceTakers.Select(x => new AttendanceTakerVM
{
ID = x.Id,
Name = x.User.FirstName + " " + x.User.LastName // adjust as required
});
StudentAttendanceTakersVM model = new StudentAttendanceTakersVM
{
Students = students,
AttendanceTakers = attendanceTakers
};
return View(model);
}
And the POST method will be
public ActionResult Create(StudentAttendanceTakersVM model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// loop through model.Students to get the ID of the Student and its selected AttendanceTaker
// initialize the data models and save to the database
return RedirectToAction("Index");
}
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>
}
I have a Razor page that displays a list of meetings. Each meeting has a id which is an attribute of a link assigned to each meeting which when clicked loads another Razor View showing the meeting details. This view calls a childAction which loads a partial page, displaying dropdown containing all available attendees and a <div> which will show a form if new visitors need to be created. The URL of the details page looks like
http://localhost:53533/Meetings/AddVisitors/18
I can select a visitor from the dropdown list and I have a working method that create a new record linking the visitor to the meeting. The methods returns the view having added the record, but the details are not shown in the refreshed table and the meeting id has gone from the page URL.
The method to show the page is:
public ActionResult AddVisitors(int? id)
{
var meetings = db.Meetings.Include("Host").Include("VisitorsMeetings").Where(x => x.id == id).FirstOrDefault();
return View(meetings);
}
The method that creates the visitor/meeting record is:
[HttpPost]
public ActionResult AddVisitors(VisitorList model)
{
VisitorMeeting newVisitorMeeting = new VisitorMeeting
{
MeetingId = model.meetingId,
VisitorId = model.SelectedId,
Arrived = false,
Departed = false,
BadgePrinted = false
};
db.VisitorMeetings.Add(newVisitorMeeting);
db.SaveChanges();
var meetings = db.Meetings.Include("Host").Include("VisitorsMeetings").Where(x => x.id == model.meetingId).FirstOrDefault();
return AddVisitors(model.meetingId);
}
The view is:
#model VisitorManager.Models.Meeting
....
<div>
<h4>Meeting</h4>
<dl class="dl-horizontal">
<dt>#Html.DisplayNameFor(model => model.Host.Name)</dt>
<dd>#Html.DisplayFor(model => model.Host.Name)</dd>
<dt>#Html.DisplayNameFor(model => model.StartTime)</dt>
<dd>#Html.DisplayFor(model => model.StartTime)</dd>
... // more properties of Meeting
</dl>
</div>
<h4>Visitor List</h4>
<div>
<table class="table">
<tr>
<th>Visitor Name</th>
<th>Company</th>
...
<th></th>
</tr>
#foreach (var item in Model.VisitorsMeetings)
{
<tr>
<td>#Html.DisplayFor(modelItem => item.Visitor.VisitorName</td>
<td>#Html.DisplayFor(modelItem => item.Visitor.Company)</td>
<td>#Html.DisplayFor(modelItem => item.Arrived)</td>
....
<td>#Html.ActionLink("Delete", "Delete", new { id = item.VisitorId })</td>
</tr>
}
</table>
</div>
// Add partial view to show drop down of all visitors
#Html.Action("VisitorList", "Meetings")
#section Scripts{
<script type="text/javascript">
var dialog = $('#dialog-mask');
$('#addVisitor').click(
function () {
$('#VisitorModal').load('#Url.Action("Add", "Meetings")');
//$('#VisitorModal').find('form')[0].reset();
//dialog.find('form')[0].reset();
dialog.show();
}
);
$('#cancel').on('click', function () {
dialog.hide();
return false;
});
$('#VisitorModal').on('submit', '#visitorform', function () {
var data = $(this).serialize();
var url = '#Url.Action("AddVisitors", "Meetings")';
var text = $('#title').val() + ' ' + $('#firstname').val() + ' ' + $('#surname').val() + ' (' + $('#company').val() + ')'; // a value from the form that you want as the option text
$.post(url, data, function (response) {
if (response) {
$('#VisitorID').append($('<option></option>').val(response).text(text)).val(response);
} else {
dialog.hide();
// Oops
}
}).fail(function () {
// Oops
dialog.hide();
});
// ... close the modal
//$('#VisitorModal').hide();
//$('#VisitorModal').find('form')[0].reset();
dialog.hide();
return false; // cancel the default submit
});
</script>
}
The PartialView is:
#using VisitorManager.Models
#model VisitorList
#{
Layout = null;
}
#using (Html.BeginForm("AddVisitors", "Meetings"))
{
#Html.AntiForgeryToken()
<hr />
<div class="form-horizontal">
<h4>Add A Visitor</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.Label("Visitor Name", htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-4">
#Html.DropDownListFor(m => m.SelectedId, Model.Data, "- Please select -", htmlAttributes: new { #class = "form-control", #id="VisitorID" })
</div>
<button type="button" id="addVisitor" class="btn btn-success">Create new Visitor</button>
</div>
#Html.HiddenFor(m=>m.meetingId)
<div class="form-group">
<div class="col-md-offset-4 col-md-8">
<input type="submit" value="Add Visitor" class="btn btn-default" />
</div>
</div>
</div>
}
<div id="dialog-mask">
<div id="dialog" style="width:500px; height:auto;">
<div id="VisitorModal"></div>
</div>
</div>
VisitorList Action:
[ChildActionOnly]
public ActionResult VisitorList()
{
var dropDownData = db.Visitors.OrderBy(z => z.Company).ThenBy(y=>y.LastName).ThenBy(x=>x.FirstName)
.Select(d => new SelectListItem
{
Text = d.Title + " " + d.FirstName + " " + d.LastName + " (" + d.Company + ")",
Value = d.id.ToString()
});
var model = new VisitorList
{
Data = dropDownData,
meetingId = 18
};
return PartialView(model);
}
VisitorList viewmodel:
public class VisitorList
{
public IEnumerable<SelectListItem> Data { get; set; }
public int SelectedId { get; set; }
public int meetingId { get; set; }
}
Meeting model:
public class Meeting
{
public int id { get; set; }
[Display(Name="Start Time", Prompt = "Please supply a start date and time.")]
[Required(ErrorMessage = "Please supply a start date and time.")]
[DataType(DataType.DateTime)]
public DateTime StartTime { get; set; }
.... // more properties
[Required]
[ForeignKey("Host")]
[Display(Name="Meeting Host")]
public int HostId { get; set; }
[Display(Name="Meeting Host")]
public virtual Host Host { get; set; }
public ICollection<VisitorMeeting> VisitorsMeetings { get; set; }
}
Visitor Model:
public class Visitor
{
public int id { get; set; }
[Display(Name="Visitor Title")]
[Required]
public string Title { get; set; }
[Display(Name="Visitor Firstname")]
[Required]
public string FirstName { get; set; }
[Display(Name="Visitor Lastname")]
[Required]
public string LastName { get; set; }
[Display(Name="Company Name")]
[Required]
public string Company { get; set; }
[NotMapped]
[Display(Name="Visitor Name")]
public string VisitorName
{
get
{
return Title + " " + FirstName + " " + LastName;
}
}
public ICollection<VisitorMeeting> VisitorsMeetings { get; set; }
}
VisitorMeeting Model:
public class VisitorMeeting
{
public int Id { get; set; }
[Display(Name="Visitor Arrived")]
[Required]
public bool Arrived { get; set; }
.... // more properties
[Required]
[ForeignKey("Meeting")]
public int MeetingId { get; set; }
public virtual Meeting Meeting{ get; set; }
[Required]
[ForeignKey("Visitor")]
public int VisitorId { get; set; }
public virtual Visitor Visitor { get; set; }
}
Can anyone tell me How I keep the meeting ID in the AddVisitors URL and why the complete added visitor row isn't correctly showing in the list of visitors displayed in the partial view called by the child action?
There is several options to do so.
I advise you to use post-redirect-get pattern as it will safe for user to refresh page any time:
[HttpPost]
public ActionResult AddVisitors(VisitorList model)
{
// ...
return this.Redirect("AddVisitors", new { id = model.meetingId});
}
Another way is to generate appropriate post url. Look like you use default route mapping, so valid url should be generated:
#using (Html.BeginForm("AddVisitors", "Meetings", new { id = Model.meetingId }))