I'm having a big issue with my Database, I cannot write to it from the web page. I am using Dapper as my ORM if that makes a difference?
db.Insert(guest.EditableGuest); is giving me the invalid object error but if I change it to db.InsertAsync(guest.EditableGuest); the error resolves itself BUT I cannot get it to add to the DB and it will not be displayed on the page where I have a list of guests in the DB for some reason?
I decided to retype the page again in case there was an issue, VS seems to be a bit finicky sometimes, one day I had an error, closed the app, reopened it and it was fine? Didn't work with this unfortunately.
GuestListController
using Dapper.Contrib.Extensions;
using FYP_RSVP_MGMT.Helpers;
using FYP_RSVP_MGMT.Models;
using FYP_RSVP_MGMT.ViewModels;
using Microsoft.AspNetCore.Mvc;
using System.Linq;
namespace FYP_RSVP_MGMT.Controllers
{
public class GuestListController : Controller
{
public IActionResult Index()
{
GuestListViewModel guest = new GuestListViewModel();
return View("Index", guest);
}
/* Create or Update a guest RSVP Response */
public IActionResult CreateUpdate(GuestListViewModel guest)
{
if (ModelState.IsValid)
{
using (var db = DbHelpers.GetConnection())
{
/* If a guestID is null, the number of existing guests will be counted
* in order to determine what the next guestID will be and will be added
* asynchronously to the DB in case other actions are on going at the same time */
if (guest.EditableGuest.GuestID == null)
{
guest.EditableGuest.GuestID = guest.Guests.Count;
db.Insert<GuestList>(guest.EditableGuest);
}
/* If the guest already exists, we are updating their details */
else
{
GuestList dbItem = db.Get<GuestList>(guest.EditableGuest.GuestID);
TryUpdateModelAsync<GuestList>(dbItem, "EditableGuest");
db.Update<GuestList>(dbItem);
}
}
/* When a guest submits their RSVP response, it will bring them to the View Guests page - TEMPORARY MEASURE */
return RedirectToAction("ViewGuestList", guest);
}
else
{
return View("ViewGuestList", new GuestList());
}
}
There is more code for Edit and Delete beyond this and the page is throwing no errors so that's all fine.
This is my DB Helper class
public class DbHelpers
{
public static SqlConnection GetConnection()
{
return new SqlConnection(#"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=WeddingDB;");
}
}
GuestList Model
public class GuestList
{
[ExplicitKey]
public int? GuestID { get; set; }
[Required]
public string GuestName { get; set; }
[Required]
public string GuestType { get; set; }
[Required]
public string ContactDetails { get; set; }
public bool PlusOne { get; set; }
public string PlusOneName { get; set; }
[Required]
public string GuestResponse { get; set; }
}
GuestListViewModel
public class GuestListViewModel
{
/* Creating a new List of all Guests
Editable Guest will hold any instance of an object from the list
that is being added/edited/deleted etc */
public List<GuestList> Guests { get; set; }
public GuestList EditableGuest { get; set; }
/* Selecting all of the existing guests from the DB */
public GuestListViewModel()
{
using (var db = DbHelpers.GetConnection())
{
this.EditableGuest = new GuestList();
this.Guests = db.Query<GuestList>("Select * From GuestList").ToList();
}
}
}
GuestList View
#model FYP_RSVP_MGMT.ViewModels.GuestListViewModel
#{
ViewData["Title"] = "Guest RSVP";
}
#* Form to submit RSVP Response *#
#using (var form = Html.BeginForm("CreateUpdate", "GuestList", FormMethod.Post))
{
<div class="container" id="GuestRSVP">
<br />
<br />
<br />
#* The Bride and Groom name plus Wedding Details will eventually change
depending on the log in details *#
<h3> Welcome to [Bride] and [Groom]'s Wedding</h3>
<h4>[Church Location]</h4>
<h4>[Wedding Date and Time]</h4>
<div class="container" id="RSVPTable" style="align-content:center">
<table>
<tr>
<td>Guest Name: </td>
<td>#Html.TextBoxFor(m => m.EditableGuest.GuestName)</td>
</tr>
<tr>
<td>Guest Type: </td>
<td>#Html.DropDownListFor(m => m.EditableGuest.GuestType, new List<SelectListItem> { new SelectListItem { Value = "Guest", Text = "Guest" }, new SelectListItem { Value = "Wedding Party", Text = "Wedding Party" } })</td>
</tr>
<tr>
<td>Email Address: </td>
<td>#Html.TextBoxFor(m => m.EditableGuest.ContactDetails)</td>
</tr>
<tr>
<td>Plus One: </td>
<td>#Html.CheckBoxFor(m => m.EditableGuest.PlusOne)</td>
</tr>
<tr>
<td>Plus One Name: </td>
<td>#Html.TextBoxFor(m => m.EditableGuest.PlusOneName)</td>
</tr>
<tr>
<td>RSVP Response:</td>
<td>#Html.DropDownListFor(m => m.EditableGuest.GuestResponse, new List<SelectListItem> { new SelectListItem { Value = "Accept", Text = "Accept with Pleasure" }, new SelectListItem { Value = "Decline", Text = "Decline with Regret" } })</td>
</tr>
</table>
<br/>
<button class="btnSubmitRSVP" type="submit" style="text-align:center"> <a asp-controller="GuestList" asp-action="CreateUpdate"></a> #(Model.EditableGuest.GuestID > 0? "Update": "Submit Response") </button>
</div>
</div>
}
Where I am sending some of the pages is probably a bit odd, like a guest RSVPing shouldn't be taken to the page where they can see the other guests and their response but this is just temporary. Could this be affected my code, surely not? Nowhere in my code is there GuestLists so I cannot understand where it is picking this up? I can provide more code if required.
Thank you for your help in advance!
Edit: Here is a picture of the database
Dapper is assuming a pluralized table name. Use [Table("GuestList")] attribute in your GuestList model class.
Related
I have a form that writes out an Approve/Deny radio button for each record. I'm trying to figure out how to use the HttpPost to loop through each and determine if the radio button is selected and if so, which one was selected.
Doing some research I see that some use the Form collection for the form and in one example I found where the user used the forms ViewModel (which is what I normally do). However, when I try either one I'm coming up empty handed.
Here is my form. I'm writing out each record in a List to a table. I've tried both the Html.RadioButton and Html.RadioButtonFor to create them. I also have a comments textbox underneath the table where someone can put in some comments. Here is a snippet of the View.
<tbody>
#foreach (var item in Model.listPendingExceptions)
{
<tr>
<td>#Html.RadioButton("rdo" + item.RID, "A")</td>
<td>#Html.RadioButton("rdo" + item.RID, "D")</td>
<td>#item.Shift_Date.ToShortDateString()</td>
</tr>
}
</tbody>
#Html.TextAreaFor(m => m.ExceptionComment, new { cols = 200, #rows = 4, #maxlength = "100", #class = "form-control", #placeholder = "100 character limitation", #autofocus = "autofocus" })
In my HttpPost I've tried using the form collection. However, what I've found is to look in the AllKeys list. When I view my Post the only thing in the AllKeys is the comment's TextBox value.
When I use a ViewModel in the HttpPost, the list of exceptions that I used to populate the table in the View is NULL. I would expect that since I didn't store the list in a hidden field.
How can I loop through each record, determine which if any radio button has been selected, as well as get the text from the Comments textbox?
UPDATE for EditTemplate
I created the folder structure for EditorTemplates in the Views.
I already had a ViewModel with a List of Exceptions but I did move the SelectedApproval from the main VM to the list of Exceptions.
public class ReportPendingExceptionsViewModel
{
public List<PendingException> listPendingExceptions { get; set; }
public bool IsAdmin { get; set; }
[Required(ErrorMessage = "*Required")]
public string ExceptionComment { get; set; }
}
public class PendingException
{
public int RID { get; set; }
public DateTime Shift_Date { get; set; }
public string Shift_Text { get; set; }
public string Emp_Id { get; set; }
public string Emp_Name { get; set; }
public string Last_Name { get; set; }
public string First_Name { get; set; }
public string Comment_Text { get; set; }
public string SelectedApproval { get; set; }
}
I then created a Razor View for the Table rows.
#model ProjectName.Models.ViewModels.PendingException
<tr>
<td>#Html.RadioButtonFor(e=>e.SelectedApproval,"A")</td>
<td>#Html.RadioButtonFor(e => e.SelectedApproval, "D")</td>
<td>#Model.Shift_Date.ToShortDateString()</td>
<td>#Model.Emp_Name</td>
<td>#Model.Shift_Text</td>
<td>#Model.Comment_Text</td>
<td></td>
</tr>
I then updated my main View to use the EditFor.
<thead>
<tr>
<th style="width:80px;">Approve</th>
<th style="width:80px;">Deny</th>
<th>Shift Date</th>
<th>Employee</th>
<th>Schedule</th>
<th>Comments</th>
<th></th>
</tr>
</thead>
<tbody>
#Html.EditorFor(f => f.listPendingExceptions)
</tbody>
However, when I run it, all I get is the RID values. So, I must be missing something. Here is the output from the View Source.
Did I miss a step?
public class ExceptionModel
{
public int Id { set; get; }
public bool IsApproved { set; get; }
public DateTime ShiftDate { set; get; }
}
public class MainModel
{
public string Comment { set;get;}
public List<ExceptionModel> lst_Exception { set;get;}
}
//this is get request action method
public ActionResult Create()
{
MainModel model = new MainModel();
model.lst_Exception = new List<ExceptionModel>()
{
new ExceptionModel() {Id = 1,IsApproved = false, ShiftDate = DateTime.Now},
new ExceptionModel() {Id = 2,IsApproved = false, ShiftDate = DateTime.Now},
new ExceptionModel() {Id = 3,IsApproved = false, ShiftDate = DateTime.Now}
};
return View(model);
}
//this is view for action method
#model MainModel
#using(Html.BeginForm())
{
<table>
<thead>
<tr>
<th>Approve</th>
<th>Deny</th>
<th>Shift Date</th>
</tr>
</thead>
<tbody>
#for (var item = 0; item < Model.lst_Exception.Count(); item++)
{
<tr>
<td>#Html.RadioButtonFor(model=>model.lst_Exception[item].IsApproved, "Approve")</td>
<td>#Html.RadioButtonFor(model=>model.lst_Exception[item].IsApproved, "Deny")</td>
<td><span>#Model.lst_Exception[item].ShiftDate</span>
#Html.HiddenFor(model => model.lst_Exception[item].ShiftDate})
</td>
</tr>
}
</tbody>
</table>
#Html.TextBoxFor(model=>model.Comment)
<input type="Submit" value="Submit" />
}
//this is Post action method
[HttpPost]
public ActionResult Create(MainModel model)
{
//here you can loop through model.lst_Exception to get the select values
//from the view
}
It is very easy to do this with Editor Templates.
Start with creating a view model for pending exception items
public class ExceptionVm
{
public int Id { set; get; }
public bool? IsApproved { set; get; }
public DateTime ShiftDate { set; get; }
}
and in your main view model, you will add a collection property which of of type
List<ExceptionVm>.
public class MyViewModel
{
public string Comment { set;get;}
public List<ExceptionVm> PendingExceptions { set;get;}
public MyViewModel()
{
PendingExceptions = new List<ExceptionVm>();
}
}
And in your GET action you initialize the view model object, load the PendingExceptions property
public ActionResult Create()
{
var vm = new MyViewModel();
vm.ExceptionVms = new List<ExceptionVm>()
{
new ExceptionVm() {Id = 1, ShiftDate = DateTime.Now.AddDays(-3)},
new ExceptionVm() {Id = 2, ShiftDate = DateTime.Now.AddDays(-2)},
new ExceptionVm() {Id = 3, ShiftDate = DateTime.Now.AddDays(-1)}
};
return View(vm);
}
Now, let's create an editor template. Create a new directory called EditorTemplates under ~/Views/YourControllerName/ or ~/Views/Shared/ and add a new razor view under that. Give the file the same name as our view model class, ExceptionVm.cshtml
Now add the below code to the editor template view. This basically render the 2 radio buttons and the date
#model ExceptionVm
<tr>
<td>#Html.RadioButtonFor(b=>b.IsApproved, true)</td>
<td>#Html.RadioButtonFor(b => b.IsApproved, false) </td>
<td> #Model.ShiftDate #Html.HiddenFor(x=>x.Id) </td>
</tr>
Now go to your main view, which is strongly typed to our MyViewModel class, and call the Html.EditorFor helper method and pass the PendingExceptions collection property to that
#model MyViewModel
#using(Html.BeginForm())
{
<table class="table">
<tbody>
#Html.EditorFor(f => f.PendingExceptions)
</tbody>
</table>
#Html.TextBoxFor(f => f.Comment)
<input type="Submit" value="Submit" class="btn btn-default" />
}
The call to the EditorFor will render a table row for each item in the PendingExceptions collection. When you submit the form, you can use the same MyViewModel class as the parameter and inspect the PendingExceptions property, iterate through each item and see whether it is true or false or null(if they have not selected anything)
[HttpPost]
public ActionResult Create(MyViewModel model)
{
// check model.PendingExceptions collection and each items IsApproved prop value
// to do : return something
}
If you do not want to allow null selection, change the IsApproved property type from bool? to bool
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 a page to display every log (message, time, type, customerId, Name) in a html table. Since the log is huge I am using a IPagedList in the Razor MVC and this works perfectly. I currently have 2 search boxes (for admins) and 1 for members. Where you can search by the message and customer ID.
Now the problem is that I don't want the users to just have a textbox where you only can put in a number (for example you put in customer ID 2 and get the customer T) - but instead I want a dropdown with all the current customer names connected to the customer IDs.
I have this functionality in another page I use but it only works because I return the model on the other page and because the log page returns a "IPagedListModel" instead of a "Model" I can't use this solution. How would I make this solution work for my log page as well?
HTML code
#:<p>#Html.DropDownListFor(m => m.SelectedCustomer, Model.CustomerList)</p>
Model
using System;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Collections.Generic;
using PagedList;
using System.Web.Mvc;
namespace ASDMVC.Models
{
[Table("Log")]
public class LogModel
{
[Key]
public long id { get; set; }
public string message { get; set; }
public DateTime timeStamp { get; set; }
public string level { get; set; }
public int customerId { get; set; }
[NotMapped]
public string Name { get; set; }
}
public class LogModelVM
{
public int SelectedCustomer { get; set; }
public IEnumerable<SelectListItem> CustomerList { get; set; }
public string Message { get; set; }
public IPagedList<LogModel> Logs { get; set; }
}
public class LogDBContext:DbContext
{
public LogDBContext() : base("MySqlConnection")
{
}
public DbSet <LogModel> Log { get; set; }
public IQueryable<LogModel> GetLogs()
{
return from log in Log
select log;
}
}
}
Controller
public class DbCustomerIds
{
public List<DbCustomer> GetCustomerIds()
{
List<DbCustomer> Customers = new List<DbCustomer>();
string queryString = "SELECT * FROM dbo.customers";
SqlDataAdapter adapter = new SqlDataAdapter(queryString, System.Configuration.ConfigurationManager.ConnectionStrings["MySqlConnection"].ConnectionString);
DataSet customers = new DataSet();
adapter.Fill(customers, "Customers");
foreach (DataRow item in customers.Tables[0].Rows)
{
DbCustomer cid = new DbCustomer();
cid.FakeId = Convert.ToInt32(item["Id"]);
cid.FakeName = Convert.ToString(item["Name"]);
Customers.Add(cid);
}
return Customers;
}
}
private IEnumerable<SelectListItem> GetCustomerIds()
{
var DbCustomerIds = new DbCustomerIds();
var customers = DbCustomerIds
.GetCustomerIds()
.Select(x =>
new SelectListItem
{
Value = x.FakeId.ToString(),
Text = x.FakeName
});
return new SelectList(customers, "Value", "Text");
}
}
[HttpPost]
public PartialViewResult LogPartialView(string searchString, string searchString2, string currentFilter, string currentFilterz, int? page, string sortOrder)
{
if (Roles.IsUserInRole(WebSecurity.CurrentUserName, "Admin"))
{
Customer = GetCustomerIds();
message = db.GetLogs();
int pageSize = 10;
int pageNumber = (page ?? 1);
var logs = message.OrderByDescending(i => i.timeStamp).ToPagedList(pageNumber, pageSize);
foreach (var log in logs)
log.Name = Customer.Where(a => a.Value == log.customerId.ToString()).FirstOrDefault().Text;
LogModelVM LMVM = new LogModelVM();
LMVM.Logs = logs;
LMVM.CustomerList = Customer;
return PartialView("_LogPartialLayout", LMVM);
}
LogModelVM LMVM = new LogModelVM();
LMVM.Logs = logs;
LMVM.CustomerList = Customer;
return PartialView("_LogPartialLayout", LMVM);
}
_LogPartialLayout
#model ASDMVC.Models.LogModelVM
#using PagedList.Mvc;
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />
#if (Roles.IsUserInRole(WebSecurity.CurrentUserName, "Admin"))
{
<table class="table">
<tr>
<th id="tableth">
message
</th>
<th id="tableth">
timestamp
</th>
<th id="tableth">
level
</th>
<th id="tableth">
customerId
</th>
<th id="tableth">
customerName
</th>
</tr>
#foreach (var item in Model.Logs)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.message)
</td>
<td>
#Html.DisplayFor(modelItem => item.timeStamp)
</td>
<td>
#Html.DisplayFor(modelItem => item.level)
</td>
<td>
#Html.DisplayFor(modelItem => item.customerId)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
</tr>
}
</table>
}
#if (Roles.IsUserInRole(WebSecurity.CurrentUserName, "Member"))
{
<table class="table">
<tr>
<th id="tableth">
message
</th>
<th id="tableth">
timestamp
</th>
<th id="tableth">
level
</th>
</tr>
#foreach (var item in Model.Logs)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.message)
</td>
<td>
#Html.DisplayFor(modelItem => item.timeStamp)
</td>
<td>
#Html.DisplayFor(modelItem => item.level)
</td>
</tr>
}
</table>
}
Page #(Model.Logs.PageCount < Model.Logs.PageNumber ? 0 : Model.Logs.PageNumber) of #Model.Logs.PageCount
#Html.PagedListPager(Model.Logs, page => Url.Action("LogPartialView",
new { page, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter, currentFilterz = ViewBag.CurrentFilterz }), PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing(PagedListRenderOptions.ClassicPlusFirstAndLast,
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
UpdateTargetId = "divLogs"
}))
Any help would be apprechiated, sorry for the long question - I just wanted to get all the information which seems relevant to the situation.
Thanks in advance.
Current error when running:
[InvalidOperationException: The model item passed into the dictionary is of type 'PagedList.PagedList`1[NowasteWebPortalMVC.Models.LogModel]', but this dictionary requires a model item of type 'NowasteWebPortalMVC.Models.LogModelVM'.]
Create a view model with the properties you need in the view
public class LogModelVM
{
public int SelectedCustomer { get; set; }
public IEnumerable<SelectListItem> CustomerList { get; set; } // suggested name change
public string Message { get; set; } // for the message search box?
public IPagedList<NowasteWebPortalMVC.Models.LogModel> Logs { get; set; }
.... // add properties for sortorder etc
}
Then in the controller method, initiaize a new LogModelVM and assign the values (e.g. model.Logs = logs;), and return the view model so that in the view you can use
#model yourAssembly.LogModelVM
....
#Html.DropDownListFor(m => m.SelectedCustomer, Model.CustomerList) // why change the id attribute?
....
#Html.PagedListPager(Model.Logs, page => Url.Action(...`
You should also consider adding the other properties such as sortOrder and currentfilter rather than using ViewBag
Side note: Ensure that all associated views, including the main view also use #model yourAssembly.LogModelVM
I have a BookingView class in Model:
public class BookingView
{
[Required]
[Display(Name = "Attraction")]
public int Attraction { get; set; }
[Required]
[Display(Name = "Date")]
public string Date { get; set; }
[Required]
[Display(Name = "Username")]
public string Username { get; set; }
}
Table against this model in DB is Tickets.
I need to write a function in another class in model named BookingManager to get all Ticket Records.
public IEnumerable<BookingView> GetAllBookings()
{
var a = from o in dre.Tickets select o;
return a.ToList();
}
I want to display these records in view named ViewAllBookings:
#model IEnumerable<VirtualTickets.Models.ViewModel.BookingView>
#{
ViewBag.Title = "ViewAllBookings";
}
<h2>ViewAllBookings</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
Attraction
</th>
<th>
Date
</th>
<th>
Username
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Attraction)
</td>
<td>
#Html.DisplayFor(modelItem => item.Date)
</td>
<td>
#Html.DisplayFor(modelItem => item.Username)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
</table>
The function gives complie time error in function GetAllBookings on return statement. If I change return type to Ticket then I get runtime error as expected because in view ViewAllBookings it is expecting IEnumerable List of records having type BookingView.
Please provide a solution to this situation. I am really confused how to deal with this.
Thanks
It looks like your setup requires converting the entity class Ticket into the view model BookingView. I guess you need something like this:
return a.AsEnumerable().Select(ticket => new BookingView
{
Attraction = ticket.SomeProperty,
// etc
})
.ToList();
You have to write method that maps Ticket to BookingView and use it like this:
public IEnumerable<BookingView> GetAllBookings()
{
var a = from o in dre.Tickets select o;
return a.AsEnumerable().Select(Map).ToList();
}
private BookingView Map(Ticket ticket)
{
var bookingView = new BookingView();
//mapping code goes here
return bookingView;
}
I am not sure if your Tickets table actually has the same columns as the BookingView class in your code, but if theere's a mapping between the table and your class, your solution would be:
var a = from o in dre.Tickets
select new BookingView { Attraction= o.Attraction,
Date=o.Date,
Username = o.UserName } ;
return a.ToList();
Yes it is obvious that you are getting this error because you are returning a Ticket object and your view needs a list of BookingView objects. The 2 don't match, i.e. they are not the same.
Your BookingManager should return your tickets to the controller. Here is an alternative way of how I would have done it.
You could change your view model to look like this:
public class BookingViewModel
{
public List<Ticket> Tickets { get; set; }
}
Your BookingManager can contain the following method to return your tickets:
public class BookingManager : IBookingManager
{
// References to your database context
public IEnumerable<Ticket> GetAllTickets()
{
return databaseContext.Tickets;
}
}
In your booking controller you can inject an instance of IBookingManager with a dependency injection framework like AutoFac:
public class BookingController : Controller
{
private readonly IBookingManager bookingManager;
public BookingController(IBookingManager bookingManager)
{
this.bookingManager = bookingManager;
}
public ActionResult Index()
{
BookingViewModel viewModel = new BookingViewModel
{
Tickets = bookingManager.GetAllTickets().ToList()
};
return View(viewModel);
}
}
And then on your view:
#model YourProject.ViewModels.Tickets.BookingViewModel
#foreach (var ticket in Model.Tickets)
{
// Display ticket details here
}
I hope this helps.
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)