How to Join and Display Data from two models on a single razor page (no mvc) - syntax

I have two models (Child and Shot) that I would like to join so that I can display information on a single razor page.
This small historical view app basically has client demographics in one model (Model Name is Child) and client immunizations in another table (Model Name is Shot). I have an index page that a user will click a 'View Immunization Details' details button next to a client's ID number and client name. This will take them to the page where I would like the joined data to display.
The top half of the page will display the demographics (ID, name, address, date of birth, etc) from the Child model and the lower half of the page will display a table of any immunization info for that particular client (from the Shot model). (immunization date, dose, shot description, etc)
This is a one to many relationship between Child and Shot. One Child record can have many Shot records.
In reviewing how to get two models to display on one page I have only seen where the data did NOT need to be filtered by the results from one model. As stated above, I need to show ONLY the shots that pertain to a SINGLE client on the Details page, NOT all shots from the Shot table.
Here is my code as I have it so far:
First, the Child model:
using System.ComponentModel.DataAnnotations;
namespace HealthyShots.Models
{
public class Child
{
[Key]
[Display(Name ="Child ID")]
public int ChildId { get; set; }
public List<Shot> Shots { get; set; }
[Display(Name ="Last Name")]
public string? LastName { get; set; }
[Display(Name = "First Name")]
public string? FirstName { get; set; }
[Display(Name = "MI")]
public string? MiddleInitial { get; set; }
[Display(Name = "Full Name")]
public string FullName
{
get
{
return LastName + ", " + FirstName + " " + MiddleInitial;
}
}
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
[Display(Name = "Date of Birth")]
public DateTime? BirthDate { get; set; }
public string? Addr1 { get; set; }
public string? Addr2 { get; set; }
public string? City { get; set; }
public string? State { get; set; }
public string? Zip { get; set; }
}
}
Then, the Shot model:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace HealthyShots.Models
{
public class Shot
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int ShotId { get; set; }
[Required]
public int ChildId { get; set; }
public Child Child { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
[Display(Name = "Immunization Date")]
public DateTime? Date { get; set; }
public string? Dose { get; set; }
[Display(Name = "Shot Number")]
public int? ShotNo { get; set; }
[Display(Name ="Shot Description")]
public string? ShotDesc { get; set; }
}
}
Then, my .cs (so far) for the Details razor page:
using HealthyShots.Data;
using HealthyShots.Models;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
namespace HealthyShots.Pages.Details
{
public class DetailsModel : PageModel
{
private readonly ApplicationDbContext _db;
public Child Child { get; set; }
public Shot Shot { get; set; }
public DetailsModel(ApplicationDbContext db)
{
_db = db;
}
public void OnGet(int Id)
{
_db.Child
.Include(child => child.Shots)
.FirstOrDefault(child => child.ChildId == Id);
}
}
}
And my razor view:
#page
#model HealthyShots.Pages.Details.DetailsModel
#{
ViewData["Title"] = "Immunization Details";
}
<h2>Details</h2>
<div>
<h4>Demographic Information</h4>
<hr />
<table class="table table-bordeless" style="width:100%">
<tr>
<td style="width: 10%">
<div class="mb-3">
<label asp-for="Child.ChildId"></label>
<input asp-for="Child.ChildId" disabled class="form-control"/>
</div>
</td>
<td style="width: 30%">
<div class="mb-3">
<label asp-for="Child.LastName"></label>
<input asp-for="Child.LastName" disabled class="form-control"/>
</div>
</td>
<td style="width: 30%">
<div class="mb-3">
<label asp-for="Child.FirstName"></label>
<input asp-for="Child.FirstName" class="form-control"/>
</div>
</td>
<td style="width: 5%">
<div class="mb-3">
<label asp-for="Child.MiddleInitial"></label>
<input asp-for="Child.MiddleInitial" disabled class="form-control"/>
</div>
</td>
<td style="width: 25%">
<div class="mb-3">
<label asp-for="Child.BirthDate"></label>
<input asp-for="Child.BirthDate" type="date" disabled class="form-control"/>
</div>
</td>
</tr>
</table>
<table class="table table-bordeless" style="width:100%">
<tr>
<td style="width: 25%">
<div class="mb-3">
<label asp-for="Child.Addr1"></label>
<input asp-for="Child.Addr1" disabled class="form-control"/>
</div>
</td>
<td style="width: 25%">
<div class="mb-3">
<label asp-for="Child.Addr2"></label>
<input asp-for="Child.Addr2" disabled class="form-control"/>
</div>
</td>
<td style="width: 25%">
<div class="mb-3">
<label asp-for="Child.City"></label>
<input asp-for="Child.City" disabled class="form-control"/>
</div>
</td>
<td style="width: 5%">
<div class="mb-3">
<label asp-for="Child.State"></label>
<input asp-for="Child.State" disabled class="form-control"/>
</div>
</td>
<td style="width: 20%">
<div class="mb-3">
<label asp-for="Child.Zip"></label>
<input asp-for="Child.Zip" disabled class="form-control"/>
</div>
</td>
</tr>
</table>
<br />
<br />
<h4>Immunizations</h4>
<hr />
<table class="table table-bordeless" style="width:100%">
<tr>
<td style="width: 20%">
<div class="mb-3">
<label asp-for="Shot.Date"></label>
<input asp-for="Shot.Date" type="date" disabled class="form-control"/>
</div>
</td>
<td style="width: 20%">
<div class="mb-3">
<label asp-for="Shot.Dose"></label>
<input asp-for="Shot.Dose" disabled class="form-control"/>
</div>
</td>
<td style="width: 60%">
<div class="mb-3">
<label asp-for="Shot.ShotDesc"></label>
<input asp-for="Shot.ShotDesc" disabled class="form-control"/>
</div>
</td>
</tr>
}
</table>
<a asp-page="/Children/Index">Back to List</a>
</div>
I know I am missing code. I have googled and searched here for how the foreign key works, but still don't fully understand it. I realize that I have to tell Visual Studio that the Id (in the Child model) must be the same as the ChildId (in the Shot model), but I am not sure WHERE to do that or WHAT the correct syntax is.
I also need a wee bit of guidance on how to reference these items on the razor page. Once they are joined via foreign key, can I just access them with asp tag helpers like I would if it were a single model being referenced? Like I have done in my code so far?
Thanks in advance for any guidance you can provide. I am a beginner, so please understand that when you frame your answer.

Create relationships using conventions
In EF Core one way to define entity relationships is using conventions. This means you have to add specific properties with specific names to your entities so that EF understands the relationship between them.
Example:
public class Child
{
public int ChildId { get; set; }
public List<Shot> Shots { get; set; }
}
public class Shot
{
public int ShotId { get; set; }
public int ChildId { get; set; }
public Child Child { get; set; }
}
In the above scenario EF will automatically create a 1 to many relationship between Shot and Child. One shot is linked to one child but one child can be linked to multiple shots. Shot.ChildId will be a foreign key to Child.ChildId.
https://www.learnentityframeworkcore.com/conventions/one-to-many-relationship
Loading related data from database
Now if you want to retrieve a specific child from database with all the related shots you can do:
var child = _db.Child
.Include(child => child.Shots)
.FirstOrDefault(child => child.ChildId == Id);
Include method tells EF to populate Child.Shots with all the shots that are related to this kid.
Similarly if you when you load the shots you can do:
var shots = _db.Shot
.Include(shot => shot.Child)
.ToList();
https://learn.microsoft.com/en-us/ef/core/querying/related-data/eager
Configure relationships manually
You can also configure relationships manually:
https://www.learnentityframeworkcore.com/configuration/one-to-many-relationship-configuration

Related

ASP.NET Core 6 MVC : HTML table to view model collection

Starting up with ASP.NET Core 6 MVC and in my case I have one view which lists few properties of one object and then some other for their children in a editable table (user can edit the values)
View model has the properties and an IEnumerable of the children:
public class MyObjectViewModel
{
public String Id { get; set; }
public String Descr { get; set; }
public IEnumerable<ChildrenObject> Children { get; set; }
public MyObjectViewModel()
{
Children = Enumerable.Empty<ChildrenObject>();
}
public class ChildrenObject
{
public String? Id { get; set; }
public String? Name { get; set; }
}
}
All under the same form:
#using (Html.BeginForm("Save", "Controller", FormMethod.Post))
{
<input type="submit" value="SAVE"/>
<br />
#Html.LabelFor(model => model.Id)
#Html.TextBoxFor(model => model.Id, new { #readonly = "readonly" })
<br />
#Html.LabelFor(model => model.Descr)
#Html.EditorFor(model => model.Descr)
<br />
<table>
<tbody>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
#foreach (var child in Model.Children)
{
<tr>
<td>#child.Id</td>
<td><input class="ef-select" type="text" value="#child.Name"></td>
</tr>
}
</tbody>
</table>
}
So when the button is pressed the data is dumped back to the model to perform the SAVE action in the controller.
All ok for the simple fields (I can get the data in the controller as the view model), but not sure how to accomplish it with the table / children property...
Any simple way without needing to use JS to serialise or pick up the data from the table?
Thanks
If you want to pass id and name of ChildrenObject in table,you can try to add hidden inputs for the Id,and set name attribute for name inputs:
#{var count = 0; }
#foreach (var child in Model.Children)
{
<tr>
<td>
#child.Id
<input value="#child.Id" name="Children[#count].Id" />
</td>
<td><input class="ef-select" type="text" value="#child.Name" name="Children[#count].Name"></td>
</tr>
count++;
}

How to get multiple answers from radio buttons when clicking submit ASP.NET CORE MVC

This is a quiz with around 40 questions and each question with multiple answers.
Everything looks fine until I try to catch the answers from the view to the controller because everything returns 0 or null, Please help!
This is what I have
The Database looks like this:
Model Evaluacion:
namespace ENCB_Placement_Test_Official.Models
{
public class Evaluacion
{
public int ExamenId { get; set; }
public int RegistroId { get; set; }
public int ReactivoId { get; set; }
public string RespuestaAlumno { get; set; }
public string Pregunta { get; set; }
public string Respuesta1 { get; set; }
public string Respuesta2 { get; set; }
public string Respuesta3 { get; set; }
public string Respuesta4 { get; set; }
}
}
Services folder with a class called RepositorioExaluaciones
namespace ENCB_Placement_Test_Official.Servicios
{
public interface IRepositorioEvaluaciones
{
Task EnviarRespuesta(Evaluacion evaluacion);
Task<IEnumerable<Evaluacion>> ObtenerEvaluaciones();
}
public class RepositorioEvaluaciones: IRepositorioEvaluaciones
{
private readonly string connectionString;
public RepositorioEvaluaciones(IConfiguration configuration)
{
connectionString = configuration.GetConnectionString("DefaultConnection");
}
public async Task<IEnumerable<Evaluacion>> ObtenerEvaluaciones()
{
using var connection = new SqlConnection(connectionString);
return await connection.QueryAsync<Evaluacion>(#"SELECT Examenes.Id AS ExamenId,
RegistroId,
ReactivoId,
RespuestaAlumno,
Pregunta,
Respuesta1,
Respuesta2,
Respuesta3,
Respuesta4
FROM Examenes
INNER JOIN Reactivos
ON Examenes.ReactivoId = Reactivos.Id
WHERE Examenes.RegistroId = 1");
}
public async Task EnviarRespuesta(Evaluacion evaluacion)
{
using var connection = new SqlConnection(connectionString);
connection.Execute(#"UPDATE Examenes
SET RespuestaAlumno = #RespuestaAlumno
WHERE RegistroId = #RegistroId", evaluacion);
}
}
}
Controller Evaluaciones
namespace ENCB_Placement_Test_Official.Controllers
{
public class EvaluacionesController:Controller
{
private readonly IRepositorioEvaluaciones repositorioEvaluaciones;
public EvaluacionesController(IRepositorioEvaluaciones repositorioEvaluaciones)
{
this.repositorioEvaluaciones = repositorioEvaluaciones;
}
public async Task<IActionResult> Evaluar()
{
if (!ModelState.IsValid)
{
return View();
}
var obtenerExamen = await repositorioEvaluaciones.ObtenerEvaluaciones();
return View(obtenerExamen);
}
[HttpPost]
public async Task<IActionResult> Evaluar(Evaluacion evaluacion)
{
if (!ModelState.IsValid)
{
return View(evaluacion);
}
await repositorioEvaluaciones.EnviarRespuesta(evaluacion);
return View();
}
}
}
View Evaluar, this is where I think I have the problem because when I debug the app and click the submit button I just get nulls and zeros
#model IEnumerable<Evaluacion>
#{
ViewData["Title"] = "Start Assesment";
var contador = 1;
}
<h1>Start Assesment</h1>
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<form asp-action="Evaluar" method="post">
#foreach (var reactivo in Model)
{
<div class="mb-3">
<label class="form-label">#contador . #reactivo.Pregunta</label>
<div class="form-check">
<input class="form-check-input" type="radio" id="#reactivo.ExamenId" name="#reactivo.ExamenId" value="Respuesta1" checked />
<label class="form-check-label" for="#reactivo.ExamenId">#reactivo.Respuesta1</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" id="#reactivo.ExamenId" name="#reactivo.ExamenId" value="Respuesta2" />
<label class="form-check-label" for="#reactivo.ExamenId">#reactivo.Respuesta2</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" id="#reactivo.ExamenId" name="#reactivo.ExamenId" value="Respuesta3" />
<label class="form-check-label" for="#reactivo.ExamenId">#reactivo.Respuesta3</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" id="#reactivo.ExamenId" name="#reactivo.ExamenId" value="Respuesta4" />
<label class="form-check-label" for="#reactivo.ExamenId">#reactivo.Respuesta4</label>
</div>
</div>
contador++;
}
<button type="submit" class="btn btn-primary">Send Response</button>
</form>
It is supposed to return an answer like "Respuesta1" and it should be stored in the Model.RespuestaAlumno and I just don't have any idea on how can I do it. please help.
In your Model Design, You need a property to receive the value of the selected answer(maybe you already have that property in your model,I don't understand spanish), So I create that property:
public class Evaluacion
{
public int ExamenId { get; set; }
public int RegistroId { get; set; }
public int ReactivoId { get; set; }
public string RespuestaAlumno { get; set; }
public string Pregunta { get; set; }
public string Respuesta1 { get; set; }
public string Respuesta2 { get; set; }
public string Respuesta3 { get; set; }
public string Respuesta4 { get; set; }
public string Answer { get; set; }
}
Then in your View, name uses property names for binding and it asked to start at index 0. I write a simple demo here, you can refer to it:
#model IEnumerable<Evaluacion>
#{
ViewData["Title"] = "Start Assesment";
var contador = 1;
}
<h1>Start Assesment</h1>
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<form asp-action="Evaluar" method="post">
#foreach (var reactivo in Model)
{
int i = contador - 1;
<div class="mb-3">
<label class="form-label">#contador . #reactivo.Pregunta</label>
<div class="form-check ">
<input class="form-check-input" type="radio" name="[#i].Answer" value="#reactivo.Respuesta1" checked />
<label class="form-check-label" for="#reactivo.ExamenId">#reactivo.Respuesta1</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="[#i].Answer" value="#reactivo.Respuesta2" />
<label class="form-check-label" for="#reactivo.ExamenId">#reactivo.Respuesta2</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="[#i].Answer" value="#reactivo.Respuesta3" />
<label class="form-check-label" for="#reactivo.ExamenId">#reactivo.Respuesta3</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="[#i].Answer" value="#reactivo.Respuesta4" />
<label class="form-check-label" for="#reactivo.ExamenId">#reactivo.Respuesta4</label>
</div>
</div>
contador++;
}
<button type="submit" class="btn btn-primary">Send Response</button>
</form>
Finally, The type of the model passed into the controller from the page is IEnumerable, You can't just use Evaluacion to receive, You need to change like this:
[HttpPost]
public async Task<IActionResult> Evaluar(List<Evaluacion> evaluacion)
{
//.......
return View(evaluacion);
}
Demo:
You can see the project can receive the answer successfully.

Pass Json data object from view containing partial view in MVC4

I am creating an MVC4 Form for person.
I have these Partial Views containing Information about Contact Info and Postal Address.
ContactInfo.cshtml
#model Demo.Models.ContactInfo
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<link rel="stylesheet" href="~/Content/plugins/validationengine/css/validationEngine.jquery.css" />
<h2>ContactInfo</h2>
<div class="form-group">
<label class="control-label col-lg-4">PhoneNumber</label>
<div class="col-lg-4">
#Html.EditorFor(model => model.PhoneNumber)
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-4">CellNumber</label>
<div class="col-lg-4">
#Html.EditorFor(model => model.CellNumber)
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-4">Email</label>
<div class="col-lg-4">
#Html.EditorFor(model => model.Email)
</div>
</div>
PostalAddress.cshtml
#model Demo.Models.PostalAddress
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<link rel="stylesheet" href="~/Content/plugins/validationengine/css/validationEngine.jquery.css" />
<h2>PostalAddress</h2>
<div class="form-group">
<label class="control-label col-lg-4">CountryID</label>
<div class="col-lg-4">
#Html.EditorFor(model => model.CountryID)
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-4">CityID</label>
<div class="col-lg-4">
#Html.EditorFor(model => model.CityID)
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-4">ProvinceID</label>
<div class="col-lg-4">
#Html.EditorFor(model => model.ProvinceID)
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-4">ZipCode</label>
<div class="col-lg-4">
#Html.EditorFor(model => model.ZipCode)
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-4">StreetAddress</label>
<div class="col-lg-4">
#Html.EditorFor(model => model.StreetAddress)
</div>
</div>
Here are the Model classes for Person,Postal Address and Contact Info.
ContactInfo.cs
using System;
using System.Collections.Generic;
namespace Demo.Models
{
public partial class ContactInfo
{
public ContactInfo()
{
this.Branches = new List<Branch>();
this.People = new List<Person>();
}
public long ID { get; set; }
public Nullable<int> PhoneNumber { get; set; }
public Nullable<int> CellNumber { get; set; }
public string Email { get; set; }
public virtual ICollection<Branch> Branches { get; set; }
public virtual ICollection<Person> People { get; set; }
}
}
Here is the Person.cs
using System;
using System.Collections.Generic;
namespace Demo.Models
{
public partial class Person
{
public Person()
{
this.Branches = new List<Branch>();
this.Documents = new List<Document>();
this.Faculties = new List<Faculty>();
this.Institutes = new List<Institute>();
this.Parents = new List<Parent>();
this.Students = new List<Student>();
this.StudentParents = new List<StudentParent>();
this.StudentParents1 = new List<StudentParent>();
}
public long ID { get; set; }
public string Name { get; set; }
public Nullable<bool> Gender { get; set; }
public Nullable<System.DateTime> DOB { get; set; }
public string Photo { get; set; }
public Nullable<long> ContactID { get; set; }
public Nullable<byte> PersonTypeID { get; set; }
public Nullable<long> PostalAddressID { get; set; }
public Nullable<byte> ReligionID { get; set; }
public virtual ICollection<Branch> Branches { get; set; }
public virtual ContactInfo ContactInfo { get; set; }
public virtual ICollection<Document> Documents { get; set; }
public virtual ICollection<Faculty> Faculties { get; set; }
public virtual ICollection<Institute> Institutes { get; set; }
public virtual ICollection<Parent> Parents { get; set; }
public virtual PersonType PersonType { get; set; }
public virtual PostalAddress PostalAddress { get; set; }
public virtual Religion Religion { get; set; }
public virtual ICollection<Student> Students { get; set; }
public virtual ICollection<StudentParent> StudentParents { get; set; }
public virtual ICollection<StudentParent> StudentParents1 { get; set; }
}
}
PostalAddress.cs
using System;
using System.Collections.Generic;
namespace Demo.Models
{
public partial class PostalAddress
{
public PostalAddress()
{
this.Branches = new List<Branch>();
this.Papers = new List<Paper>();
this.People = new List<Person>();
}
public long ID { get; set; }
public Nullable<byte> CountryID { get; set; }
public Nullable<int> CityID { get; set; }
public Nullable<int> ProvinceID { get; set; }
public string ZipCode { get; set; }
public string StreetAddress { get; set; }
public virtual ICollection<Branch> Branches { get; set; }
public virtual city city { get; set; }
public virtual country country { get; set; }
public virtual ICollection<Paper> Papers { get; set; }
public virtual ICollection<Person> People { get; set; }
public virtual province province { get; set; }
}
}
Here is the Main Person.cshtml
I am rendering the upper partial views in it and sending the json object data back.
#model Demo.Models.Person
#{
ViewBag.Title = "Person";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<legend>Person</legend>
<div class="form-group">
<label class="control-label col-lg-4">Name</label>
<div class="col-lg-4">
#Html.EditorFor(model => model.Name)
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-4">Gender</label>
<div class="col-lg-4">
#Html.EditorFor(model => model.Gender)
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-4">DOB</label>
<div class="col-lg-4">
#Html.EditorFor(model => model.DOB)
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-4">Photo</label>
<div class="col-lg-4">
#Html.EditorFor(model => model.Photo)
</div>
</div>
#Html.Partial("ContactInfo", new Demo.Models.ContactInfo())
<div class="form-group">
<label class="control-label col-lg-4">PersonTypeID</label>
<div class="col-lg-4">
#Html.DropDownList("PersonTypeID", String.Empty)
</div>
</div>
#Html.Partial("PostalAddress", new Demo.Models.PostalAddress())
<div class="form-group">
<label class="control-label col-lg-4">ReligionID</label>
<div class="col-lg-4">
#Html.DropDownList("ReligionID", String.Empty)
</div>
</div>
<p>
<input id="submit" class="submit" type="submit" value="Create" />
</p>
}
<script>
$('#submit').click(function (e) {
e.preventDefault();
//alert('hello');
var person = {
Name: $('#Name').val(),
Gender: $('#Gender').val(),
DOB: $('#DOB').val(),
Photo: $('#Photo').val(),
PersonTypeID: $('PersonTypeID').val(),
ContactInfo: {
PhoneNumber: $('#PhoneNumber').val(),
CellNumber: $('#CellNumber').val(),
Email: $('#Email').val()
},
PostalAddress: {
CountryID: $('#CountryID').val(),
CityID: $('#CityID').val(),
ProvinceID: $('#ProvinceID').val(),
ZipCode: $('#ZipCode').val(),
StreetAddress: $('#StreetAddress').val(),
},
};
var data = { jsonperson: person };
//console.log(data);
$.ajax({
url: '/Person/Create',
type: "post",
data: JSON.stringify(data),
success: function () {
$('#message').html('person added').fadeIn();
},
error: function () {
$('#message').html('person added').fadeIn();
}
});
But when i send the data from form to controller The data containing postal address and Contact info is Null.
Here is the Person Controller
[HttpPost]
public JsonResult Create(Person jsonperson)
{
if (ModelState.IsValid)
{
db.People.Add(jsonperson);
db.SaveChanges();
// return RedirectToAction("Index");
}
ViewBag.ContactID = new SelectList(db.ContactInfoes, "ID", "Email", jsonperson.ContactID);
ViewBag.PersonTypeID = new SelectList(db.PersonTypes, "ID", "Name", jsonperson.PersonTypeID);
ViewBag.PostalAddressID = new SelectList(db.PostalAddresses, "ID", "ZipCode", jsonperson.PostalAddressID);
ViewBag.ReligionID = new SelectList(db.Religions, "ID", "Name", jsonperson.ReligionID);
// return View(jsonperson);
JsonResult jr = new JsonResult();
jr.Data = true;
return jr;
}
Data is coming to the controller but Contact Info details and Postal Address is null. It is driving me crazy .
Thanks in advance.
the serialized JSON data that is being passed to your controller after you submit the form, does not have any property such as ContactId or PersonTypeID etc
your controller will receive PhoneNumber, CellNumber, Email, CountryID, CityID, ProvinceID, ZipCode, StreetAddress
therefore your model Person should have the above properties as it is named in your partial view models so they can be read accordingly into your Person model.

Postback occurs when client side validation is enabled MVC3

I have client side validation enabled and indeed it is working in that I see error message for required fields when I submit the form. But a postback is also occuring despite the fact that client side validation has fired. It is my understanding that client side validation is supposed to suppress the postback. Can someone confirm for me that is the expected behaviour and see if there is anything amiss with this view. Many thanks.
This is the view in question
#model Intranet.ViewModels.Student.CreateStudentViewModel
#{
ViewBag.Title = "Create Student";
}
<h2>
Create Student</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<table class="detailViewTable">
<tr>
<td>#Html.LabelFor(c=>c.Contact.Title)</td>
<td>#Html.EditorFor(c=>c.Contact.Title)</td>
<td>#Html.ValidationMessageFor(c=>c.Contact.Title)</td>
</tr>
<tr>
<td>#Html.LabelFor(c=>c.Contact.FirstName)</td>
<td>#Html.EditorFor(c=>c.Contact.FirstName)</td>
<td>#Html.ValidationMessageFor(c=>c.Contact.FirstName)</td>
</tr>
<tr>
<td>#Html.LabelFor(c=>c.Contact.LastName)</td>
<td>#Html.EditorFor(c=>c.Contact.LastName)</td>
<td>#Html.ValidationMessageFor(c=>c.Contact.LastName)</td>
</tr>
<tr>
<td>#Html.LabelFor(c=>c.Contact.Phone)</td>
<td>#Html.EditorFor(c=>c.Contact.Phone)</td>
<td>#Html.ValidationMessageFor(c=>c.Contact.Phone)</td>
</tr>
<tr>
<td>#Html.LabelFor(c=>c.Contact.AltPhone)</td>
<td>#Html.EditorFor(c=>c.Contact.AltPhone)</td>
<td>#Html.ValidationMessageFor(c=>c.Contact.AltPhone)</td>
</tr>
<tr>
<td>#Html.LabelFor(c=>c.Contact.Email)</td>
<td>#Html.EditorFor(c=>c.Contact.Email)</td>
<td>#Html.ValidationMessageFor(c=>c.Contact.Email)</td>
</tr>
<tr>
<td>Guardian 1</td>
<td>#Html.DropDownListFor(c=>c.Guardian1ID, new SelectList(Model.Contacts, "ID", "FullName"))</td>
</tr>
<tr>
<td>Guardian 2</td>
<td>#Html.DropDownListFor(c=>c.Guardian2ID, new SelectList(Model.Contacts, "ID", "FullName"))</td>
</tr>
</table>
<p>
<input type="submit" value="Create" />
</p>
}
<div>
#Html.ActionLink("Back to List", "List")
</div>
And here is the CreateStudentViewModel
public class CreateStudentViewModel
{
public int ID { get; set; }
public ContactViewModel Contact { get; set; }
public int Guardian1ID { get; set; }
public int Guardian2ID { get; set; }
public List<ContactViewModel> Contacts { get; set; }
}
And the ContactViewModel that contains the validation properties.
public class ContactViewModel
{
public int ID { get; set; }
[Required(ErrorMessage = "Title is required")]
public string Title { get; set; }
[Required(ErrorMessage = "First Name is required")]
[DisplayName("First Name")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Last Name is required")]
[DisplayName("Last Name")]
public string LastName { get; set; }
public string Phone { get; set; }
[DisplayName("Alternate Phone")]
public string AltPhone { get; set; }
public string Email { get; set; }
public string FullName
{
get { return string.Format("{0} {1} {2}", Title, FirstName, LastName); }
}

mvc3 submit model empty

I have a problem which I don't understand and there doesn't seem to be an easy way to debug the problem. I'm sure it's simple.
#model StartStop.ServiceResources.UserSettings
my MVC3 view is bound a specific model;
public class Setting
{
public Int64 SettingID { get; set; }
public Int64 UserID { get; set; }
public int PreferenceType { get; set; }
public string PreferenceName { get; set; }
public bool PreferenceBool { get; set; }
public int PreferenceInt { get; set; }
public string PreferenceString { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime ModifiedOn { get; set; }
}
public class UserSettings
{
public Int64 UserID { get; set; }
public List<Setting> Settings { get; set; }
}
the view lists out the check boxes which represent the list;
#using (Html.BeginForm("ManageAccount","Account", FormMethod.Post))
{
<table class="tbl" cellspacing="0">
<tr>
<th>Preference</th>
<th>Setting</th>
</tr>
#if (Model != null)
{
foreach (var item in Model.Settings.ToList())
{
<tr>
<td>#item.PreferenceName
</td>
<td>
#if (item.PreferenceType == 2)
{
#Html.CheckBoxFor(modelItem => item.PreferenceBool)
}
</td>
</tr>
}
}
</table>
<input type="submit" value="Save Changes" class="action medium" />
}
All good, I load the data into the view it renders the view and picks up the correct settings. However, when I do a post at the bottom, the view model returns a null! I'm not sure why...
[HttpPost]
[Authorize]
public ActionResult ManageAccount(StartStop.ServiceResources.UserSettings model)
{
if (ModelState.IsValid)
{
foreach (StartStop.ServiceResources.Setting oSetting in model.Settings)
{
StartStop.Helpers.UserPreferences.SaveUserSetting(oSetting);
}
}
return View(model);
}
Can anyone help?
The problem is on the following line in your view:
#Html.CheckBoxFor(modelItem => item.PreferenceBool)
I see people writing the following lambda expression modelItem => item.SomeProperty in their views very often and asking why the model binder doesn't correctly bind collection properties on their view models.
This won't generate proper name for the checkbox so that the default model binder is able to recreate the Settings collection. I would recommend you reading the following blog post to better understand the correct format that the model binder expects.
Try like this:
#model StartStop.ServiceResources.UserSettings
#using (Html.BeginForm("ManageAccount", "Account", FormMethod.Post))
{
<table class="tbl" cellspacing="0">
<tr>
<th>Preference</th>
<th>Setting</th>
</tr>
#if (Model != null)
{
for (var i = 0; i < Model.Settings.Count; i++)
{
<tr>
<td>#Model.Settings[i].PreferenceName</td>
<td>
#if (Model.Settings[i].PreferenceType == 2)
{
#Html.CheckBoxFor(x => x.Settings[i].PreferenceBool)
}
</td>
</tr>
}
}
</table>
<input type="submit" value="Save Changes" class="action medium" />
}
This being said, I would recommend you using editor templates, like so:
#using (Html.BeginForm("ManageAccount","Account", FormMethod.Post))
{
<table class="tbl" cellspacing="0">
<tr>
<th>Preference</th>
<th>Setting</th>
</tr>
#if (Model != null)
{
#Html.EditorFor(x => x.Settings)
}
</table>
<input type="submit" value="Save Changes" class="action medium" />
}
and then define a custom editor template which will automatcially be rendered for each element of the Settings collection (~/Views/Shared/EditorTemplates/Setting.cshtml):
#model StartStop.ServiceResources.Setting
<tr>
<td>#Model.PreferenceName</td>
<td>
#if (Model.PreferenceType == 2)
{
#Html.CheckBoxFor(x => x.PreferenceBool)
}
</td>
</tr>
Also the only input field that I can see in this form is the checkbox which is bound to the PreferenceBool property on your model. So inside your POST controller action you will get the Settings list property initialized but don't expect to find any values for the other properties in this Setting class unless of course you include input fields for them in the form (and more precisely in the editor template that I have shown).

Resources