ModelView Validation while navigating between Kendo TabStrip tabs - asp.net-mvc-4

I am trying to fix a modelview's validation issue before submitting to the server
Here is the Main ViewModel
public class AdmissionViewModel
{
public int OperatorID { get; set; }
/* some other members */
public AdmissionFeeViewModel Fee { get; set; }
public AdmissionFallDateViewModel FallAdmission { get; set; }
public AdmissionDateViewModel SpringAdmission { get; set; }
public AdmissionDateViewModel SummerAdmission { get; set; }
}
Here are definations for the above ViewModel members
public class AdmissionFeeViewModel
{
public AdmissionFeeBaseModel Domestic { get; set; }
public AdmissionFeeBaseModel International { get; set; }
}
--> Base model
public class AdmissionFeeBaseModel
{
public int? AdmissionFeeID { get; set; }
[Required(ErrorMessage = "Application Fee is required")]
public decimal? ApplicationFee { get; set; }
public string ApplicationFeeWaiver { get; set; }
public string FeeWaiverNotes { get; set; }
public string FeeDataUrl { get; set; }
}
public class AdmissionFallDateViewModel
{
public AdmissionFallDateBaseModel Domestic { get; set; }
public AdmissionFallDateBaseModel International { get; set; }
}
--> base Model
public class AdmissionFallDateBaseModel: AdmissionDateBaseModel
{
[Display(Name = "Early Decision Offered")]
public override string PriorityDecisionOffered { get; set; }
[Display(Name = "Early Decision Deadline")]
public override string PriorityDecisionDeadline { get; set; }
[Display(Name = "Early Decision Notification")]
public override string PriorityDecisionNotificationDate { get; set; }
[Display(Name = "Early Decision Deposit Deadline")]
public override string PriorityDecisionDepositDeadline { get; set; }
[Display(Name = "Financial Aid Application Deadline")]
public override string PriorityFinancialAidAppDeadline { get; set; }
[Display(Name = "Early Action Offered")]
public string PriorityActionOffered { get; set; }
[Display(Name = "Early Action Deadline")]
public string PriorityActionDeadline { get; set; }
[Display(Name = "Early Action Notification")]
public string PriorityActionNotificationDate { get; set; }
[Display(Name = "Early Decision or Action Notes")]
public override string PriorityAdmissionNotes { get; set; }
[Display(Name = "Data URL")]
public override string PriorityDataURL { get; set; }
}
public class AdmissionDateViewModel
{
public AdmissionDateBaseModel Domestic { get; set; }
public AdmissionDateBaseModel International { get; set; }
}
--> base model
public class AdmissionDateBaseModel
{
[HiddenInput]
public int? AdmissionDateID { get; set; }
[Display(Name = "Regular Admission Deadline")]
public string ApplicationDeadline { get; set; }
[Display(Name = "Regular Admission Notification")]
public string AdmissionNotificationDate { get; set; }
[Display(Name = "Regular Admission Deposit Deadline")]
public string DepositDeadline { get; set; }
[Display(Name = "Accept Offer of Admission")]
public string AcceptOfferDeadline { get; set; }
[Display(Name = "Waiting List Used")]
public string WaitingListUsed { get; set; }
[Display(Name = "Deferred Admission")]
public string DeferAdmission { get; set; }
[Display(Name = "Transfer Admission")]
public string TransferAdmission { get; set; }
[Display(Name = "Financial Aid Application Deadline")]
public string FinancialAidAppDeadline { get; set; }
[Display(Name = "Admission Notes")]
public string AdmissionNotes { get; set; }
[Display(Name = "Data URL")]
public string DataURL { get; set; }
[Display(Name = "Priority Decision Offered")]
public virtual string PriorityDecisionOffered { get; set; }
[Display(Name = "Priority Decision Deadline")]
public virtual string PriorityDecisionDeadline { get; set; }
[Display(Name = "Priority Decision Notification Date")]
public virtual string PriorityDecisionNotificationDate { get; set; }
[Display(Name = "Priority Decision Deposit Deadline")]
public virtual string PriorityDecisionDepositDeadline { get; set; }
[Display(Name = "Priority Financial Aid Application Deadline")]
public virtual string PriorityFinancialAidAppDeadline { get; set; }
[Display(Name = "Admission Notes")]
public virtual string PriorityAdmissionNotes { get; set; }
[Display(Name = "Data URL")]
public virtual string PriorityDataURL { get; set; }
}
Here is the main View where Kendo().TabStrip is placed with 4 tabs one for each member from AdmissionViewModel
#model UniversityApp.ViewModels.AdmissionViewModel
#Html.HiddenFor(model => model.OperatorID)
<table>
<tr>
<td>
#(Html.Kendo().TabStrip()
.Name("tabAdmission")
.Events(events => events
.Select("tabAdmissionOnSelect")
)
.Animation(false)
.Items(items =>
{
items.Add().Text("Application Fees").Content(#<text>
#Html.EditorFor(m => m.Fee)
</text>).Selected(true);
items.Add().Text("Fall Admission").Content(#<text>
#Html.EditorFor(m => m.FallAdmission)
</text>);
items.Add().Text("Spring Admission").Content(#<text>
#Html.EditorFor(m => m.SpringAdmission)
</text>);
items.Add().Text("Summer Admission").Content(#<text>
#Html.EditorFor(m => m.SummerAdmission)
</text>);
})
)
</td>
</tr>
</table>
-->here is child views the the kendo.tabstrip uses
#model UniversityApp.ViewModels.AdmissionFeeViewModel
#Html.HiddenFor(model => model.Domestic.AdmissionFeeID)
#Html.HiddenFor(model => model.International.AdmissionFeeID)
<table>
<tr>
<th></th>
<th>Domestic Applicant</th>
<th>International Applicant</th>
</tr>
<tr>
<td><label>Application Fee</label></td>
<td>
#Html.EditorFor(model => model.Domestic.ApplicationFee)
#Html.ValidationMessageFor(model => model.Domestic.ApplicationFee)
</td>
<td>
#Html.EditorFor(model => model.International.ApplicationFee)
#Html.ValidationMessageFor(model => model.International.ApplicationFee)
</td>
</tr>
<tr>
<td><label>Application Fee Waiver</label></td>
<td>
#Html.EditorFor(model => model.Domestic.ApplicationFeeWaiver)
#Html.ValidationMessageFor(model => model.Domestic.ApplicationFeeWaiver)
</td>
<td>
#Html.EditorFor(model => model.International.ApplicationFeeWaiver)
#Html.ValidationMessageFor(model => model.International.ApplicationFeeWaiver)
</td>
</tr>
<tr>
<td></td>
<td></td>
<td><input type="button" btn-next-tab="true" value="Next" /></td>
</tr>
/* childview */
#model UniversityApp.ViewModels.AdmissionDateViewModel
#Html.HiddenFor(model => model.Domestic.AdmissionDateID)
#Html.HiddenFor(model => model.International.AdmissionDateID)
<table>
<tr>
<th></th>
<th>Domestic Applicant</th>
<th>International Applicant</th>
</tr>
<tr>
<td>#Html.LabelFor(model => model.Domestic.ApplicationDeadline)</td>
<td>
#Html.EditorFor(model => model.Domestic.ApplicationDeadline)
#Html.ValidationMessageFor(model => model.Domestic.ApplicationDeadline)
</td>
<td>
#Html.EditorFor(model => model.International.ApplicationDeadline)
#Html.ValidationMessageFor(model => model.International.ApplicationDeadline)
</td>
</tr>
/* all other properties */
<tr>
<td><input type="button" btn-previous-tab="true" value="Previous" /></td>
<td></td>
<td><input type="button" btn-next-tab="true" value="Next" /></td>
</tr>
</table>
in the same way we have 2 more child views.
Anyway, problem is:
when i click on submit (save) button in the main view, all the required fields ring bells, but if i am on any other tabs that do not have required fields and hit submit,
form is submitted with out client validation errors.

I also had the same issue and after investigation, jQuery Validate by default ignores anything that is :hidden http://jqueryvalidation.org/validate/#ignore
From https://api.jquery.com/hidden-selector/ :hidden is defined as
CSS display value of none.
Form elements with type="hidden".
Width and Height are set to 0.
Ancestor element is hidden
When a tab strip isn't selected then it is set to be display:none so Validate will ignore the inputs as its ancestor is :hidden (display: none).
The workaround that I used was to add this to the view
<script>
$(document).ready(function() {
$("form").data("validator").settings.ignore = "";
});
</script>
It will now of course validate everything on the view including your hidden inputs, so may not be the correct approach but might help. You could always and some specificity to $("form").data("validator").settings.ignore = "input[type=hidden]"
The Kendo Validator doesn't suffer from the same issue http://demos.telerik.com/kendo-ui/validator/index but the user won't know it's errored if the tab isn't in view, so you will need some sort of summary or try to work out which tab has the error then select it.

Related

Asp Core Filter & Search Data Using Drop Downs & Search Bars

I would like users to search through pets using a search bar or drop down lists. I tried using viewbag and asp tag helpers but I keep getting errors. Below is a picture of what i'm going for. Any help is appreciated.
Model
public class Reptile
{
public int ReptileId { get; set; }
public string Name { get; set; }
public string Age { get; set; }
[Display(Name ="Reptile's Image")]
public byte[] Image { get; set; }
[Display(Name ="Food Requirements")]
public string FoodReq { get; set; }
[Display(Name="Habitat Requiremtns")]
public string HabitatReq { get; set; }
public string Gender { get; set; }
public string Type { get; set; }
public string Size { get; set; }
public string Color { get; set; }
[Display(Name="Recent Checkup")]
public bool RecentCheckup { get; set; }
public bool Trained { get; set; }
public bool Neutered { get; set; }
public bool Declawed { get; set; }
[Display(Name = "Good With Other Reptiles")]
public bool GoodWithRept { get; set; }
[Display(Name = "Good With Kids")]
public bool GoodWithKids { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public int ApplicationUserId { get; set; }
}
Controller
public async Task<IActionResult> Index(string searchString)
{
var reptiles = from r in _context.Reptiles
select r;
if (!string.IsNullOrEmpty(searchString))
{
reptiles = reptiles.Where(r => r.Type.Contains(searchString));
}
return View(await reptiles.ToListAsync());
}
View
<form asp-controller="Reptiles" asp-action="Index" method="get">
<div class="form-actions no-color">
<p>
Search By Type: <input type="text" name="SearchString" />
<input type="submit" value="Filter" class="btn btn-default" /> |
<a asp-action="Index">Back to Full List</a>
</p>
</div>
</form>
I've been trying to follow the docs here Tutorial: Add sorting, filtering, and paging - ASP.NET MVC with EF Core. Not having any luck though.
Here is a simple demo to show how to use searchstring:
Controller:
public IActionResult Index(string searchString)
{
IEnumerable<Reptile> list = new List<Reptile> { new Reptile { Type = "t1", Name= "Reptile1" }, new Reptile { Type = "t2", Name = "Reptile2" }, new Reptile { Type = "t3", Name = "Reptile3" } };
ViewData["CurrentFilter"] = searchString;
if (!String.IsNullOrEmpty(searchString))
{
list = list.Where(s => s.Name.Contains(searchString));
}
return View(list);
}
View:
Find by name:
|
Back to Full List
<table>
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Type)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
<input type="text" asp-for="#item.Type">
</td>
</tr>
}
</tbody>
</table>
result:
Okay, I figured out how to use select to filter the reptile page by using the data users already added to the database from the properties in the model. I had to create a view model and add the Reptile model to it.
View Model
public class ReptileGenderViewModel
{
public Reptile Reptile { get; set; }
public List<Reptile> reptiles;
public SelectList genders;
public string reptileGender { get; set; }
}
Reptile Controller
public async Task<IActionResult> Index(string searchString, string reptileGender)
{
IQueryable<string> genderQuery = from g in _context.Reptiles
orderby g.Gender
select g.Gender;
var reptiles = from r in _context.Reptiles
select r;
if (!string.IsNullOrEmpty(searchString))
{
reptiles = reptiles.Where(r => r.Type.Contains(searchString));
}
if (!string.IsNullOrEmpty(reptileGender))
{
reptiles = reptiles.Where(g => g.Gender == reptileGender);
}
var reptileGenderVM = new ReptileGenderViewModel();
reptileGenderVM.genders = new SelectList(await genderQuery.Distinct().ToListAsync());
reptileGenderVM.reptiles = await reptiles.ToListAsync();
return View(reptileGenderVM);
}
View
<select asp-for="reptileGender" asp-items="Model.genders">
<option value="">All</option>
</select>

How can I display the total number of records a table holds(the count) in a view by passing the count value in the "return view method"

How can I display the total number of records a table holds(the count). I would like to display the count in a view so I am trying to pass the count as a parameter on return view but I get an error saying cannot convert string to int. I am pretty sure there is a smarter way to do this. I have tried converting the int value using toString() but I still get syntax erros thereafter. I have placed both my controller and view below. Notice what I am trying to do in my controller in the return view method I am trying to insert the count but I get an error that says
cannot convert from int? to string
Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Data;
using System.Web.Http;
using System.Configuration;
using PagedList;
namespace App.Web.Controllers
{
public class DisplayUploadedFileController : Controller
{
private EntitiesModel db = new EntitiesModel();
public ActionResult DisplayUploadedFileContents(int? id, int? page, int? totalCount)
{
var vm = new dbclients_invalidEmailsVM();
vm.UploadId = id ?? default(int);
if (page == null)
{
page = 1;
}
int pageSize = 10;
int pageNumber = (page ?? 1);
var rows = from myRow in db.tbl_dataTable
select myRow;
totalCount = rows.Count();
return View(db.tbl_dataTable.OrderByDescending(r => r.ClientId).Where(r => r.UploadId == id).ToList().ToPagedList(pageNumber, pageSize), totalCount);
}
}
}
View
#model PagedList.IPagedList<App.Web.marketingdbclients_dataTable>
#using PagedList.Mvc;
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link href="http://www.jqueryscript.net/css/jquerysctipttop.css" rel="stylesheet" type="text/css">
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="pagination.js"></script>
</head>
<body>
<div class="container" style="margin-top:50px;">
<table class="table" id="table">
<tr>
<th>
First Name
</th>
<th>
Last Name
</th>
<th>
Cell1
</th>
<th>
Email1
</th>
<th>
Company
</th>
<th>
Job Title
</th>
<th>
Province
</th>
<th>
Source
</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.Cell1)
</td>
<td>
#Html.DisplayFor(modelItem => item.Email1)
</td>
<td>
#Html.DisplayFor(modelItem => item.Company)
</td>
<td>
#Html.DisplayFor(modelItem => item.JobTitle)
</td>
<td>
#Html.DisplayFor(modelItem => item.PhysicalProvince)
</td>
<td>
#Html.DisplayFor(modelItem => item.Source)
</td>
</tr>
}
</table>
</br>
Number of Records #Model.Count()<br />
Page #(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of #Model.PageCount
#Html.PagedListPager(Model, page => Url.Action("DisplayUploadedFileContents", new { uploadId = Model.First().UploadId, page }))
</div>
</body>
</html>
My Table Represented as a model
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace App.Web
{
using System;
using System.Collections.Generic;
public partial class marketingdbclients_dataTable
{
public int ClientDataId { get; set; }
public Nullable<int> ClientId { get; set; }
public Nullable<int> UploadId { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string IdentificationNumber { get; set; }
public string RaceId { get; set; }
public string DateOfBirth { get; set; }
public string Age { get; set; }
public string TitleTypeId { get; set; }
public string GenderTypeId { get; set; }
public string Nationality { get; set; }
public string PhysicalCountry { get; set; }
public string PhysicalProvince { get; set; }
public string PhysicalCity { get; set; }
public string Area { get; set; }
public string HighestQualification { get; set; }
public string CurrentQualification { get; set; }
public string PhysicalAddress { get; set; }
public string PostalAddress { get; set; }
public string Cell1 { get; set; }
public string Cell2 { get; set; }
public string Cell3 { get; set; }
public string Cell4 { get; set; }
public string Work1 { get; set; }
public string Work2 { get; set; }
public string Work3 { get; set; }
public string Work4 { get; set; }
public string Home1 { get; set; }
public string Home2 { get; set; }
public string Home3 { get; set; }
public string Home4 { get; set; }
public string LSMGroup { get; set; }
public string Municipality { get; set; }
public string Crediting_Rating { get; set; }
public string Email1 { get; set; }
public string Email2 { get; set; }
public string Email3 { get; set; }
public string Email4 { get; set; }
public string Income { get; set; }
public string Company { get; set; }
public string Industry { get; set; }
public string JobTitle { get; set; }
public string LeadStage { get; set; }
public string ReggieNumber { get; set; }
public string Source { get; set; }
public System.DateTime DateInserted { get; set; }
//public int totalEntriesCount { get; set; }
}
}
Please create a new ViewModel class and store your two inputs like so:
public class MyViewModel
{
public List<marketingdbclients_dataTable> marketingdbclients_dataTables { get; set; }
public int totalCount { get; set; }
public MyViewModel()
{
this.marketingdbclients_dataTables = new List<marketingdbclients_dataTable>();
this.totalCount = 0;
}
}
Controller file should be
public ActionResult DisplayUploadedFileContents(int? id, int? page, int? totalCount)
{
var vm = new dbclients_invalidEmailsVM();
vm.UploadId = id ?? default(int);
if (page == null)
{
page = 1;
}
int pageSize = 10;
int pageNumber = (page ?? 1);
var rows = from myRow in db.tbl_dataTable
select myRow;
totalCount = rows.Count();
MyViewModel model = new MyViewModel();
model.marketingdbclients_dataTables = db.tbl_dataTable.OrderByDescending(r => r.ClientId).Where(r => r.UploadId == id).ToList().ToPagedList(pageNumber, pageSize);
model.totalCount = totalCount ;
return View(model);
}
Then in your View (index.cshtml), declare MyViewModel like so:
#model WebApp.Models.MyViewModel
<div>
your html
</div>
The concept we just used is called View Model. Please read more about it here:
Understanding ViewModel
You may only pass one model object when calling View(model).
You can create an object that contains both the count and the datatable that you use as the view model.
A simple way to do this may be using an anonymous object:
return View(
new {
Page = db.tbl_dataTable.OrderByDescending(r => r.ClientId).Where(r => r.UploadId == id).ToList().ToPagedList(pageNumber, pageSize)db.tbl_dataTable.OrderByDescending(r => r.ClientId).Where(r => r.UploadId == id).ToList().ToPagedList(pageNumber, pageSize),
Count = totalCount
});

Reference model class in controller, so muliple data can be in one view

I am trying to get data from multiple SQL Server stored procedure to be accessible in 1 view so I can then run comparisons and create graphs/grids etc.
I got each section to work on their own, and I am now trying to get them to work together.
I have change my controller and put the field types into their own classes, and then created a "Ring of Rings" class, and put them all in.
namespace APP.Models
{
public class SP_RESULTS
{
public type Type { get; set; }
public status Status { get; set; }
public condition Condition { get; set; }
public rooms Rooms { get; set; }
}
public class type
{
[Key]
public decimal type_id { get; set; }
public string type_code { get; set; }
public string type_name { get; set; }
}
public class status
{
//status
public decimal status_id { get; set; }
public string status_code { get; set; }
public string status_name { get; set; }
public string rentable { get; set; }
}
public class condition
{
//condition
public decimal condition_id { get; set; }
public string condition_code { get; set; }
public string condition_name { get; set; }
public string rentable { get; set; }
public int colour_code { get; set; }
public int service_order { get; set; }
}
public class rooms
{
//rooms
public decimal room_id { get; set; }
public string room_no { get; set; }
public decimal type_id { get; set; }
public int floor { get; set; }
public decimal status_id { get; set; }
public decimal condition_id { get; set; }
}
}
I then amended each section of my controller that was running SQL Server stored procedures, to use the correct class name instead of the "ring of rings" name, IE:
var outputmodel4 = new List<rooms>();
var command4 = db.Database.Connection.CreateCommand();
command4.CommandText = "SELECT rooms.room_id , rooms.room_no , rooms.type_id , rooms.floor_no , rooms.status_id , rooms.condition_id FROM rooms";
using (var SPOutput4 = command4.ExecuteReader())
{
foreach (var row in SPOutput4)
{
outputmodel4.Add(new rooms()
{
room_id = (decimal)SPOutput4["room_id"],
room_no = (string)SPOutput4["room_no"],
type_id = (decimal)SPOutput4["type_id"],
floor_no = (int)SPOutput4["floor_no"],
status_id = (decimal)SPOutput4["status_id"],
condition_id = (decimal)SPOutput4["condition_id"],
});
}
db.Database.Connection.Close();
return View(outputmodel4);
}
...etc for other SQL stored procedures
..and the same with my view
#model IEnumerable<app.Models.SP_RESULTS >
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Rooms.room_id)
</th>
<th>
#Html.DisplayNameFor(model => model.Rooms.room_no)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Rooms.room_id)
</td>
<td>
#Html.DisplayFor(modelItem => item.Rooms.room_no)
</td>
</tr>
}
</table>
…etc for the other models
Everything looks fine in VBS (no red swiggles), but when I access the view in the browser, I get an error:
Server Error in '/' Application.
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[app.Models.rooms]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[app.Models.SP_RESULTS]'.
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.InvalidOperationException: The model item passed into the dictionary is of type 'System.Collections.Generic.List1[app.Models.rooms]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[app.Models.SP_RESULTS]'.
If I go back to my "rings of rings" model, and change it to this :
public class SP_RESULTS
{
public IEnumerable<type> Type { get; set; }
public IEnumerable<status> Status { get; set; }
public IEnumerable<condition> Condition { get; set; }
public IEnumerable<rooms> Rooms { get; set; }
}
and change the following lines in in my controller:
var outputmodel4 = new List<rooms>();
outputmodel4.Add(new rooms()
to
var outputmodel4 = new List<SP_RESULTS>();
outputmodel4.Add(new SP_RESULTS()
VBS tells me that that
SP_RESULTS does not contain a definition for room_id
I've tried prefixing the definition with the class name, with no luck.
I've looked on SO, fourms.asp.net and google.. can cannot see a solution (there probably is, but I am unsure what solution I an looking for.... if that makes sense).
Would someone be able to tell me what I need to do to get all my model classes working in the same view ?
I apologise now, as I realize that this question has probably been asked several (million) times, but I cannot seem to fine out that jumps out says THIS is that way to do it.
For anyone else looking for an answer (and for future me), I got this work by putting the models like this :
namespace APP.Models
{
public class SP_RESULTS
{
public type Type { get; set; }
public status Status { get; set; }
public condition Condition { get; set; }
public rooms Rooms { get; set; }
public class type
{
[Key]
public decimal type_id { get; set; }
public string type_code { get; set; }
public string type_name { get; set; }
}
public class status
{
//status
public decimal status_id { get; set; }
public string status_code { get; set; }
public string status_name { get; set; }
public string rentable { get; set; }
}
public class condition
{
//condition
public decimal condition_id { get; set; }
public string condition_code { get; set; }
public string condition_name { get; set; }
public string rentable { get; set; }
public int colour_code { get; set; }
public int service_order { get; set; }
}
public class rooms
{
//rooms
public decimal room_id { get; set; }
public string room_no { get; set; }
public decimal type_id { get; set; }
public int floor { get; set; }
public decimal status_id { get; set; }
public decimal condition_id { get; set; }
}
}
}
Using VIEWDATA in the controller for each Store procedure:
var outputmodel3 = new List<SP_RESULTS.conditions>();
var command3 = db.Database.Connection.CreateCommand();
command3.CommandText = "dbo.pr_PROCEDUREONE";
command3.CommandType = System.Data.CommandType.StoredProcedure;
using (var SPOutput3 = command3.ExecuteReader())
{
foreach (var row in SPOutput3)
{
outputmodel3.Add(new SP_RESULTS.conditions()
{
condition_id = (decimal)SPOutput3["condition_id"],
condition_code = (string)SPOutput3["condition_code"],
condition_name = (string)SPOutput3["condition_name"],
});
}
ViewData["CONDITIONSOutput"] = outputmodel3;
}
var outputmodel4 = new List<SP_RESULTS.rooms>();
var command4 = db.Database.Connection.CreateCommand();
command4.CommandText = "SELECT rooms.room_id , etc FROM rooms";
using (var SPOutput4 = command4.ExecuteReader())
{
foreach (var row in SPOutput4)
{
outputmodel4.Add(new SP_RESULTS.rooms()
{
room_id = (decimal)SPOutput4["room_id"],
room_no = (string)SPOutput4["room_no"],
});
}
ViewData["ROOMSOutput"] = outputmodel4;
db.Database.Connection.Close();
return View();
}
..and then changing my view to read :-
table class="table">
<tr>
<th>
Condition ID
</th>
<th>
Condition code
</th>
<th>
Condition name
</th>
<th>
is it rentable?
</th>
<th>
Condition color code
</th>
<th>
Condition service order
</th>
<th></th>
</tr>
#foreach (var item in ViewData["CONDITIONSOutput"] as IEnumerable<Happ.Models.SP_RESULTS.conditions>)
{
<tr>
<td>
#item.condition_id
</td>
<td>
#item.condition_code
</td>
<td>
#item.condition_name
</td>
<td>
#item.rentable
</td>
<td>
#item.colour_code
</td>
<td>
#item.service_order
</td>
</tr>
}
</table>
<table class="table">
<tr>
<th>
Room ID
</th>
<th>
Room No
</th>
<th>
Rooms type_id
</th>
<th></th>
</tr>
#foreach (var item in ViewData["ROOMSOutput"] as IEnumerable<APP.Models.SP_RESULTS.rooms>)
{
<tr>
<td>
#item.room_id
</td>
<td>
#item.room_no
</td>
</tr>
}
</table>

Data Annotation for currency format not working

In my ASP.NET MVC Core web project on VS2015, the following model is displaying data as, e.g., 15481 instead of $15,481 even though I'm using [DisplayFormat] below:
Models:
public class State
{
[Key]
public int StateId { get; set; }
[Column(TypeName ="varchar(40)")]
public string StateName { get; set; }
[Column(TypeName = "char(2)")]
public string StateCode { get; set; }
}
public class Sales
{
[Key]
public int SalesId { get; set; }
public int? FiscalYear { get; set; }
[DisplayFormat(DataFormatString = "{(0:C0)}")]
public float? SaleAmount { get; set; }
public int StateId { get; set; }
public State State { get; set; }
}
ModelView:
public class StatesSalesViewModel
{
[HiddenInput]
public int StateId { get; set; }
[Display(Name ="State")]
public string StateName { get; set; }
public int? FiscalYear { get; set; }
[DisplayFormat(DataFormatString = "{(0:C0)}")]
public float? SaleAmount { get; set; }
}
Controller:
public async Task<IActionResult> FYSales(List<StatesSalesViewModel> model, string GO, int currentlySelectedIndex, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
ViewBag.YearsList = Enumerable.Range(1996, 29).Select(g => new SelectListItem { Value = g.ToString(), Text = g.ToString() }).ToList();
if (!string.IsNullOrEmpty(GO))
{
var qryVM = from s in _context.States
join g in _context.Sales on s.StateId equals g.StateId
where g.FiscalYear == currentlySelectedIndex
select new StatesSalesViewModel() {StateId = s.StateId, StateName = s.StateName, SaleAmount = g.SaleAmount , FiscalYear = currentlySelectedIndex };
return View(qryVM.ToList());
}
}
View:
#model IList<mProject.Models.StatesSalesViewModel>
<div class="row">
<div class="col-md-12">
<form asp-controller="StatesSales" asp-action="getSales" asp-route-returnurl="#ViewData["ReturnUrl"]" method="post">
#{
IEnumerable<SelectListItem> yearsList = (IEnumerable<SelectListItem>)ViewBag.YearsList;
var currentlySelectedIndex = 0; // Currently selected index (usually will come from model)
}
<strong>Select a Post Year</strong>
<h6>Choose a year o begin:</h6>
<label>Year:</label><select asp-for="#currentlySelectedIndex" asp-items="yearsList"></select><input type="submit" class="btn btn-default" name="GO" value="GO" />
<table class="table">
<thead>
<tr>
<th></th>
<th></th>
<th>Fiscal Year</th>
<th>State</th>
<th>Sales</th>
</tr>
</thead>
<tbody>
#for (int i=0; i< Model.Count(); i++)
{
<tr>
<td>#Html.HiddenFor(r => r[i].StateID)</td>
<td>#Html.HiddenFor(r => r[i].FYSalesID)</td>
<td>
#Html.TextBoxFor(r => r[i].FiscalYear)
</td>
<td>
#Html.TextBoxFor(r => r[i].StateName)
</td>
<td>
#Html.TextBoxFor(r => r[i].SaleAmount)
</td>
</tr>
}
</tbody>
</table>
<button type="submit" class="btn btn-default">Save</button>
</form>
</div>
</div>
A [DisplayFormat] attribute is only respected when using #Html.DisplayFor() or #Html.EditorFor(). It is ignored when using TextBoxFor().
In addition, if you wanted to use it with #Html.EditorFor(r => r[i].SaleAmount) you need to modify the attribute to include the ApplyFormatInEditMode property
[DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = true)]
public float? SaleAmount { get; set; }
however that would be of little use to you, because although it would display in the textbox correctly, it will not bind back to you float property unless you were also to create a custom model binder which converted (say) "$15,481" back to a float
The currency annotation can be used. However it is just telling MVC which display or editor template to use. As We said current template uses the system currency. You would have to provide custom editor template or display template and some other way to determine the currency symbol to display.Look here at how to provide your own implementations
Try using this
[DisplayFormat(DataFormatString = "{0:C0}")]
Example
public class Sales
{
[Key]
public int SalesId { get; set; }
[DisplayFormat(DataFormatString = "{0:C0}")]
public float? SaleAmount { get; set; }
}
Check here for more details

MVC ViewModel errors

Goal: To create a re-usable drop down menu that lists my website's administrators, managers and agents. These types of users are defined by the .NET Simplemembership webpages_Roles and webpages_UsersInRoles tables.
So Far:
I have a UserProfile table in my database which has 25 columns. I have a corresponding domain model of the same name which is accessed from my UsersContext() EF.
The drop down menu only needs to list the User's FirstName, LastName and UserId so instead of working with the complete domain model, I created the following ViewModel:
namespace MyModels.Models.ViewModels
{
public class AdminsAndAgentsListVM
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int UserId { get; set; }
}
}
I then added the following to my Account controller (notice I'm not working with partial view yet):
public ActionResult AdminsAndAgentsList()
{
UsersContext _db = new UsersContext(); //provides me access to UserProfiles data
var admins = Roles.GetUsersInRole("Admin"); //gets users with this role
var viewModel = _db.UserProfiles
.Where(x => admins.Contains(x.UserName)); //Selects users who match UserName list
return View(viewModel);
}
I then scaffold a list view and base it on the strongly typed ViewModel:
#model IEnumerable<MyModels.Models.ViewModels.AdminsAndAgentsListVM>
#{
ViewBag.Title = "AdminsAndAgentsList";
}
<h2>AdminsAndAgentsList</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.FirstName)
</th>
<th>
#Html.DisplayNameFor(model => model.LastName)
</th>
<th>
#Html.DisplayNameFor(model => model.UserId)
</th>
<th></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.UserId)
</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>
I do a successful build and when I run the web page I get the following error:
The model item passed into the dictionary is of type'System.Data.Entity.Infrastructure.DbQuery1[My.Models.UserProfile]',
but this dictionary requires a model item of type
'System.Collections.Generic.IEnumerable1[My.Models.ViewModels.AdminsAndAgentsListVM]'.
If I recreate the view but strongly type it agains the UserProfile, it works fine. So how to re work this so I can strongly type against my ViewModel instead? Please provide examples if possible. I am new to C# and MVC and really benefit from the seeing the code first hand. Much appreciate the help!
EDIT -----------------------------
Here is the object for the UserProfile:
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
}
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
[Required]
[ReadOnly(true)]
[DisplayName("SubscriberID")]
public int? SubscriberId { get; set; } //Foreign key
[StringLength(50, ErrorMessage = "The {0} must be at least {2} characters long.")]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[StringLength(50, ErrorMessage = "The {0} must be at least {2} characters long.")]
[Display(Name = "Last Name")]
public string LastName { get; set; }
//public DateTime DOB { get; set; }
[DataType(DataType.Date)]
public DateTime? DOB { get; set; } //This allows null
public bool? Gender { get; set; }
[Required]
[MaxLength(250)]
[EmailAddress]
public string Email { get; set; }
[MaxLength(250)]
[EmailAddress]
[NotEqualTo("Email", ErrorMessage = "Alt Email and Email cannot be the same.")]
public string AltEmail { get; set; }
[MaxLength(250)]
[EmailAddress]
public string FormEmail { get; set; }
public Address Address { get; set; }
[MaxLength(20)]
public string Telephone { get; set; }
[MaxLength(20)]
public string Mobile { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime DateAdded { get; set; }
[DataType(DataType.DateTime)]
public DateTime? LastLoginDate { get; set; }
public bool? OffersOptIn { get; set; } //any offers we may have with us or partners
public bool? NewsOptIn { get; set; } //newsletter
public bool? SubscriptionOptIn { get; set; } //account, technical, renewal notices, pp invoices, pp receipts
public bool? OrderOptIn { get; set; } //orders - workflow notices
[DataType(DataType.DateTime)]
public DateTime? LastUpdatedAccountDate { get; set; } //Last time user updated contact info
}
Try this. It will cast your query into your view model.
var viewModel = _db.UserProfiles
.Where(x => admins.Contains(x.UserName))
.Select(x => new AdminsAndAgentsListVM {
FirstName = x.FirstName,
LastName = x.LastName,
UserId = x.UserId});
You're passing the view your query, not your model.
Execute the query as you have it
var query = _db.UserProfiles
.Where(x => admins.Contains(x.UserName));
Then instantiate and populate your view model
var viewModels = new List<AdminsAndAgentsListVM>();
foreach (var item in query)
{
var viewModel = new AdminsAndAgentsListVM();
viewodel.FirstName = item.FirstName;
viewodel.LastName = item.LastName;
viewodel.UserId = item.UserId;
viewModels.Add(viewModel);
}
return View(viewModels);
This assumes, of course, that a UserProfile and AdminsAndAgentsListVM have matching properties.
Change your return line to:
return View(viewModel.AsEnumerable());
You aren't selecting your ViewModel. You need to do a Select(x => new AdminsAndAgentsListVM on your query. I would also do ToList() on there.