I’m trying to use complex models with Kendo grid edit popup. When submitting ALResults object properties are always null. It works fine when I’m not using Kendo. Is there a problem with kendo complex model submitting?
public class InitialApplicantLevel2Model
{
public InitialApplicantLevel2Model()
{
alResultsModel = new ALResults();
}
public int InitialApplicantLevel2ID { get; set; }
public string ApplicantName { get; set; }
public string ContactNumber { get; set; }
public string School { get; set; }
[Required(ErrorMessage="Ref No. required.")]
public int? EnquiryID { get; set; }
public ALResults alResultsModel { get; set; }
}
public class ALResults
{
public int ResultsID { get; set; }
public int InitialApplicantLevel2ID { get; set; }
public string Stream { get; set; }
public string Grading { get; set; }
public string IndexNo { get; set; }
public int? Year { get; set; }
public int? Attempt { get; set; }
public double? ZScore { get; set; }
public string Medium { get; set; }
}
#model SIMS.Models.StudentIntake.InitialApplicantLevel2Model
<tr>
<td>Year: </td>
<td>#Html.TextBoxFor(o=>o.alResultsModel.Year)</td>
<td>Index No: </td>
<td>#Html.TextBoxFor(o=>o.alResultsModel.IndexNo)</td>
<td>Medium: </td>
<td>#Html.TextBoxFor(o=>o.alResultsModel.Medium)</td>
</tr>
<tr>
<td>Stream: </td>
<td>#Html.TextBoxFor(o=>o.alResultsModel.Stream)</td>
<td>Attempt: </td>
<td>#Html.TextBoxFor(o=>o.alResultsModel.Attempt)</td>
<td>Zscore: </td>
<td>
#Html.TextBoxFor(o=>o.alResultsModel.ZScore)
</td>
</tr>
I found the answer here
Unfortunately Kendo UI doesn't support Class Composition / View Models
containing complex objects, your View Models need to be completely
flat to avoid unexpected behaviour.
Related
I have an ASP MVC 5 Web application with code first migrations enabled. I have two tables/classes named Post.cs and PostSubject.cs.
Post.cs
public class Post
{
public int Id { get; set; }
[Required(ErrorMessage="Title is required")]
[MaxLength(255,ErrorMessage="Title too long")]
public string Title { get; set; }
[MaxLength(1023,ErrorMessage="Description too long")]
[Required(ErrorMessage = "Description is required")]
public string Description { get; set; }
public PostSubject PostSubject { get; set; }
[Display(Name="Stream")]
[Required(ErrorMessage="Choose a Stream")]
public int PostSubjectId { get; set; }
[NotMapped]
public HttpPostedFileBase Attachment { get; set; }
}
PostSubject.cs
public class PostSubject
{
public short Id { get; set; }
public string SubjectName { get; set; }
}
I am trying to load all the Posts along with their PostSubjects in the index view.
Index Action
[HttpGet]
public ActionResult Index()
{
var contributions =_context.Posts.Include(m=>m.PostSubject).ToList();
return View(contributions);
}
Index Html
#model IEnumerable<Cluster_University.Models.Post>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<table>
<tr>
<td>Title</td>
<td>Descripttion</td>
<td>Subject</td>
</tr>
#foreach (var posts in Model)
{
<tr>
<td>#posts.Title</td>
<td>#posts.Description</td>
<td>#posts.PostSubject.SubjectName</td>
</tr>
}
</table>
Problem: I have a null reference error at #Posts.PostSubject.SubjectName
Please make me understand the problem in my code.
I have the following classes defined in my project
public class PolicyDocs
{
[Key]
[Required]
[MaxLength(20)]
public string PolicyDocID { get; set; }
[DisplayName("Date Revised")]
public DateTime DateRevised { get; set; }
[MaxLength(500)]
[DisplayName("Document Name")]
public string DocumentName { get; set; }
[MaxLength(10)]
[DisplayName("Version")]
public string VersionNo { get; set; }
[MaxLength(255)]
[DisplayName("Location")]
public string DocumentPath { get; set; }
public string CategoryID { get; set; }
public ICollection<PolicyDocumentsCatagory> PolicyDocumentsCatagory { get; set; }
}
and
public class PolicyDocumentsCatagory
{
[Key]
[MaxLength(20)]
//[Required]
public string CategoryID { get; set; }
[MaxLength(20)]
[Required]
[DisplayName("Description")]
public string CategoryDesc { get; set; }
[DisplayName("Active")]
public Boolean IsActive{ get; set; }
}
Data is properly loaded in to the table .
Example:
PolicyDocs:
1 ,"12-Jan-2017", "Document 1","version 1",100
PolicyDocumentsCategory:
100, "Information Technology", "true".
I am using the following query in my Controller Class
_context = new ApplicationDbContext();
//PolicyDocs _Policy = new PolicyDocs();
var _LPolicies = _context.PolicyDocs.Include(i=> i.PolicyDocumentsCatagory).ToList();
return PartialView("_GetAllPolicies", _LPolicies);
While running the query I am unable to see any data loaded for PolicyDocumentsCategory in "_LPolicies" object. The query is loading data in table "PolicyDocs".
Nor I am able to reference the PolicyDocumentsCategory in VIEW. I can not see the properties of "PolicyDocumentsCatagory" in the view
#model IEnumerable< MyRTOCloud.DBModels.PolicyDocs>
<div class="row">
<div>
<table id="Categories" class="table table-hover">
<thead>
<th>Category</th>
<th>Document Name</th>
<th>Release Date</th>
<th>Version</th>
</thead>
#foreach (var lp in Model)
{
<tr>
<td>#lp.PolicyDocumentsCatagory.____</td> // I can not see any peroprty of "PolicyDocumentsCatagory"
<td>#lp.DocumentName </td>
<td>#lp.DateRevised </td>
<td>#lp.VersionNo </td>
</tr>
}
</table>
</div>
</div>
Thanks,
PolicyDocumentsCatagory is a collection and you have to iterate it in itself or in your case you can combine with a separator as a string.
So, try something like that;
<td>#string.Join(",", lp.PolicyDocumentsCatagory.Select(x => x.CategoryID))</td>
Your PolicyDocs class is referencing a single CategoryID leading me to assume this is a one-to-many relationship (PolicyDocs being the many, PolicyDocumentsCatagory being the one). If that is the case, you will want to change your PolicyDocumentsCatagory property in PolicyDocs to public virtual PolicyDocumentsCatagory PolicyDocumentsCatagory { get; set; }. Note the virtual part. This is what allows EF to populate the relationship property automatically.
public class PolicyDocs
{
[Key]
[Required]
[MaxLength(20)]
public string PolicyDocID { get; set; }
[DisplayName("Date Revised")]
public DateTime DateRevised { get; set; }
[MaxLength(500)]
[DisplayName("Document Name")]
public string DocumentName { get; set; }
[MaxLength(10)]
[DisplayName("Version")]
public string VersionNo { get; set; }
[MaxLength(255)]
[DisplayName("Location")]
public string DocumentPath { get; set; }
public ICollection<PolicyDocumentsCatagory> PolicyDocumentsCatagory { get; set; }
}
public class PolicyDocumentsCatagory
{
[Key]
[MaxLength(20)]
public string CategoryID { get; set; }
[Required]
[MaxLength(20)]
public string PolicyDocID { get; set; }
[ForeignKey]
public PolicyDocs PolicyDocs { get; set; }
[MaxLength(20)]
[Required]
[DisplayName("Description")]
public string CategoryDesc { get; set; }
[DisplayName("Active")]
public Boolean IsActive{ get; set; }
}
Your models are not valid. You need to have a foreign key in your PolicyDocumentsCatagory that referencing to PolicyDocs and there is no need for CategoryID in PolicyDocs.
One PolicyDocs has many PolicyDocumentsCatagory, while one PolicyDocumentsCatagory is referenced to only one PolicyDocs. Standard one to many relationship type.
I am new to KnockoutJS and I am trying to create a 3-level model binding, for a master-detail ASP.NET MVC view.
here is the screen i am trying to implement this behavior on:
I have the following ViewModel design
public class CreateReservationViewModel
{
public string Title { get; set; }
public String LogoPath { get; set; }
public string StartDate { get; set; }
public string EndDate { get; set; }
public string StartTime { get; set; }
public string EndTime { get; set; }
public int TimeSpan { get; set; }
public int MinPersons { get; set; }
public int MaxPersons { get; set; }
public List<ReservationOptionViewModel> ReservationOptions { get; set; }
public string MessageToClient { get; set; }
public CreateReservationViewModel()
{
ReservationOptions = new List<ReservationOptionViewModel>();
}
}
public class ReservationOptionViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public int TypeId { get; set; }
public string TypeDescription { get; set; }
public string Info { get; set; }
public List<ValuesViewModel> ReservationOptionValues { get; set; }
public ReservationOptionViewModel()
{
ReservationOptionValues = new List<ValuesViewModel>();
}
}
public class ValuesViewModel
{
public int Id { get; set; }
public string ValueTitle { get; set; } // this value i cant seem to get to bind user input always defaults to whatever i set it in knockout
}
and here is my .js and .html https://jsfiddle.net/camLpdty/
Although I am able to successfully bind the first and second levels, 3rd level binding always picks up default values i.e: [({ Id: 0, ValueTitle: "this is read only and cant change"})] }
everything else seems to work...
From your jsfiddle it looks like the binding context for the Values column is a bit off. data-bind="value: Values.ValueTitle" - Here Values is an array but you're trying to bind a single input box to it. It doesn't know which element's ValueTitle to use so it's probably not bound to anything. You'll need to use a "foreach" or change how you're displaying that information
As a quick test you can try changing that last binding from:
<td>
<input class="form-control input-sm"
data-bind="value: ReservationOptionValues.ValueTitle" />
</td>
to
<td data-bind="foreach: ReservationOptionValues">
<input class="form-control input-sm" data-bind="value: ValueTitle" />
</td>
This will create an input box for every element in the Values array so you probably don't want to leave it like this, but it should highlight the knockout-context problem
I'm currently working on a page where I can list all the bookings from an account but encountering a problem. I would like to access all the properties from the room that's assigned to a booking but i'm not quite sure how to achieve this.
Bookings Table:
Room Model:
public class Room
{
public int RoomID { get; set; }
public int RoomNumber { get; set; }
public string RoomType { get; set; }
public string Image { get; set; }
public int Price { get; set; }
public int Adults { get; set; }
public int Childs { get; set; }
public bool SmokingRoom { get; set; }
public bool IsOccupied { get; set; }
public Floor Floor { get; set; }
public virtual ICollection<Booking> Bookings { get; set; }
}
Booking Model:
public class Booking
{
public int BookingID { get; set; }
public Account Account { get; set; }
public DateTime DateOfReservation { get; set; }
public DateTime Arrival { get; set; }
public DateTime Departure { get; set; }
public int Adults { get; set; }
public int Children { get; set; }
public Room Room { get; set; }
public bool CheckIn { get; set; }
public bool CheckOut { get; set; }
public string Status { get; set; }
}
Databasehandler method:
public static List<Booking> GetAllBookingsByAccount(int id)
{
HotelDbContext context = new HotelDbContext();
var bookings = context.Bookings.Where(b => b.Account.AccountID == id).ToList();
return bookings;
}
View:
#if (Model.Bookings.Count != 0)
{
<table>
<thead>
<tr>
<th>Arrival</th>
<th>Departure</th>
<th>Hotel</th>
<th>Room</th>
<th>Persons</th>
<th>Price</th>
<th>Status</th>
</tr>
</thead>
<tbody>
#foreach (var booking in Model.Bookings)
{
<tr>
<td>#booking.Arrival.ToShortDateString()</td>
<td>#booking.Departure.ToShortDateString()</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>#booking.TotalPersons</td>
<td></td>
<td>#booking.Status</td>
</tr>
}
</tbody>
</table>
}
else
{
<p>You currently do not have any bookings.</p>
}
Thank you for your time!
For lazy loading to work you need to set the properties as virtual so that EF is able to override the property. This means you do not have to call the Include method.
Update your data model to:
public class Booking
{
public int BookingID { get; set; }
public virtual Account Account { get; set; }
public DateTime DateOfReservation { get; set; }
public DateTime Arrival { get; set; }
public DateTime Departure { get; set; }
public int Adults { get; set; }
public int Children { get; set; }
public virtual Room Room { get; set; }
public bool CheckIn { get; set; }
public bool CheckOut { get; set; }
public string Status { get; set; }
}
That will enable EF to lazyload and you'll be able to access all the properties from that entity without any extra code.
You need to include the room on bookings:
var bookings = context.Bookings.Include("Room").Where(b => b.Account.AccountID == id).ToList();
I'm trying to make a view that has data from 2 tables in my database. I have looked up a couple things and I tried to use a ViewModel but I can't get it to work. Then I was thinking about using a view and a partial view to display data from the 2 tables. What is the best way using the ViewModel or using a view and a partial view?
Also if someone knows what I am doing wrong with my ViewModel and wants to help that would be awesome.
my 2 models:
public partial class prospect
{
public prospect()
{
this.result = new HashSet<result>();
}
public int id { get; set; }
public int testid { get; set; }
public string name { get; set; }
public string surname { get; set; }
public string email { get; set; }
public string cellnumber { get; set; }
public System.DateTime timestampstart { get; set; }
public Nullable<System.DateTime> timestampend { get; set; }
public Nullable<int> totalresult { get; set; }
public string birthdate { get; set; }
public string school { get; set; }
public Nullable<int> grade { get; set; }
public string address { get; set; }
public string cityandcode { get; set; }
public virtual ICollection<result> result { get; set; }
public virtual resultpersection resultpersection { get; set; }
}
public partial class resultpersection
{
public int id { get; set; }
public int prospectid { get; set; }
public int sectionid { get; set; }
public Nullable<int> result { get; set; }
public virtual prospect prospect { get; set; }
public virtual section section { get; set; }
}
The things I want to display are:
prospect.name , prospect.surname, prospect.totalresult and all the results per section of this prospect(this comes from the resultpersection table)
my viewmodel:
namespace testAptitude.Models
{
public class ResultsPerProspect
{
public List<prospect> prospect { get; set; }
public List<resultpersection> resultPerSection { get; set; }
}
}
my controller
public ActionResult Index()
{
ResultsPerProspect vm = new ResultsPerProspect();
vm.prospect = (from p in db.prospect select p).ToList();
vm.resultPerSection = (from rps in db.resultpersection select rps).ToList(); ;
List<ResultsPerProspect> viewModelList = new List<ResultsPerProspect>();
viewModelList.Add(vm);
return View(viewModelList.AsEnumerable());
}
my view
#model IEnumerable<testAptitude.Models.ResultsPerProspect>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<table>
<thead>
<tr>
<th class="col-sm-3">
#Html.DisplayNameFor(model => model.prospect)
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(model => item.prospect)
</td>
</tr>
}
</tbody>
</table>
I can't do item.prospect.name because it say's that is doesn't contain a definition for name
and what is dispalys now is this:
prospect
System.Collections.Generic.HashSet`1[testAptitude.Models.result]
Thanks in advance!
With your current code you can access it using
#Html.DisplayNameFor(model => model.FirstOrDefault().prospect)
You are passing IEnumerable<testAptitude.Models.ResultsPerProspect> to your view i.e multiple objects of class ResultsPerProspect. You would need to iterate throught this List. Each items in this list will contain definition for prospect and resultPerSection.
Or you can pass single object of class ResultsPerProspect as you are just adding single element in list.
UPDATE
You have List Of ResultsPerProspect. and each item of ResultsPerProspect has List of prospect and List of resultPerSection. So you would need to first iterate for loop over List Of ResultsPerProspect. and inside that for loop , for loop for List of prospect List of and resultPerSection
CODE
#foreach (var item in Model)
{
foreach (var pros in item.prospect)
{
<tr>
<td>
#Html.DisplayFor(model => pros.Name)
</td>
</tr>
}
}
Why don't you create a class ("Container") that consists of your two (sub)classes (let's say A and B)? You can then create a Container object and put the needed objects in Container.A and Container.B. You can then easly pass "Container" to your view and access your objects.