I'm having some troubles with Models and ViewModels.
I need list all bills and the users in the same view. (users in a dropdown list)
Also: When to use IEnumerable<T>? Because depending I change the view change the error message.
Model
public class Bill
{
public int Id { get; set; }
public string Title { get; set; }
public DateTime Date { get; set; }
public string Category { get; set; }
public double Amount { get; set; }
public Card Card { get; set; }
public int CardId { get; set; }
}
ViewModel
public class UserBills
{
public IEnumerable<ApplicationUser> User { get; set; }
public Bill Bill { get; set; }
}
View
#*#model IEnumerable<Nucontrol.Models.Bill>*#
#model IEnumerable<Nucontrol.ViewModels.UserBills>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Bill.Card.Number)
</td>
<td>
#Html.DisplayFor(modelItem => item.Bill.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.Bill.Date)
</td>
<td>
#Html.DisplayFor(modelItem => item.Bill.Category)
</td>
<td>
#Html.DisplayFor(modelItem => item.Bill.Amount)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Bill.Id }) |
#Html.ActionLink("Details", "Details", new { id = item.Bill.Id }) |
#Html.ActionLink("Split", "Split", new { id = item.Bill.Id }, new { data_target = "#myModal", data_toggle = "modal" })
</td>
</tr>
}
<!-- List all users -->
#Html.DropDownListFor(m => User.Identity.Name, new SelectList(User.Identity.Name, "Id", "Name"), "", new { #class = "form-control" })
Controller
public ActionResult Index()
{
var users = _context.Users.ToList();
var bills = _context.Bills.ToList();
var viewModel = new UserBills
{
User = users
};
return View(viewModel);
}
You have some issues in sample code provided:
1) The DropDownListFor uses User.Identity.Name property as model binding, which actually derived from IIdentity.Name which is getter-only property. Declare another property with setter available which holds user ID in your viewmodel.
2) Passing UserBills viewmodel into view which bound to IEnumerable<UserBills> model may cause InvalidOperationException. You need to use either passing IEnumerable<UserBills> from controller or define #model UserBills.
3) I suggest you use IEnumerable<SelectListItem> to create DropDownListFor items from IEnumerable<ApplicationUser> generated by identity data context and pass it to view (see also IdentityUser properties).
Here is initial solution based from my thought:
Model
public class UserBills
{
public int UserId { get; set; }
public IEnumerable<SelectListItem> Users { get; set; }
public IEnumerable<Bill> Bills { get; set; }
}
Controller
public ActionResult Index()
{
var users = _context.Users.ToList();
var bills = _context.Bills.ToList();
var viewModel = new UserBills
{
Users = users.Select(x => new SelectListItem() { Value = x.Id.ToString(), Text = x.UserName.ToString() }),
Bills = bills
}
return View(viewModel);
}
View
#model Nucontrol.ViewModels.UserBills
#foreach (var item in Model.Bills)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Card.Number)
</td>
<!-- other properties -->
</tr>
}
#Html.DropDownListFor(m => m.UserId, Model.Users, "", new { #class = "form-control" })
NB: Since you're getting selected user ID from viewmodel binding, it is possible to create HttpContext.User instance and setting User.Identity.Name property from that ID.
Related
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");
}
I am trying add a model with respect to users (ApplicationUser) and for that i created a model my problem is the data is not displaying in view (username for particular post)
Model
public class OrganizationViewModel
{
public ApplicationUser AppUser { get; set; }
public int OrganizationId { get; set; }
public string Name { get; set; }
}
Controller
public ActionResult Create(OrganizationViewModel organizationviewmodel)
{
var getuserid = User.Identity.GetUserId();
var userid = db.Users.Single(s => s.Id == getuserid);
var organizationvmodel = new Organization
{
AppUser = userid,
Name = organizationviewmodel.Name
};
db.Organizations.Add(organizationvmodel);
db.SaveChanges();
return RedirectToAction("Index");
}
But in view which means index.cshtml when i try to populate it as a list its empty just like this output image
View
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.AppUser.UserName)
#ViewBag.UserName
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.OrganizationId }) |
#Html.ActionLink("Details", "Details", new { id = item.OrganizationId }) |
#Html.ActionLink("Delete", "Delete", new { id = item.OrganizationId })
</td>
</tr>
}
Please give me a good solution because i am very new to asp.net mvc
I have a radio button list with an IList View Model in C# MVC 5. My ViewModel values are passed to controller Action Result method.
However, the webpage allows the user to select multiple radio buttons. What I need to how do I select individual button for my list items (one at a time).
Here's the screen for selected radio buttons:
Here's my ViewModel:
public class DeliveryDateVM
{
public int Id { get; set; }
public bool SelectedItem { get; set; }
public string DeliveryDay { get; set; }
public string DeliveryType { get; set; }
}
Here's my View:
#model IList<ViewModels.DeliveryDateVM>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#for (var i = 0; i < Model.Count; i++) {
#Html.HiddenFor(x => x[i].Id)
#{var uniqueID = Model[i].Id;}
<tr>
<td>
#{var uniqueID = Model[i].Id;}
#Html.RadioButtonFor(model => Model[i].SelectedItem, true, new { id = uniqueID })
</td>
<td>
#Html.DisplayFor(x => x[i].DeliveryType)
#Html.HiddenFor(x => x[i].DeliveryType)
</td>
<td>
#Html.DisplayFor(x => x[i].DeliveryDay)
#Html.HiddenFor(x => x[i].DeliveryDay)
</td>
</tr>
}
<button type="submit" class="btn btn-primary">Submit</button>
}
The controller values pass screen:
Here's my GET Controller:
public ActionResult DeliveryDates()
{
var model = db.DeliveryPeriods
.Select(c =>
new DeliveryDateVM()
{
Id = c.Id,
DeliveryDay = c.DeliveryDay,
DeliveryType = c.DeliveryType,
}).ToList();
return View(model);
}
Radio buttons need to be grouped by name and your giving each radio button a different name attribute.
Change you view models to
public class MainVM // rename as required
{
public string SelectedDay { get; set; }
public List<DeliveryDateVM> Days { get; set; }
}
public class DeliveryDateVM
{
public int Id { get; set; }
public string DeliveryDay { get; set; }
public string DeliveryType { get; set; }
}
so that you view is
#model MainVM
....
#for (var i = 0; i < Model.Days.Count; i++)
{
#Html.RadioButtonFor(m => m.SelectedDay, Model.Days[i].DeliveryDay, new { id = "" })
#Html.DisplayFor(m => m.Days[i].DeliveryType)
#Html.HiddenFor(m => m.Days[i].DeliveryType)
....
}
This will now generate all radio buttons with name="SelectedDay" and the value of SelectedDay when you post back to your model will be the value of the DeliveryDay (i.e. "Monday" or "Tuesday" etc)
Side note: You may want to consider changing the DeliveryDay and SelectDay properties to a DayOfWeek enum and also create your own enum for DeliveryType.
Based on your comments, the revised get method would be
MainVM model = new MainVM
{
SelectedDay = "Monday", // set this if you want a default button selected
Days = db.DeliveryPeriods.Select(c => new DeliveryDateVM()
{
Id = c.Id,
DeliveryDay = c.DeliveryDay,
DeliveryType = c.DeliveryType,
}).ToList()
};
return View(model);
I have a ViewModel which contains a collection of type of my Model, like so:
public class OrderConfirm
{
public ICollection<QuoteLine> SalesLines { get; set; }
public string Currency { get; set; }
public int EnquiryID { get; set; }
}
My QuoteLine Model looks like so:
public class QuoteLine
{
public int QuoteLineId { get; set; }
public int LostReasonId { get; set; }
public virtual LostReason LostReason { get; set; }
public string ItemName { get; set; }
}
In my View, I then Iterate through each of these QuoteLines, within a form, like so:
#using (Ajax.BeginForm("ConfirmLostOrder", new AjaxOptions()
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "LostOrders",
OnBegin = "LostOrderConfirm"
}))
{
<table class="completed-enq-table">
<tr>
<th>
Item Number
</th>
<th>
Reason
</th>
</tr>
#foreach (var sales in Model.SalesLines)
{
<tr>
<td>#sales.ItemName
#Html.HiddenFor(model => sales.QuoteLineID)
</td>
<td>#Html.DropDownListFor(model => sales.LostReasonId, ((IEnumerable<myApp.Models.LostReason>)ViewBag.LostReasons).Select(option => new SelectListItem
{
Text = (option == null ? "None" : option.LostReason),
Value = option.LostReasonId.ToString(),
Selected = (Model != null) && (option.LostReasonId == sales.LostStatusId)
}))
</td>
</tr>
}
</table>
<input type="submit" style="float: right;" value="Submit Lost Order" />
}
Then my HttpPost action looks like so:
[HttpPost]
public ActionResult ConfirmLostOrder(List<QuoteLine> models)
{
// process order
return PartialView("Sales/_ConfirmLostOrder");
}
The problem is, models is null. If I use a FormCollection I can see each of the values submitted but I'd like to use my model and not a FormCollection as I'd like to process and edit each of the line submitted individually as they may have different reason's
You can't use a foreach in this instance, it needs to be a for loop so that the name attributes of the fields contain the correct index so that default model binding knows it's binding to a list.
Firstly, I'm going to move your dropdown values out of the ViewBag (they should really be in there). That'll also take out some of that nasty logic in your view :)
So your model is now:
public class OrderConfirm
{
public List<QuoteLine> SalesLines { get; set; }
public string Currency { get; set; }
public int EnquiryID { get; set; }
public SelectList LostReasons { get; set; }
}
Try this instead of your foreach:
#for (var i = 0; i < Model.SalesLines.Count; i++)
{
<tr>
<td>
#Model.SalesLines[i].ItemName
#Html.HiddenFor(m => m.SalesLines[i].QuoteLineId)
#Html.HiddenFor(m => m.SalesLines[i].ItemName) //assuming you want this
</td>
<td>
#Html.DropDownListFor(m => m.SalesLines[i].LostReasonId, Model.LostReasons)
</td>
</tr>
}
Then change your post method to take your OrderConfirm model type:
[HttpPost]
public ActionResult ConfirmLostOrder(OrderConfirm model)