I have a question. I have a 3 models (Person, RegisteredAddress and AddressCorrespondence) The RegisterAddress and AddressCorrespondence are exactly the same table (with the same names of columns). The Person model is :
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName{ get; set; }
public DateTime BirthDate{ get; set; }
public bool TheSameAddress{ get; set; }
The model of Addresses
public int Id { get; set; }
public string Street{ get; set; }
public int Number{ get; set; }
public string Code{ get; set; }
public string State{ get; set; }
public string City{ get; set; }
public int Person_Id { get; set; }
And then I have a 3 views. Evry view are a form to fill which respons evry model. In first view we can mark the checkBox value of "TheSameAddress" property.
What I want to do is: When the user mark this checkbox then i'd like to open only a view with a RegisteredAddress but i'd like to save the same data into
AddressCorrespondence table.
It's mean that when the checkbox is marked then should be only shown a 2nd form (after 1st) but the 3rd view/form couln't be display but in the database the date should be save.
Does anybody havy any idea how can i make it/solve my problem ?
Option 1
if I understood you can do it like :
in the view :
Person Register View
form design is as you want, you can hide or show the third form if the checkbox is enabled or not
#model WebApplication2.Models.Person
#{
ViewBag.Title = "Register Person";
}
#using (Html.BeginForm())
{
<div class="col-lg-12"><br /></div>
<label>FirstName</label>
#Html.TextBoxFor(m=>m.FirstName)
<div class="col-lg-12"><br /></div>
<label>LastName</label>
#Html.TextBoxFor(m => m.LastName )
<div class="col-lg-12"><br /></div>
<label>The Same Address?</label>
#Html.CheckBoxFor(m=>m.TheSameAddress)
<div class="col-lg-12"><br /></div>
<label>City</label>
<input type="text" name="City"id="City" />
<div class="col-lg-12"><br /></div>
<label>Code</label>
<input type="text" name="Code" id="Code" />
<div class="col-lg-12"><br /></div>
<label>Number</label>
<input type="number" name="Number" id="Number" />
<div class="col-lg-12"><br /></div>
<input type="submit" value="Save" />
}
Controller
compare if the checkbox is true adds the data to RegisteredAddress and AddressCorrespondence else just add data to RegisteredAddress
public ActionResult CreatePeople()
{
return View();
}
[HttpPost]
public ActionResult CreatePeople(Person person,string city,string code , int number)
{
if (person.TheSameAddress == true )
{
var newperson = new Person
{
FirstName = person.FirstName,
LastName = person.LastName,
BirthDate= DateTime.Now,
TheSameAddress=person.TheSameAddress
};
db.person.Add(newperson);
db.SaveChanges();
var newRegisterAdress = new RegisteredAddress
{
City = city,
Code = code,
Number = number,
Person = newperson
};
db.RegisteredAddress.Add(newRegisterAdress);
db.SaveChanges();
var newAdressCorrsp = new AddressCorrespondence
{
City = city,
Code = code,
Number = number,
Person = newperson
};
db.AddressCorrespondence.Add(newAdressCorrsp);
db.SaveChanges();
}
else
{
var newperson = new Person
{
FirstName = person.FirstName,
LastName = person.LastName,
BirthDate = DateTime.Now,
TheSameAddress=person.TheSameAddress
};
db.person.Add(newperson);
var newRegisterAdress = new RegisteredAddress
{
City = city,
Code = code,
Number = number,
Person = newperson
};
db.RegisteredAddress.Add(newRegisterAdress);
db.SaveChanges();
}
return View();
}
it is an idea of how you can do can be adapted to the three views if you want
if you want to asign the data in other view
Option 2
register the person first and asign the adress from index view , Index =People
like:
IndexView
#model IEnumerable<WebApplication2.Models.Person>
#{
ViewBag.Title = "People";
}
<table class="table">
<tr>
<th>
<label>Name</label>
</th>
<th>
<label>LastName</label>
</th>
<th>
<label>BirthDate</label>
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.FirstName )
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName )
</td>
<td>
#Html.DisplayFor(modelItem => item.BirthDate)
</td>
<td>
#Html.ActionLink("Add Adress", "CreateAdress", new { Id_Person = item.ID }, htmlAttributes: new { #class = "btn btn-primary btn-xs" })
</td>
</tr>
}
</table>
Controller
public ActionResult CreateAdress(int Id_Person )
{
ViewBag.PersonId = Id_Person;
return View();
}
[HttpPost]
public ActionResult CreateAdress(RegisteredAddress ra ,int Id_Person)
{
var personf = db.person.Find(Id_Person);
if (personf.TheSameAddress == true)
{
var newRegisterAdress = new RegisteredAddress
{
City = ra.City ,
Code = ra.Code,
Number = ra.Number,
Person = personf
};
db.RegisteredAddress.Add(newRegisterAdress);
db.SaveChanges();
var newAdressCorrsp = new AddressCorrespondence
{
City = ra.City,
Code = ra.Code,
Number = ra.Number,
Person = personf
};
db.AddressCorrespondence.Add(newAdressCorrsp);
db.SaveChanges();
}
else
{
var newRegisterAdress = new RegisteredAddress
{
City = ra.City,
Code = ra.Code,
Number = ra.Number,
Person = personf
};
db.RegisteredAddress.Add(newRegisterAdress);
db.SaveChanges();
}
return RedirectToAction("People");
}
I leave a link with the project here
Related
How I can solve this error :
Value cannot be null
this is the Model :
public class Orders_Tables
{
// internal IQueryable<object> result { get; set; }
public Lab_Orders LabOrders { get; set; }
public Lab_orders_Cash LabOrdersCash { get; set; }
public Lab_Sample_status LabOrderStatus { get; set; }
public IEnumerable<LAB_RESULTS> LabResults { get; set; }
public IEnumerable<TestsRanges> testsRanges { get; set; }
public IEnumerable<TestsUnits> testUnits { get; set; }
public IEnumerable<LAB_CULTURE_RESULT> LabCultureResults { get; set; }
public IEnumerable<LAB_MICRO_NEGATIVE_RESULT> LabMicroResults { get; set; }
public IEnumerable<LabTests> labtests { get; set; }
public IEnumerable<LAB_RESULTS_CLINIC_VIEW> labClinicsViewResult { get; set; }
public IEnumerable<LAB_RESULT_CASH_VIEW> labCashView { get; set; }
public IEnumerable<LAB_PARASITOLOGY_VIEW> LabParasitologyView { get; set; }
public IEnumerable<LAB_CULTURES_VIEW> LabCulturesView { get; set; }
public IEnumerable<LAB_MICRO_VIEW> LabMicroView { get; set; }
public IEnumerable<LAB_HISTOPATHOLOGY_VIEW> LabHistopathologyView { get; set; }
public IEnumerable<LAB_HISTO_RESULT> LabHistoResult { get; set; }
public IEnumerable<LAB_PARA_RESULTS> LabParaResult { get; set; }
public Lab_Hematology_Samples LabSamples { get; set; }
public IEnumerable<LAB_URINE_ANALYSIS_RESULT> LabUrineResult { get; set; }
public IEnumerable<LAB_URINE_RESULT_VIEW> LaburineView { get; set; }
public Patients patients { get; set; }
public IEnumerable<Customers> customers { get; set; }
}
This is the view I used the Model.order_table :
#model IEnumerable<AljawdahNewSite.Models.Orders_Tables>
#{
ViewBag.Title = "Clinics Orders";
//Layout = "~/Views/Shared/_LayoutPatients.cshtml";
}
<link rel="stylesheet" href=#Url.Content("/Content/bootstrap.min.css")>
<link rel="stylesheet" href=#Url.Content("/Content/style.css")>
<li><img width="500" height="100" style="margin-left:300px" src="~/Contents/images/mamlakaheader.png" /> </li>
<li style="margin-left:1200px">Logout - تسجيل الخروج </span></li>
<li style="margin-left:500px"> #Html.ActionLink("Welcome " + Session["UserFullname"], "Clinicsorders", "Orders") </li>
<h3 style="text-align:center;color:coral;padding:10px;margin-left:500px">Orders List</h3>
<style> a {color: brown;text-decoration: wavy;text-shadow: 20px;font-size:medium;} a:hover {color: blue;}</style>
#using (Html.BeginForm("Clinicsorders", "Orders", FormMethod.Get))
{
<div class="vertical-top">
<span id="FromDate" style="margin-left:60px;color:red;font-size:large"><b>From Date</b></span><input id="FromDatetext" style="margin-left:10px" type="text" name="startdate" />
<span id="ToDate" style="margin-left:95px;color:red;font-size:large"><b>To Date</b></span><input id="ToFatetext" type="text" style="margin:10px" name="enddate" /><br /><br />
<span id="PCfileno" style="color:red;font-size:large"><b>Polyclinic File No</b></span><input id="PCFILENOTEXT" style="margin-left:10px" type="text" name="pcfileno" />
<span id="IDNO" style="color:red;font-size:large;margin-left:30px"><b>ID or Iqama No</b></span><input id="idnotext" type="text" style="margin:10px" name="idno" />
<span id="passport" style="color:red;font-size:large"><b>Passport No</b></span><input id="passporttext" style="margin-left:10px" type="text" name="Passport" />
<span id="mobile" style="color:red;font-size:large;margin-left:30px"><b>Mobile No</b></span><input id="mobiletext" type="text" style="margin:10px" name="Mobile" />
<input id="Submit1" type="submit" value="Search" class="btn btn-dark" />
</div>
}
<table class="table">
<tr>
<td> Patient Name </td>
<td> Order No. </td>
<td> Test Id</td>
<td> order date </td>
<td> Polyclinic File </td>
<td> Order Status </td>
</tr>
#foreach (var item in Model)
{
<tr>
<td>#item.patients.Patient_Name</td>
<td>#item.LabOrders.ORDER_ID</td>
<!--Here is one-->
<td>#(item.LabCultureResults?.FirstOrDefault()?.TESTID.HasValue ? item.LabCultureResults?.FirstOrDefault()?.TESTID : "No Test ID" )</td>
<td>#item.LabOrders.ORDER_DATE</td>
<td>#item.patients.PCfileNO</td>
<td>#item.LabOrderStatus.status_name</td>
<td>#Html.ActionLink("Result", "MasterDetails", new { id = item.LabOrders.ORDER_ID },new { #class = "btn btn-primary" , target = "_blank" })</td>
<td>#Html.ActionLink("Invoice", "Index", "Invoice", new { id = item.LabOrders.ORDER_ID }, new { #class = "btn btn-primary", target = "_blank" })</td>
#if (item.LabCultureResults?.FirstOrDefault()?.TESTID)
{
<!--Here is two (Do not render if TESTID has not a value)-->
<td>#Html.ActionLink("Culture Result", "download_culture", new { id = item.LabOrders.ORDER_ID, testid = item.LabCultureResults.FirstOrDefault().TESTID }, new { #class = "btn btn-primary", target = "_blank" })</td>
}
#*<td>#Html.ActionLink("Culture Result", "download_culture", new { id = item.LabOrders.ORDER_ID,testid = item.LabCultureResults.FirstOrDefault().TESTID }, new { #class = "btn btn-primary", target = "_blank" })</td>*#
</tr>
}
</table>
The error appeared on this line when I added TESTID
<td>#item.LabCultureResults.FirstOrDefault().TESTID</td>
How to show TESTID if available and skip if null ?
This is the controller code :
public ActionResult Clinicsorders(DateTime? startdate,DateTime? enddate, string pcfileno, string idno , string Passport , string Mobile)
{
int custId = (int)Session["UserCustid"];
// eager load navigation properties
// query is IQueryable
var query = db.Lab_Orders.Where(r => r.CUSTID == custId)
.Join(db.Lab_Sample_status, order => order.order_status, status => status.status_id, (order, status) => new { Order = order, Status = status })
.Join(db.Patients, rec => rec.Order.patient_no, patient => patient.Patient_No, (rec, patient) => new Orders_Tables { LabOrders = rec.Order, LabOrderStatus = rec.Status, patients = patient });
if (startdate.HasValue && enddate.HasValue)
{
query = query.Where(r => DbFunctions.TruncateTime(r.LabOrders.ORDER_DATE) >= DbFunctions.TruncateTime(startdate.Value) &&
DbFunctions.TruncateTime(r.LabOrders.ORDER_DATE) <= DbFunctions.TruncateTime(enddate.Value));
}
if (!string.IsNullOrEmpty(pcfileno))
{
query = query.Where(r => r.patients.PCfileNO == pcfileno);
}
if (!string.IsNullOrEmpty(idno))
{
query = query.Where(r => r.patients.Patient_id == idno);
}
var model = query.OrderByDescending(r=>r.LabOrders.ORDER_ID).ToList();
return View(model);
}
I need to make changes on the controller code and add the table , the orders table class include all the tables in one model together
Sample code demonstrating the problem:
List<string> LabCultureResults = null;
Console.WriteLine(LabCultureResults.Count); // (1)
Console.WriteLine(LabCultureResults.FirstOrDefault()); // (2)
(1) throws the infamous NullReferenceException ("Object reference not set to an instance of an object.").
(2) throws an ArgumentNullException with message "Value cannot be null. (Parameter 'source')", because an extension method (like FirstOrDefault) can be called on a null value, but may decide not to accept it.
So your LabCultureResults is null.
When that Model is the result of an Entity Framework query, you need to .Include(it => it.LabCultureResults).
Be aware that FirstOrDefault() may return a null, so don't just get the TESTID of the result - that will throw a NullReferenceException when the result is null. So use item.LabCultureResults?.FirstOrDefault()?.TESTID.
I catch another issue in your code. You have two accessors in page for TESTID. They marked on code below. You have to handle both accessors. May be your item or LabOrdersis null. If not, the code below can does the job.
If condition depends on its own type.
For example if TESTID is int? you can use this:
item.LabCultureResults?.FirstOrDefault()?.TESTID.HasValue
<table class="table">
<tr>
<td> Patient Name </td>
<td> Order No. </td>
<td> Test Id</td>
<td> order date </td>
<td> Polyclinic File </td>
<td> Order Status </td>
</tr>
#foreach (var item in Model)
{
<tr>
<td>#item.patients.Patient_Name</td>
<td>#item.LabOrders.ORDER_ID</td>
<!--Here is one-->
<td>#(item.LabCultureResults?.FirstOrDefault()?.TESTID.HasValue ? item.LabCultureResults?.FirstOrDefault()?.TESTID : "No Test ID" )</td>
<td>#item.LabOrders.ORDER_DATE</td>
<td>#item.patients.PCfileNO</td>
<td>#item.LabOrderStatus.status_name</td>
<td>#Html.ActionLink("Result", "MasterDetails", new { id = item.LabOrders.ORDER_ID },new { #class = "btn btn-primary" , target = "_blank" })</td>
<td>#Html.ActionLink("Invoice", "Index", "Invoice", new { id = item.LabOrders.ORDER_ID }, new { #class = "btn btn-primary", target = "_blank" })</td>
#if(item.LabCultureResults?.FirstOrDefault()?.TESTID.HasValue)
{
<!--Here is two (Do not render if TESTID has not a value)-->
<td>#Html.ActionLink("Culture Result", "download_culture", new { id = item.LabOrders.ORDER_ID,testid = item.LabCultureResults.FirstOrDefault().TESTID }, new { #class = "btn btn-primary", target = "_blank" })</td>
}
</tr>
</table>
FirstOrDefault returns....default if it does not exist so dereferencing it will generate a nullreference exception
edit: extra if TESTID does not exist
#item.LabCultureResults.FirstOrDefault().TESTID ?? "EMPTY"
A quick fix is to use the null coalescent operator ( ? )
#item.LabCultureResults.FirstOrDefault()?.TESTID ?? "EMPTY"
OR
#item.LabCultureResults.FirstOrDefault() ?? "EMPTY"
A combination of the above answers:
Try:
#item?.LabCultureResults.FirstOrDefault()?.TESTID ?? ""
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 want to post data to database but my viewmodel is empty when posting it to the controller, i tried different approaches, but everytime my viewmodel is null.
These are my classes :
public class Player
{
[Key]
public int Id { get; set; }
public string Name{ get; set; }
public string PreName{ get; set; }
}
public class Activitity
{
[Key]
public int Id { get; set; }
public string WhichActivity { get; set; }
public List<Player> Players { get; set; }
}
public class Aanwezigheid
{
[Key]
public int Id { get; set; }
public ReasonEnum Reason{ get; set; }
public int PlayerId{ get; set; }
public Player Player{ get; set; }
public List<Player> Players{ get; set; }
public int ActivityId { get; set; }
}
My View Model :
public class PresenceVM
{
public int PlayerId{ get; set; }
public int ActivityId { get; set; }
public string Name{ get; set; }
public string PreName { get; set; }
public ReasonEnum Reason { get; set; }
}
My HTTPGET for a list of players and I want to put the absence reason with the player in the database.
[HttpGet]
public ActionResult Presence(int id)
{
var sp = _context.Players.ToList();
foreach(Players s in sp)
{
var van = new PresenceVM
{
PlayerId = s.Id,
Name = s.Name,
PreName = s.PreName,
ActivityId = id
};
list.Add(van);
}
return View(list);
}
My HttpPost
[HttpPost]
public ActionResult Presence(List<PresenceVM> list)
{
var sp = _context.Players.ToList();
var list = new List<Presence>();
foreach (Players s in sp)
{
var van = new Aanwezigheid
{
PlayerId = s.Id,
ActivityId = vm.ActivityId,
Reason = vm.Reason
};
list.Add(van);
_context.Presence.Add(van);
//_context.SaveChanges();
}
return RedirectToAction("Index", "Presence");
}
The problem is that my PresenceVm (viewmodel) does not get any data in true my controller. I don't understand why? Is it because of a list? With one item it's easy to post it to database. Maybe multiple items?
Edit 1:
The viewmodel for the Get & Post
#model IEnumerable<....ViewModels.PresenceVM>
#{
ViewBag.Title = "Presence";
}
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.PreName)
</th>
<th>
#Html.DisplayName("Reason")
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.PreName)
</td>
<td>
#Html.EnumDropDownListFor(modelItem => item.Reason, "Present", new { #class = "form-control" })
</td>
</tr>
}
</table>
<form action="/Presences/Presence" method="post">
<div class="form-horizontal form-details">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
You're putting the form scope in wrong place (only include submit button). The correct way should include all properties you want to submit with indexes (since you're using IEnumerable<PresenceVM>, like this example:
<form action="/Presences/Presence" method="post">
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.PreName)
</th>
<th>
#Html.DisplayName("Reason")
</th>
</tr>
#for (var i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.EditorFor(modelItem => item[i].Name)
</td>
<td>
#Html.EditorFor(modelItem => item[i].PreName)
</td>
<td>
#Html.EnumDropDownListFor(modelItem => item[i].Reason, "Present", new { #class = "form-control" })
</td>
</tr>
}
</table>
<div class="form-horizontal form-details">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
Note that if you want to allow user input, you need to change all DisplayFor into EditorFor.
Your Presence method is returning an object that does not exist (list).
It must return the view model if you want to use it in the post method.
[HttpGet]
public ActionResult Presence(int id)
{
List<PresenceVM> model = context.Players.Select(u => new PresenceVM
{
PlayerId = s.Id,
Name = s.Name,
PreName = s.PreName,
ActivityId = id
}).ToList();
return View(model);
}
Seems that you don't use the Form tag
VIEW
#model MyViewModel
#using (Html.BeginForm(("Presence"))
{
#Html.AntiForgeryToken()
//divs
<div class="form-horizontal form-details">
<input type="submit" value="Save" class="btn btn-default" />
</div>
}
And it's better to pass a Model than a list of Model
VIEWMODEL
public class MyViewModel{
public IList<PresenceVM> MyList {get;set;}
}
CONTROLLER
public ActionResult Presence(MyViewModel xxx)
{
//whatever
}
Not sure why I got down voted but I'm going to re-write my question after doing some research and testing. This is a side project that I'm using to learn MVC/EF/Repository/Bootstrap etc. I only get a couple hours here a few nights a week to work on it.
Basic original question:
I know I should really be using a List<> in a ViewModel to pass the data to the View but I'm not sure how or if it will meet my requirement.
What I am trying to do is get a list of users to display in a table which would have a checkbox in each row. Above that table I want to have a list of Groups to that they could be assigned to. You select the section from the DropDownList (DDL) and then check who you want to assign it to. It's the groups/sections that I want want to assign as a list and pass to the View.
So, I've got a ViewModel with a list and I'm using a repository to populate the VM. What I don't know how to do exactly is where/when to populate that list with each VM object and even if I do and there are say 50 users I wouldn't want to make 50 trips to the DB to pull the same information.That is why I'm thinking that for this scenario using the ViewBag to pass that Group list to the View might be justifiable. On the flip side I would like to learn how to populate that list properly in the VM for future coding.
Updated question/code:
So, after more research and following some suggestions I've now got the following code. I'm still not sure how I will properly populate my Patrols in my ViewModel in order to populate the DDL in my View.
At the moment I've got the View displaying the table with the checkboxes. Now I'm back to working on getting the values to populate the DDL and then I'll have to work on posting to the controller, looping to find the check rows, and updating the database. In my case each member record is defaulted to a PatrolId=0 and this page should allow me to update the PatrolId to a value from the DDL.
The Patrols property in the PatrolMemberViewModel should be a list of about 5 records that I would pull from a DB table instead of hard coding in the DDL.
ViewModel:
public class PatrolViewModel
{
public int PatrolId { get; set; }
public string PatrolName { get; set; }
}
public class PatrolMemberViewModel
{
[Key]
public int MemberId { get; set; }
public int PatrolId { get; set; }
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Display(Name = "Phone")]
public string PhonePrimary { get; set; }
[Display(Name = "Email")]
public string EmailPrimary { get; set; }
public bool IsSelected { get; set; }
public PatrolViewModel Patrols { get; set; }
}
Controller:
public ViewResult Unassigned()
{
try
{
IEnumerable<PatrolMemberViewModel> model = repository.SelectAllUnassigned();
return View(model);
}
catch (Exception)
{
ModelState.AddModelError(string.Empty, "Error retrieving the record.");
return View();
}
}
Repository:
public IEnumerable<PatrolMemberViewModel> SelectAllUnassigned()
{
using (DataContext db = new DataContext())
{
var results = (from p in db.Person
where p.IsActive == true
&& p.IsScout == true
&& p.PatrolId == 0
select new PatrolMemberViewModel
{
MemberId = p.PID,
FirstName = p.FirstName ?? string.Empty,
LastName = p.LastName ?? string.Empty,
EmailPrimary = p.EmailPrimary ?? string.Empty,
PhonePrimary = p.PhonePrimary ?? string.Empty,
PatrolId = p.PatrolId,
IsSelected = false
}
).OrderBy(o => o.LastName).ThenBy(o => o.FirstName).ToList();
return results;
}
}
View:
#model IList<ProjectName.ViewModels.PatrolMemberViewModel>
#{
ViewBag.Title = "Unassigned";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Patrols</h2>
#using (Html.BeginForm("Update", "Patrol", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(false, "", new { #class = "alert alert-danger" })
<table class="table table-bordered table-striped table-hover table-condensed tbackground">
<tr>
<th class="text-center">
</th>
<th class="text-center">
First Name
</th>
<th class="text-center">
Last Name
</th>
<th class="text-center">
Email
</th>
<th class="text-center">
Phone
</th>
</tr>
#for (var i = 0; i < Model.Count(); i++)
{
<tr>
<td class="text-center">
#Html.CheckBoxFor(m => m[i].IsSelected)
</td>
<td>
#Html.DisplayFor(m => m[i].FirstName)
</td>
<td>
#Html.DisplayFor(m => m[i].LastName)
</td>
<td>
#Model[i].EmailPrimary
</td>
<td class="text-center">
#Html.DisplayFor(m => m[i].PhonePrimary)
</td>
</tr>
}
</table>
<div class="control-wrapper">
<input type="submit" id="btnSubmit" value="Assign" class="btn btn-success" />
</div>
}
<p> </p>
Start by creating the view models to represent what you want to display/edit in the view. Your PatrolMemberViewModel can be used but remove the [Key] attribute and the int PatrolId and PatrolViewModel Patrols properties.
Then create the parent view model
public class AssignPatrolViewModel
{
[Display(Name = "Patrol")]
[Required(ErrorMessage = "Please select a patrol")]
public int? SelectedPatrol { get; set; }
public IEnumerable<SelectListItem> PatrolList { get; set; }
public List<PatrolMemberViewModel> Members { get; set; }
}
and you GET method would be
public ViewResult Unassigned()
{
var model = new AssignPatrolViewModel
{
PatrolList = new SelectList(db.Patrols, "PatrolId", "PatrolName"), // modify to suit
Members = repository.SelectAllUnassigned().ToList()
};
return View(model);
}
and in the view
#model AssignPatrolViewModel
....
#using (Html.BeginForm())
{
#Html.LabelFor(m => m.SelectedPatrol)
#Html.DropDownListFor(m => m.SelectedPatrol, Model.PatrolList, "Please select")
#Html.ValidationMessageFor(m => m.SelectedPatrol)
<table>
.... // thead elements
<tbody>
#for(int i = 0; i < Model.Members.Count; i++)
{
<tr>
<td>
#Html.CheckBoxFor(m => m.Members[i].IsSelected)
#Html.HiddenFor(m => m.Members[i].MemberId)
// add other hidden inputs for properties you want to post
</td>
<td>#Html.DisplayFor(m => m.Members[i].FirstName)</td>
....
</tr>
}
</tbody>
</table>
<input type="submit" value="Assign" class="btn btn-success" />
}
Then in the POST method
[HttpPost]
public ViewResult Unassigned(AssignPatrolViewModel model)
{
if (!ModelState.IsValid)
{
model.PatrolList = new SelectList(db.Patrols, "PatrolId", "PatrolName");
return View(model);
}
// Get selected ID's
IEnumerable<int> selected = model.Members.Where(m => m.IsSelected).Select(m => m.MemberId);
// Get matching data models
var members = db.Person.Where(p => selected.Contains(p.PID)); // modify to suit
// loop each each member, update its PatrolId to the value of model.SelectedPatrol
// save and redirect
}
You could create a new class for your view model, with the list of users and the list of sections as properties within it. Some people seem to like that approach.
But I think your use of ViewBag for passing the list of sections is perfectly valid. I do that all the time for DDLs like this.
I have controller for address I used it to enter multiple addresses but I want to create dropdosnlist to select the person and enter his addresses
I create this helper class in my model folder to create select item
public class PersonsSelectItems
{
public int SelectedId { get; set; }
public List<Person> Persons { get; set; }
}
I use AddressController to send the selectitem to it view
public class AddressController : Controller
{
private readonly CustomersDBEntities context = new CustomersDBEntities();
private PersonsSelectItems personsSelectItems= new PersonsSelectItems();
///get list of persones
///
public List<Person> GetPersonsList()
{
return (from c in personsSelectItems.Persons
select c).ToList();
}
//
// GET: /Address/
public ActionResult Index()
{
//var model = GetPersonsList(); //var model = GetPersonsList().Select(x => new SelectListItem
//{
// Value = x.PersonID.ToString(),
// Text = x.FirstName,
// Selected = true | false
//});
///var model = new PersonsSelectItems { Persons = GetPersonsList() };
var model = GetPersonsList();
return View(model);
}
//
// GET: /Address/Welcome/
public string Welcome()
{
return "This is the Welcome action method...";
}
[HttpPost]
public ActionResult Create(Address address)
{
//Loop through the request.forms
var Addesslist = new List<Address>();
for (int i = 1; i <= Request.Form.Count; i++)
{
var street = Request.Form["street_0" + i + ""];
var city = Request.Form["city_0" + i + ""];
var postalCode = Request.Form["postalCode_0" + i + ""];
var province = Request.Form["province_0" + i + ""];
var personID = 1;
if (street != null && city != null && postalCode != null && province != null)
{
try
{
context.Addresses.Add(new Address
{
Street = street,
City = city,
Province = province,
PostalCode = postalCode,
PersonID = personID
});
context.SaveChanges();
}
catch (Exception exc)
{
}
}
else
{
break;
}
}
return RedirectToAction("Index");
}
}
I get this expsetion
Value cannot be null. Parameter name: source
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: source
Adress view
#model MVC.Models.Address
Tuple<Person,Order>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm("Create", "Address", FormMethod.Post))
{
#Html.DropDownListFor(x => x.Person, new SelectList(Model.Person, "PersonId", "FirstName"))
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="table-responsive">
<table id="address_table" class="table">
<thead>
<tr>
<th>Street</th>
<th>City</th>
<th>Province</th>
<th>PostalCode</th>
<th> </th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input id="Text1" type="text" name="street_01" maxlength="255" required class="street" /></td>
<td>
<input id="Text2" type="text" name="city_01" maxlength="255" required class="city" /></td>
<td>
<input id="Text3" type="text" name="province_01" maxlength="255" required class="province" /></td>
<td>
<input id="Text4" type="text" name="postalCode_01" maxlength="7" required class="postalCode" /></td>
<td> </td>
</tr>
</tbody>
</table>
</div>
<input type="button" value="Add Row" id="add_AdressRow" class="btn btn-lg btn-success btn-block" />
<p>
<input type="submit" value="Create" />
</p>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
just want to ask how can I bind dropDwonList using list from GetPersonsList() function
from AdressController and bind I now their are away to do it but I could not find it ?
Your problem is that you're trying to use some LINQ over a null list.
This bad boy here:
public List<Person> Persons { get; set; }
Is null. You can add a constructor to your type to initialize it:
public class PersonsSelectItems
{
public int SelectedId { get; set; }
public List<Person> Persons { get; set; }
public PersonsSelectItems() {
Persons = new List<Person>();
}
}
..and that will stop your current error.
I have to point out a couple of things though. Firstly, the naming Persons is strange. Make it an English plural of People.
Secondly, you don't actually have to use LINQ here. Your GetPersonList method can simply be:
public List<Person> GetPersonsList()
{
return personsSelectItems.Persons;
}
Even then.. you have access to that collection already. So your model assignment can be:
var model = _personsSelectItems.Persons;