How to save data dynamically from a View in MVC5 - c#

I want to build a Questionnaire MVC5 project.
I have a MSSQL database with several tables like: Employee, Questions, Results ...
I made a new MVC5 project, I add it the model base on my database and I manage all CRUD operations need it.
Now I made an view for Questionar :
#model IEnumerable<ChestionarMVC.Models.FormQuestion>
#{
ViewBag.Title = "Chestionar";
}
<h2>Chestionar</h2>
#foreach (var item in Model)
{
#Html.Partial("_Chestionar",item)
}
<input id="Submit1" type="submit" value="submit" />
And a partialView to show each question with 2 text area, one for the answer and one for some aditional info :
#model ChestionarMVC.Models.FormQuestion
<table border="1" style="width:100%">
<tr>
<td>
#Html.DisplayFor(modelItem => Model.Question)
</td>
</tr>
<tr>
<td>
Raspuns <br />
<textarea id="TextArea1" rows="2" cols="80" style="width:800px; height:100px;"></textarea>
</td>
</tr>
<tr>
<td>
Document <br />
<textarea id="TextArea2" rows="2" cols="80" style="width:400px"></textarea>
</td>
</tr>
</table>
Now I want to save in the tblResults the QuestionID, Answer and Document.
In webforms I made a usercontrol, then I used Foreach usercontrol , and saved to database.
In MVC how can I save all?
This is the QuestionsModel:
namespace ChestionarMVC.Models
{
using System;
using System.Collections.Generic;
public partial class FormQuestion
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public FormQuestion()
{
this.FormResults = new HashSet<FormResult>();
this.TBLPos = new HashSet<TBLPos>();
}
public int idQuestion { get; set; }
public string Question { get; set; }
public int idCategory { get; set; }
public int idPosition { get; set; }
public Nullable<int> Ordine { get; set; }
public virtual FormCategory FormCategory { get; set; }
public virtual Formular Formular { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<FormResult> FormResults { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<TBLPos> TBLPos { get; set; }
}
}
this is the ResultsMOdel:
namespace ChestionarMVC.Models
{
using System;
using System.Collections.Generic;
public partial class FormResult
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public FormResult()
{
this.Documentes = new HashSet<Documente>();
}
public int idResult { get; set; }
public int idUser { get; set; }
public int idQuestion { get; set; }
public string Answer { get; set; }
public string RefferenceDocument { get; set; }
public Nullable<System.DateTime> StampDate { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Documente> Documentes { get; set; }
public virtual Employee Employee { get; set; }
public virtual FormQuestion FormQuestion { get; set; }
}
}
this is the Questionnaire ActionResult used to generate the Questionnaire-View:
public ActionResult Chestionar()
{
var formQuestions = db.FormQuestions;
return View(formQuestions.ToList());
}

Start by creating a view model containing the properties you want for the view (note add other validation attributes as required to suit your needs)
public class QuestionVM
{
public int ID { get; set; }
public string Question { get; set; }
[Required(ErrorMessage = "Please enter and answer")]
public string Answer { get; set; }
public string Document { get; set; }
}
Then create an EditorTemplate. In /Views/Shared/EditorTemplates/QuestionVM.cshtml
#model QuestionVM
#Html.HiddenFor(m => m.ID)
#Html.HiddenFor(m => m.Question)
#Html.DisplayNameFor(m => m.Question)
#Html.DisplayFor(m => m.Question)
#Html.LabelFor(m => m.Answer)
#Html.TextAreaFor(m => m.Answer)
#Html.ValidationMessageFor(m => m.Answer)
... // ditto for Document (as for Answer)
And in the main view
#model IEnumerable<QuestionVM>
#using (Html.BeginForm())
{
#Html.EditorFor(m => m)
<input type="submit" ... />
}
Note that the EditorFor() method will generate the html for each Question based on the template, and importantly will add the correct name attributes that enable your form controls to be posted back and bound to your model
The in the controller
public ActionResult Chestionar()
{
// Get data model and map to view models
var model = db.FormQuestions.Select(q => new QuestionVM()
{
ID = q.idQuestion,
Question = q.Question,
Answer = .....,
Document = .... // see notes below
};
return View(model);
}
[HttpPost]
public ActionResult Chestionar(IEnumerable<QuestionVM> model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Get the data model again, map the view model properties back to the data model
// update properties such as user and date
// save and redirect
}
Side note: your question indicates an (one) Answer and Document for each question, yet you current models for the Question have a collection (ICollection<FormResult> FormResults) containing properties for the Answer and RefferenceDocument so its not clear if you want to add multiple answers and documents for each Question, or just one.

Related

How in an ASP.NET MVC view do I convert primary keys to actual names / descriptor fields

I am looking to display an ASP.NET MVC view to show my booking table data via a view.
However the view outputs the Movie and Venue data simply as ID values corresponding to their primary keys. This is not very readable for humans of course. I've tried for approximately 4 hours to get this resolved and what should be simple isn't. I can't find a way to make that ID into the movie or venue name. Any help is appreciated this is for some coursework that is due in a few days. Thanks
Markup of my view:
#model IEnumerable<MovieClubSite.Controllers.Booking>
#{
ViewBag.Title = "Booking Manager";
}
<br />
<h2>Booking Manager</h2>
<ul class="list-group">
<li class="list-group-item list-group-item-dark">Pick a showing</li>
#foreach (var item in Model)
{
<li class="list-group-item list-group-item-action list-group-item-info">
<div class="float-left">
<b>Username:</b> #Html.DisplayFor(modelItem => item.CustomerReference)<br />
<b>Movie:</b>#Html.DisplayFor(modelItem => item.Showing.MovieID)<br />
<b>Venue:</b>#Html.DisplayFor(modelItem => item.Showing.VenueID)<br />
<b>Date/Time:</b>#Html.DisplayFor(modelItem => item.Showing.ShowingDateTime)<br />
</div>
<div class="float-right">
<br />
</div>
</li>
}
<a href='#Url.Action("Create", "Bookings")' class="list-group-item list-group-item-action list-group-item-success">
Add a new showing
</a>
</ul>
Code for my Booking controller:
// GET: Bookings
public ActionResult Index()
{
var bookings = db.Bookings
.Include(b => b.Member)
.Include(b => b.Showing);
return View(bookings.ToList());
}
BOOKING CLASS
namespace MovieClubSite.Controllers
{
using System;
using System.Collections.Generic;
public partial class Booking
{
public int BookingID { get; set; }
public int ShowingID { get; set; }
public int MemberID { get; set; }
public string CustomerReference { get; set; }
public virtual Member Member { get; set; }
public virtual Showing Showing { get; set; }
}
}
MOVIE CLASS
namespace MovieClubSite.Controllers
{
using System;
using System.Collections.Generic;
public partial class Movie
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Movie()
{
this.Showings = new HashSet<Showing>();
}
public int MovieID { get; set; }
public string MovieName { get; set; }
public Nullable<int> MovieYear { get; set; }
public string MovieImage { get; set; }
public string MovieDescription { get; set; }
public string MovieLinkWiki { get; set; }
public string MovieLinkIMDB { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Showing> Showings { get; set; }
}
}
SHOWING CLASS
namespace MovieClubSite.Controllers
{
using System;
using System.Collections.Generic;
public partial class Showing
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Showing()
{
this.Bookings = new HashSet<Booking>();
}
public int ShowingID { get; set; }
public int MovieID { get; set; }
public int VenueID { get; set; }
public System.DateTime ShowingDateTime { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Booking> Bookings { get; set; }
public virtual Movie Movie { get; set; }
public virtual Venue Venue { get; set; }
}
}
VENUE CLASS
namespace MovieClubSite.Controllers
{
using System;
using System.Collections.Generic;
public partial class Venue
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Venue()
{
this.Showings = new HashSet<Showing>();
}
public int VenueID { get; set; }
public string VenueDescription { get; set; }
public int VenueCapacity { get; set; }
public string VenueName { get; set; }
public string VenueImage { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Showing> Showings { get; set; }
}
}
Since you are already including the navigation properties in your model, you could just change the displayed properties:
<b>Movie:</b>#Html.DisplayFor(modelItem => item.Showing.Name)<br />
<b>Venue:</b>#Html.DisplayFor(modelItem => item.Showing.Name)<br />
Where .Name is the actual property that holds the value, you want to show.
But, as a rule you do not want to pass your whole entities down to your view, ever. A better practice is to create a view model class and only select the properties you need:
public class BookingViewModel
{
public int BookingId { get; set; }
public string CustomerReference { get; set; }
public string Venue { get; set; }
public string Movie { get; set; }
public DateTime ShowingDateTime { get; set; }
}
// GET: Bookings
public ActionResult Index()
{
var bookings = db.Bookings
.Include(b => b.Member)
.Include(b => b.Showing)
.ThenInclude(sh => sh.Movie)
.ThenInclude(sh => sh.Venue)
.Select(x => new BookingViewModel {
BookingId = x.BookingId,
CustomerReference = x.CustomerReference,
Venue = x.Showing.Venue.VenueName,
Movie = x.Showing.Movie.MovieName,
ShowingDateTime = x.Showing.ShowingDateTime
})
.ToList()
return View(bookings);
}
Then you just need to update your view model declaration and properties to fit:
#model IEnumerable<MovieClubSite.Models.BookingViewModel>
I've solved it with help from the posters above and a bit of searching online. Its taken about 4 hours to solve this, that's programming tho :/ Hopefully this post will help others with the same issue.
VIEW
public ActionResult Index()
{
var bookings = db.Bookings
.Include(b => b.Member)
.Include(b => b.Showing)
.Include(x => x.Showing.Movie)
.Include(x => x.Showing.Venue)
.ToList();
return View(bookings);
}
CONTROLLER
model IEnumerable<MovieClubSite.Controllers.Booking>
#{
ViewBag.Title = "Booking Manager";
}
<br />
<h2>Booking Manager</h2>
<ul class="list-group">
<li class="list-group-item list-group-item-dark">Pick a showing</li>
#foreach (var item in Model)
{
<li class="list-group-item list-group-item-action list-group-item-info">
<div class="float-left">
<b>Username: </b> #Html.DisplayFor(modelItem => item.CustomerReference)<br />
<b>Movie: </b>#Html.DisplayFor(modelItem => item.Showing.Movie.MovieName)<br />
<b>Venue: </b>#Html.DisplayFor(modelItem => item.Showing.Venue.VenueName)<br />
<b>Date/Time: </b>#Html.DisplayFor(modelItem => item.Showing.ShowingDateTime)<br />
</div>
<div class="float-right">
<br />
</div>
</li>
}
<a href='#Url.Action("Create", "Bookings")' class="list-group-item list-group-item-action list-group-item-success">
Add a new showing
</a>
</ul>
model IEnumerable<MovieClubSite.Controllers.Booking>
#{
ViewBag.Title = "Booking Manager";
}
<br />
<h2>Booking Manager</h2>
<ul class="list-group">
<li class="list-group-item list-group-item-dark">Pick a showing</li>
#foreach (var item in Model)
{
<li class="list-group-item list-group-item-action list-group-item-info">
<div class="float-left">
<b>Username: </b> #Html.DisplayFor(modelItem => item.CustomerReference)<br />
<b>Movie: </b>#Html.DisplayFor(modelItem => item.Showing.Movie.MovieName)<br />
<b>Venue: </b>#Html.DisplayFor(modelItem => item.Showing.Venue.VenueName)<br />
<b>Date/Time: </b>#Html.DisplayFor(modelItem => item.Showing.ShowingDateTime)<br />
</div>
<div class="float-right">
<br />
</div>
</li>
}
<a href='#Url.Action("Create", "Bookings")' class="list-group-item list-group-item-action list-group-item-success">
Add a new showing
</a>
</ul>

CRUD operations for multiple related records in different tables from single view in MVC C#

I was hoping I could get some assistance on this functionality I am trying to achieve. I have searched, but i do not seem to be able to translate the examples/tutorials I have found to make it work in my own code.
For brevity, let's say I have 2 Models with a one to many relationship between the two. Let's say it is parents and children. One parent can have zero or many children. Each child can only have 1 parent:
namespace MyApp.Models
{
public partial class Parent
{
[Key]
public int parent_id { get; set; }
public string parent_name { get; set; }
public string parent_address { get; set; }
public ICollection<Child> Child { get; set; }
}
public partial class Child { get; set; }
{
[Key]
public int child_id { get; set; }
public int parent_id { get; set; }
public string child_name { get; set; }
public string child_allergies { get; set; }
public virtual Parent parent { get; set; }
}
}
Entity created the tables in the database properly, assigning primary/foreign keys where they needed to be.
I put the common fields in a viewmodel to render them in my view:
using MyApp.Models;
namespace MyApp.ViewModels
{
public class ParentChildViewModel
{
public int parent_id { get; set; }
public string parent_name { get; set; }
public string parent_address { get; set; }
public string child_name { get; set; }
public string child_allergies { get; set; }
}
}
I have my view written as below:
#model MyApp.ViewModels.ParentChildViewModel
#using (Html.BeginForm())
{
<div>
#Html.LabelFor(model => model.parent_name)
#Html.EditorFor(model => model.parent_name)
</div>
<div>
#Html.LabelFor(model => model.parent_address)
#Html.EditorFor(model => model.parent_address)
</div>
<table id="child_table">
#{ Html.RenderPartial("_children"); }
</table>
<div>
<button id="add">Add Child</button>
<button id="rem">Remove Child</button>
</div>
<div>
<input type="submit" value="Create" />
</div>
}
<script type="text/javascript">
$("#add").click(function () {
$.ajax({
url: "#Url.Action("BlankChRow")",
cache: false,
success: function (html) {
$("#child_table").append(html);
}
});
return false;
});
$("#rem").click(function () {
$("#child_table tbody tr:last")
.remove();
return false;
});
</script>
I created a partial view for the child so I can repeat those:
#model MyApp.ViewModels.ParentChildViewModel
#using (Html.BeginCollectionItem("children"))
{
<tr>
<td>
<div>
#Html.LabelFor(model => model.child_name)
#Html.EditorFor(model => model.child_name)
</div>
</td>
<td>
<div>
#Html.LabelFor(model => model.child_allergies)
#Html.EditorFor(model => model.child_allergies)
</div>
</td>
</tr>
}
Then, in my controller (this is where I am stuck):
private ApplicationDbContext db = new ApplicationDbContext();
public ActionResult BlankChRow()
{
return PartialView("_children");
}
public ActionResult Create()
{
return View(new ParentChildViewModel());
}
[HttpPost]
public ActionResult Create(ParentChildViewModel pcvm)
{
var parent = new Parent()
{
parent_id = pcvm.parent_id,
parent_name = pcvm.parent_name,
parent_address = pcvm.parent_address
};
var child = new Child()
{
parent_id = pcvm.parent_id,
child_name = pcvm.child_name,
child_allergies = pcvm.child_allergies
};
if (ModelState.IsValid)
{
db.Parent.Add(parent);
db.Child.Add(child);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(pcvm);
}
I have experimented a few different ways of doing this... but I am unable to get this working the way I would like. Ideally, the View can be brought up, and while they enter the Parent's data, they could add one or many children. The children that are added would each be their own record in the Child table/entity while having the appropriate parent_id.
Any help is appreciated.
I see that you want to generate a list of children for a given parent but your viewmodel doesn't look so.
using MyApp.Models;
namespace MyApp.ViewModels
{
public class ParentChildViewModel
{
public int parent_id { get; set; }
public string parent_name { get; set; }
public string parent_address { get; set; }
public string child_name { get; set; }
public string child_allergies { get; set; }
}
}
Try a viewmodel with a list of children.
Something like this :
using MyApp.Models;
namespace MyApp.ViewModels
{
public class ParentChildViewModel
{
public int parent_id { get; set; }
public string parent_name { get; set; }
public string parent_address { get; set; }
public IEnumerable<Child> children { get; set; }
}
}
I'm supposing here that you just want to iterate over the list. That's why I use the IEnumerable interface instead of the IList interface.
To add a child in the list, you could call the "Create" function of your controller and pass the parent_id of the new child.
In your controller you can therefore create a new child for a given parent in the database using your application context.
Once the database transaction done, you can create a new ParentChildViewModel and fulfill it with the corresponding children and return it to the view.

The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[]', but this dictionary requires a model item of type ''

Here is my view with viewModel:-
#model _24By7CallAndBPOPvtLtd.Models.JobDetailandApplyModel
<table class="table table-striped">
#foreach (var item in Model.jobdetail)
{
<tr>
<td>
#item.job_title
</td>
<td>
<i class="glyphicon glyphicon-pencil"></i>
<i class="glyphicon glyphicon-remove" style="color:red"></i>
</td>
</tr>
}
</table>
<table class="table table-striped">
#foreach (var item in Model.jobapplied)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.User_Account.Full_Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Job_Det.job_title)
</td>
<td>
Download
</td>
</tr>
}
</table>
Here is my Controller:-
public ActionResult Test1()
{
var jobs=db.Job_Det.ToList();
return View(jobs);
}
public ActionResult Test2()
{
db.Job_Applied.ToList();
return View("Test1");
}
Here is my ViewModel:-
public class JobDetailandApplyModel
{
public IEnumerable<Job_Det> jobdetail { get; set; }
public IEnumerable<Job_Applied> jobapplied { get; set; }
}
public partial class Job_Applied
{
public int JobApplied_ID { get; set; }
public int Employeer_ID { get; set; }
public int Job_ID { get; set; }
public int User_ID { get; set; }
public Nullable<int> Cv_Id { get; set; }
public Nullable<int> status_Id { get; set; }
public System.DateTime date_applied { get; set; }
public Nullable<System.TimeSpan> time_applied { get; set; }
public string timestamp_applied { get; set; }
public virtual Employeer Employeer { get; set; }
public virtual Job_Det Job_Det { get; set; }
public virtual Status Status { get; set; }
public virtual User_CV User_CV { get; set; }
public virtual User_Account User_Account { get; set; }
}
What is the problem in this code?.i m passing two models in one view using viewModel, i need to display two lists in one view..
Try
var jobs = db.Job_Det.ToList();
var jobApplied = db.Job_Applied.Include(x => x.Job_Det).ToList();
JobDetailandApplyModel model = new JobDetailandApplyModel() {
jobdetail = jobs,
jobapplied = jobApplied
}
return View(model);
also, not sure if this will make a difference but replace
#Html.DisplayFor(modelItem => item.Job_Det.job_title)
with
#item.Job_Det.job_title
You will need to initialise the model properly and assign values to each of the 2 properties. Change your controller to something like this:
var model = new JobDetailAndApplyModel();
model.jobdetail = db.Job_Det.ToList();
model.jobapplied = db.Job.Applied.ToList();
return view(model);
EDIT: the Test2 controller you have will not do much, when you pass it a string like that it just uses the view with a name the same as that string. It is passed no model, so you will get a null reference error. You will need something like this to make that one work:
return View("Test1", model);
Using the other code from above to create the model object.
Your view is expecting a model of type JobDetailandApplyModel
Test1 provide a model of type
List < Job_Det >
Test2 provides NO model

Nested lookup in MVC and Entity Framework

I'm running into a problem with a project I'm working on. I'm relatively new to MVC so I'm hoping this is something simple!
I'm trying to perform an edit on an object, but the object in question has a couple of ICollections that it can't seem to bind to.
The class I'm trying to update is called 'Key', and the definition is:
public partial class Key
{
public Key()
{
this.KeyFields = new HashSet<KeyField>();
this.KeyServices = new HashSet<KeyService>();
}
public int ID { get; set; }
public System.Guid APIKey { get; set; }
public string SiteURL { get; set; }
public bool Active { get; set; }
public virtual ICollection<KeyField> KeyFields { get; set; }
public virtual ICollection<KeyService> KeyServices { get; set; }
}
The KeyField and KeyService classes are:
public partial class KeyField
{
public int ID { get; set; }
public int SiteKey { get; set; }
public int Field { get; set; }
public bool Active { get; set; }
public virtual Field Field1 { get; set; }
public virtual Key Key { get; set; }
}
public partial class KeyService
{
public int ID { get; set; }
public int SiteKey { get; set; }
public int Service { get; set; }
public bool Active { get; set; }
public virtual Key Key { get; set; }
public virtual Service Service1 { get; set; }
}
The related Service class is:
public partial class Service
{
public Service()
{
this.KeyServices = new HashSet<KeyService>();
}
public int ID { get; set; }
public string Name { get; set; }
public int Service_View { get; set; }
public virtual ICollection<KeyService> KeyServices { get; set; }
public virtual View View { get; set; }
[NotMapped]
public bool IsSelected { get; set; }
}
and the related Field class is:
public partial class Field
{
public Field()
{
this.KeyFields = new HashSet<KeyField>();
}
public int ID { get; set; }
public string Title { get; set; }
public int Field_View { get; set; }
public virtual View View { get; set; }
public virtual ICollection<KeyField> KeyFields { get; set; }
[NotMapped]
public bool IsSelected { get; set; }
}
The View is using a custom model called 'KeyDetailsModel'. It contains an instance of the 'Key' object and 2 IEnumerables. These are there so that I can output all the fields and services in the database into a list of checkboxes on the View. The fields and services that should be preselected on the View are ICollections in the Key class.
the definition is:
public class KeyDetailsModel
{
public Key Key { get; set; }
public IEnumerable<Field> Fields { get; set; }
public IEnumerable<Service> Services { get; set; }
}
The is the code in the controller that sets up the 'display' page:
public ViewResult KeyDetails(int id)
{
var fieldChannel = new Repo<Field>();
var serviceChannel = new Repo<Service>();
//gets the key information
var key = _keyChannel.GetById(id);
//gets all the fields in the database
var fields = fieldChannel.GetAll();
//gets all the services in the database
var services = serviceChannel.GetAll();
//gets the KeyFields for the key, and sets the IsSelected flag for the related Field
foreach (var f in fields)
{
var selectedField = (from sf in key.KeyFields
where sf.ID == f.ID && sf.SiteKey == id
select sf).FirstOrDefault();
if (selectedField != null)
{
f.IsSelected = true;
}
}
//gets the KeyServices for the key, and sets the IsSelected flag for the related Service
foreach (var f in services)
{
var selectedService = (from ss in key.KeyServices
where ss.ID == f.ID && ss.SiteKey == id
select ss).FirstOrDefault();
if (selectedService != null)
{
f.IsSelected = true;
}
}
//create the model
var KeyDetailsModel = new KeyDetailsModel
{
Fields = fields,
Key = key,
Services = services
};
return View(KeyDetailsModel);
}
This is the method in the controller that will perform the edit:
[HttpPost]
public ActionResult KeyDetails(KeyDetailsModel KeyDetailsModel)
{
if (KeyDetailsModel != null && ModelState.IsValid)
{
return View(KeyDetailsModel);
}
else
{
return View(KeyDetailsModel);
}
}
Finally, the cshtml page is:
#model EdinburghNapier.EAWebLayer.Admin.Models.KeyDetailsModel
#{
ViewBag.Title = "KeyDetails";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Key Details</h2>
#using (Html.BeginForm("KeyDetails", "Key", FormMethod.Post))
{
<fieldset>
<legend>Key</legend>
<div class="display-label">
#Html.DisplayNameFor(model => model.Key.APIKey)
</div>
<div class="display-field">
#Html.EditorFor(model => model.Key.APIKey)
</div>
<div class="display-label">
#Html.DisplayNameFor(model => model.Key.SiteURL)
</div>
<div class="display-field">
#Html.EditorFor(model => model.Key.SiteURL)
</div>
<div class="display-label">
#Html.DisplayNameFor(model => model.Key.Active)
</div>
<div class="display-field">
#Html.EditorFor(model => model.Key.Active)
</div>
<div class="display-field">
<ul>
#foreach (var f in Model.Fields)
{
<li>
<input type="checkbox"
name="Key.KeyFields" value="#f.ID" id="Field_#f.ID"
checked="#f.IsSelected"/>
<label for="#f.ID">#f.Title</label>
</li>
}
</ul>
</div>
<div class="display-field">
<ul>
#foreach (var f in Model.Services)
{
<li>
<input type="checkbox"
name="Key.KeyServices" value="#f.ID" id="Service_#f.ID"
checked="#f.IsSelected"/>
<label for="#f.ID">#f.Name</label>
</li>
}
</ul>
</div>
</fieldset>
#Html.HiddenFor(model => model.Key.ID)
<input type="submit" value="Save"/>
}
What is happening when I submit the form is that the 'KeyFields' and 'KeyServices' properties of the 'Key' class in the 'KeyDetailsModel' are always 0. The edit screen doesn not seem to be able to bind the check box lists to the properties.
One consequence of this is that the status of the ModelState is always false when I submit.
I've been bashing my head against a wall over this for a couple of days now, hope someone can help! I appreciate that I may have supplied too much code and too little explanation - please let me know if that's the case!
You are binding your checkboxes in incorrect way, you should read a bit more about it, you can check some article like this:
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
Your code should be changed, so that each checkbox had an indexer in its name:
<div class="display-field">
<ul>
#for (var i = 0; i < Model.Fields.Count(); i++)
{
<li>
<input type="checkbox"
name="#Html.NameFor(x=>x.Fields[i])" value="#Model.Fields[i].ID" id="#Html.IdFor(x=>x.Fields[i])"
checked="#Model.Fields[i].IsSelected" />
<label for="#Html.NameFor(x=>x.Fields[i])">#Model.Fields[i].Title</label>
</li>
}
</ul>
</div>
<div class="display-field">
<ul>
#for (var i = 0; i < Model.Services.Count(); i++)
{
<li>
<input type="checkbox"
name="#Html.NameFor(x=>x.Services[i])" value="#Model.Services[i].ID" id="#Html.IdFor(x=>x.Services[i])"
checked="#Model.Services[i].IsSelected" />
<label for="#Html.NameFor(x=>x.Services[i])">#Model.Services[i].Name</label>
</li>
}
</ul>
</div>
You will also have to make your Services and Fields properties an array here:
public class KeyDetailsModel
{
public Key Key { get; set; }
public Field[] Fields { get; set; }
public Service[] Services { get; set; }
}
Alternately, you can just use #Html.CheckBoxFor(x=>x.Fields[i].IsSelected inside the cycle, stick to Mvc Helpers, they really make the difference.

Asp.net ViewModel returning null

I am fairly new to Asp.Net Mvc5 as well as c# and I am hoping to acquire a few pointers here, I have been trying to figure out why I was getting the following error :
The model item passed into the dictionary is of type 'System.Data.Entity.DbSet1[SoccerTeams.Models.Player]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[SoccerTeams.Models.ViewModels.TeamViewModel]'.
However now after debugging I realized that the ViewModel object is actually returning null values for all the items. I have created a page that works correctly for adding a team as well as players for that team. In my database the teams has one table and all the players are in another table which each player has the teams name with it (player and team name are in two separate columns) so it can be associated with the correct team. I have created a ViewModel and I am attempting to call that ViewModel and be able to return all the players to the view so I can show them in a list.
My Team controller for the "View all players view" is as follows:
public ActionResult ViewAllPlayers()
{
TeamViewModel teamView = new TeamViewModel();
return View(teamView);
}
My Team Model is as follows:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace SoccerTeams.Models
{
// This teams class will be used to represent the teams in the database
public class Team
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string TeamName { get; set; }
public string Coach { get; set; }
public string Conference { get; set; }
}
}
My Player Model is as follows:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace SoccerTeams.Models
{
public class Player
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public Guid TeamId { get; set; }
public string Name { get; set; }
}
}
My ViewModel is as follows:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace SoccerTeams.Models.ViewModels
{
public class TeamViewModel
{
public string TeamName { get; set; }
public string Coach { get; set; }
public string Conference { get; set; }
public List<Player> Players { get; set; }
}
}
My CreateTeam action is as follows:
public async Task<ActionResult> Create(TeamViewModel model, string addfiverows)
{
if (ModelState.IsValid)
{
if (!string.IsNullOrEmpty(addfiverows)) return View(model);
var team = new Team { TeamName = model.TeamName, Coach = model.Coach, Conference = model.Conference };
db.Teams.Add(team);
var result = await db.SaveChangesAsync();
if(result > 0)
{
foreach(var player in model.Players)
{
var p = new Player { Name = player.Name, Id = team.Id };
db.Players.Add(p);
}
result = await db.SaveChangesAsync();
}
if(result > 0) return RedirectToAction("Index");
}
return View();
}
My View I am trying to display on is as follows:
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
#model SoccerTeams.Models.ViewModels.TeamViewModel
#{
ViewBag.Title = "View All Players";
}
<h2>View All Players</h2>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.TeamName)
</th>
<th>
#Html.DisplayNameFor(model => model.Players)
</th>
<th></th>
</tr>
#foreach (var item in Model.Players)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.TeamName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Players)
</td>
</tr>
}
</table>
From researching I thought the problem had something to do with the #model IEnumerable<SoccerTeams.Models.ViewModels.TeamViewModel>. However after talking with #Tony Bao challenging my understanding I noticed that the Viewmodel is actually returning the fields however with null values.
I am also seeking any guides or tutorials as I am not only looking for a solution but also a better understanding of why this happens and how to use the ViewModel properly.
First add Player collection to Team model:
public class Team
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string TeamName { get; set; }
public string Coach { get; set; }
public string Conference { get; set; }
public virtual ICollection<Player> Players { get; set; }
}
and in Player model add Team:
public class Player
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string Name { get; set; }
public Guid TeamId { get; set; }
public virtual Team Team {get; set;}
}
and in ViewAllPlayers action:
public ActionResult ViewAllPlayers(Guid id)
{
var team = db.Teams.Include(t => t.Players).Single(t => t.Id == id);
TeamViewModel teamView = new TeamViewModel
{
TeamName = team.TeamName,
Coach = team.Coach,
Conference = team.Conference,
Player = new List<Player>(team.Players)
};
return View(teamView);
}
You can view DisplayFor Templates in your scenario. There are tons of materials on displaytemplates(for display) and editortempates(for insert and update). you can search on google
Here what i will propose
public ActionResult ViewAllPlayers()
{
//this should be from your database
var teamviewer = new TeamViewModel();
teamviewer.TeamName = "t1";
teamviewer.Players = new List<Player>() { new Player { PlayerName = "p1" }, new Player { PlayerName = "p2" } };
return View();
}
Your view Models
public class TeamViewModel
{
public string TeamName { get; set; }
public string Coach { get; set; }
public string Conference { get; set; }
public List<Player> Players { get; set; }
}
public class Player
{
public string PlayerName { get; set; }
}
Create a Displayfor template here
Views/Shared/DisplayTemplates/Player.cshtml
Player.cshtml
#model SoccerTeams.Models.Player
#Html.DisplayFor(m=>m.PlayerName)
On your index page
#model SoccerTeams.Models.TeamViewModel
<h2>View All Players</h2>
<table class="table">
<tr>
<td>
#Html.DisplayFor(modelItem => modelItem.TeamName)
</td>
<td>
#Html.DisplayFor(modelItem=>modelItem.Players)
</td>
</tr>
</table>

Categories