Multiple models in one view using IEnumerable - c#

Problem Statement:
I want to create a view where first I'm accepting two parameters as a from date and to date and also a dropdown based on which one index in order to display all the details corresponding to the item selected in the combobox.
For this I've defined two models:
First model consists of the parameters which is to be inserted from the front end and again in the second model I'm defining all the parameters required to display in the index of that page.
Here is my model:
public class SearchModel
{
[Required]
[Display(Name = "From Date")]
public DateTime FromDate { get; set; }
[Required]
[Display(Name = "To Date")]
public DateTime ToDate { get; set; }
[Display(Name = "Search #")]
public String SearchNumber { get; set; }
}
public class IndexModel
{
[Required]
[Display(Name = "Search #")]
public string SearchNumber { get; set; }
[Key]
[Required]
[Display(Name = "SearchID")]
public int SearchID{ get; set; }
[Display(Name = "Received Date")]
public DateTime ReceivedDate { get; set; }
}
And finally I'm defining both of the models in a global Model as:
public class GlobalModel
{
public SearchModel SearchModel { get; set; }
public IndexModel IndexModel { get; set; }
}
Finally, when I try to write code in the view in index with GlobalModel as
#model IEnumerable<...SomePath.GlobalModel>
#{
ViewBag.Title = "Index";
}
<div style="padding-left: 40%">
#using (Html.BeginForm(new { ReturnUrl = ViewBag.ReturnUrl }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Search</legend>
<ol>
<li>
<div class="editor-label">
#Html.LabelFor(model => model.SearchModel.FromDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.SearchModel.FromDate)
#Html.ValidationMessageFor(model => model.SearchModel.FromDate)
</div>
</li>
<li>
<div class="editor-label">
#Html.LabelFor(model => model.SearchModel.ToDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.SearchModel.ToDate)
#Html.ValidationMessageFor(model => model.SearchModel.ToDate)
</div>
</li>
<li>
#Html.DropDownListFor(model => model.SearchModel.SearchNumber, new SelectList(Model.ddlSearchNo.ddlSearchNumber, "Value", "Text"), "Select Search #", new { id="SearchID",onchange="GetSearchNo()" })
#Html.ValidationMessageFor(model => model.FirstOrDefault().SearchModel.SearchNumber)
</li>
</ol>
</fieldset>
}
</div>
<div>
<table style="border-collapse: separate; border-spacing: 15px;">
<tr>
<th style="text-align:center;">
#Html.DisplayNameFor(model => model.IndexModel.SearchNumber)
</th>
<th style="text-align:center;">
#Html.DisplayNameFor(model => model.IndexModel.SearchID)
</th>
<th style="text-align:center;">
#Html.DisplayNameFor(model => model.IndexModel.ReceivedDate)
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td style="text-align:center;">
#Html.DisplayFor(modelItem => item.IndexModel.SearchNumber)
</td>
<td style="text-align:center;">
#Html.DisplayFor(modelItem => item.IndexModel.SearchID)
</td>
<td style="text-align:center;">
#Html.DisplayFor(modelItem => item.IndexModel.ReceivedDate)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.IndexModel.SearchID}) |
#Html.ActionLink("Details", "Details", new{id=item.Model.SearchID}) |
#Html.ActionLink("Delete", "Delete", new {id=item.IndexModel.SearchID })
</td>
</tr>
}
</table>
</div>
Here,though I am using the global model name at the top and with the object of the global model I'm trying to access the parameters of the child models but I'm getting error like not
declared or cannot recognise.
I am able to access all the parameters of the child model if I use FirstorDefault() which was suggested by someone as :
#Html.LabelFor(model => model.FirstorDefault().SearchModel.FromDate)
but at the time of loading it throws exception as values cannot be null.
Again if I dont use Ienumerable in top then I'm getting error in the foreach loop.
Also I found in stackoverflow that I need to use Ienumerable while defining the child model in the parent model as :
public IEnumerable<SearchModel> SearchModel { get; set; }
public IEnumerable<IndexModel> IndexModel { get; set; }
but again some other error are there.
I just want to load my view which I'm not able even after lot of try and research.
What changes should I make...?
Any help would be appreciated.
In controller index action method,I've written code as:
public ActionResult Index()
{
var result = GetDetails();
return View(result);
}
and my method is:
public IEnumerable<GlobalModel> GetDetails()
{
return (from po in dbEntity.SearchDetails.AsEnumerable()
select new GlobalModel()
{
IndexModel = po
//SearchID = po.SearchID,
// //ReceivedDate = po.ReceivedDate,
}).ToList();
}
Here,I am passing the Global model for the view to be loaded that consists of two child models.Now since the Index details is coming from database ,so here also I'm not able to initialise all the parameters,since I am trying to initialise the entire model to the fields of the table which is not at all possible .
Also, I'm getting all the fields of the database table at po. but again not able to initialise as IndexModel refers to entire parameters of the Index Model.
So,I got stuck here.I need to solve this issue.Where am I doing wrong..?

As I can see you only want to loop through the IndexModel and SearchModel only have a single value. Try doing this approach:
Change your Global Model to:
public class GlobalModel
{
public SearchModel SearchModel { get; set; }
public IEnumerable<IndexModel> IndexModels { get; set; }
}
For your method to get the IndexModels:
public IEnumerable<IndexModel> GetDetails()
{
return (from po in dbEntity.SearchDetails
select new IndexModel()
{
SearchNumber = po.SearchNumber,
SearchID = po.SearchID,
ReceivedDate = po.ReceivedDate
}).ToList();
}
In your Action Method:
public ActionResult Index()
{
var model = new GlobalModel():
var model.IndexModels = GetDetails();
return View(model);
}
Finally in your View:
#model ProjectName.Model.GlobalModel
#{
ViewBag.Title = "Index";
}
<div style="padding-left: 40%">
#using (Html.BeginForm(new { ReturnUrl = ViewBag.ReturnUrl }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Search</legend>
<ol>
<li>
<div class="editor-label">
#Html.LabelFor(model => model.SearchModel.FromDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.SearchModel.FromDate)
#Html.ValidationMessageFor(model => model.SearchModel.FromDate)
</div>
</li>
<li>
<div class="editor-label">
#Html.LabelFor(model => model.SearchModel.ToDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.SearchModel.ToDate)
#Html.ValidationMessageFor(model => model.SearchModel.ToDate)
</div>
</li>
<li>
#Html.DropDownListFor(model => model.SearchModel.SearchNumber, new SelectList(Model.ddlSearchNo.ddlSearchNumber, "Value", "Text"), "Select Search #", new { id="SearchID",onchange="GetSearchNo()" })
#Html.ValidationMessageFor(model => model.SearchModel.SearchNumber)
</li>
</ol>
</fieldset>
}
</div>
<div>
<table style="border-collapse: separate; border-spacing: 15px;">
<tr>
<th style="text-align:center;">
#Html.DisplayNameFor(model => model.IndexModels.SearchNumber)
</th>
<th style="text-align:center;">
#Html.DisplayNameFor(model => model.IndexModels.SearchID)
</th>
<th style="text-align:center;">
#Html.DisplayNameFor(model => model.IndexModels.ReceivedDate)
</th>
</tr>
#foreach (var item in Model.IndexModels) {
<tr>
<td style="text-align:center;">
#Html.DisplayFor(modelItem => item.SearchNumber)
</td>
<td style="text-align:center;">
#Html.DisplayFor(modelItem => item.SearchID)
</td>
<td style="text-align:center;">
#Html.DisplayFor(modelItem => item.ReceivedDate)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.SearchID}) |
#Html.ActionLink("Details", "Details", new{id=item.SearchID}) |
#Html.ActionLink("Delete", "Delete", new {id=item.SearchID })
</td>
</tr>
}
</table>
</div>
let me know if it works.

The problem is because you don't initialize your Search model and the Index model!
First, you should modify your GetDetails method as follows
public IEnumerable<GlobalModel> GetDetails()
{
var models=(from po in dbEntity.SearchDetails.AsEnumerable()
select new GlobalModel()
{
IndexModel = new IndexModel(){
/* FromDate= po. FromDate,
ToDate= po. ToDate,*/
},
SearchModel = new SearchModel(){
/*SearchID = po.SearchID,
ReceivedDate = po.ReceivedDate,*/
},
}).ToList();
}
In addition, you'd better create an empty constructor for the IndexModel class and the SearchModel class as well. This constructor will initialize the class fields/properties.

Related

How to send IEnumerable back from View to Controller

I'm pretty new to MVC and ASP.Net, my controller has 2 create methods for GET and POST. Through the Get method I'm generating a list and sending it into the view. In the view I'm making changes to a specific value in each var in the list and trying to send it back to the controller and the POST method.
When reaching the Post method, the value of the list is null.
The list I'm sending is a list of products and I'm using a view model to create a class with common values to pass through to the view.
To pass through the IEnumerable collection that was edited by the view I tried using BeginCollectionForm, setting the items through ViewBag, make changes to the model (#model IEnumerable<MarsBurgerV1.ViewModel.ItemVM>), but still each time the checkout button is being pressed the list in the Post method is NULL.
After a lot of tries and changes, currently my code looks as the following:
OrderController.cs (relevant parts)
public class OrderController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
public ActionResult Create()
{
var meals = (from m in db.meals
select new ItemVM
{
Id = m.Id,
Name = m.Name,
Price = m.Price,
ItemType = "Meal",
ImageURL = m.ImageUrl,
Quantity = 0
}).ToList();
var drinks = (from d in db.drinks
select new ItemVM
{
Id = d.Id,
Name = d.Name,
Price = d.Price,
ItemType = "Drink",
Quantity = 0
}).ToList();
//More Like That....
List<ItemVM> items = new List<ItemVM>();
items.AddRange(meals);
items.AddRange(drinks);//More Like...
return View(items.ToList());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(IEnumerable<ItemVM> items = null)
{
//Here items equals to null.
if (ModelState.IsValid)
{
//.....
}
return View();
}
Views/Order/Create.cshtml:
#model IEnumerable<***.ViewModel.ItemVM>
#{
ViewBag.Title = "Create";
var lst = Model.ToList();
ViewBag.List = Model.ToList();
}
<style>
tr td {
vertical-align: middle;
}
</style>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<h3> Order:</h3>
<table class="table table-condensed table-hover">
<tr calss="table-header">
<th>
#Html.DisplayName("Preview")
</th>
<th>
#Html.DisplayNameFor(m => m.Name)
</th>
<th>
#Html.DisplayNameFor(m => m.Price)
</th>
<th>
#Html.DisplayName("Quantity")
</th>
<th></th>
<th></th>
</tr>
#for (var i = 0; i < Model.Count(); i++)
{
<tr>
<td>
#if (Model.ElementAt(i).ImageURL != null)
{
<img src="#Url.Content(Model.ElementAt(i).ImageURL)" alt="IMAGES" height="100" width="100" />
}
</td>
<td>
#Html.DisplayFor(m => Model.ElementAt(i).Name)
</td>
<td>
#Html.DisplayFor(m => Model.ElementAt(i).Price)
</td>
<td>
<a type="button" class="btn btn-danger btn-xs" href="#">
<span class="glyphicon glyphicon-minus"></span>
</a>
#Html.EditorFor(l => lst[i].Quantity, new { htmlattribute = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => lst[i].Quantity, "", new { #class = "text-danger" })
<a type="button" class="btn btn-success btn-xs" id="plus" href="#">
<span class="glyphicon glyphicon-plus"></span>
</a>
</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Checkout" class="btn btn-primary"/>
</div>
</div>
}
ViewModel/ItemVM.cs:
namespace ***.ViewModel
{
public class ItemVM
{
[Required]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
[DataType(DataType.Currency)]
public double Price { get; set; }
[Required]
public string ItemType { get; set; }
public string ImageURL { get; set; }
[Required]
public int Quantity { get; set; }
}
}
In the end I want to use the "Create" (HttpPost method) to create a new order based on the values in the list received from the view.
What is the proper way of doing that and receiving the IEnumerable into the POST method?
Ok, I was finally able to make it work.
I've changed the #model to List Type,
Add the (actionName, Controller, FormMethod) to the HTML Helper Html.BeginForm,
Used Model[i] inside the loop to access variables and
marked all unchanged variables with
#Html.HiddenFor.
Create.cshtml:
#model List<MarsBurgerV1.ViewModel.ItemVM>
#{
ViewBag.Title = "Create";
}
#using (Html.BeginForm("Create","Order", FormMethod.Post))
{
#Html.AntiForgeryToken()
<h3> Order:</h3>
<table class="table table-condensed table-hover">
<tr calss="table-header">
<th>
#Html.DisplayName("Preview")
</th>
<th>
#Html.DisplayName("Name")
</th>
<th>
#Html.DisplayName("Price")
</th>
<th>
#Html.DisplayName("Quantity")
</th>
<th></th>
<th></th>
</tr>
#for (var i = 0; i < Model.Count(); i++)
{
<tr>
#Html.HiddenFor(m => Model[i].Id)
#Html.HiddenFor(m => Model[i].Type)
<td>
#Html.HiddenFor(m => Model[i].ImageURL)
#if (Model[i].ImageURL != null)
{
<img src="#Url.Content(Model[i].ImageURL)" alt="IMAGES" height="100" width="100" />
}
#Html.ValidationMessageFor(model => Model[i].ImageURL, "", new { #class = "label-control" })
</td>
<td>
#Html.HiddenFor(m => Model[i].Name)
#Html.DisplayFor(m => Model[i].Name)
#Html.ValidationMessageFor(model => Model[i].Name, "", new { #class = "label-control" })
</td>
<td>
#Html.HiddenFor(m => Model[i].Price)
#Html.DisplayFor(m => Model[i].Price, new { #class = "form-control" })
#Html.ValidationMessageFor(model => Model[i].Price, "", new { #class = "label-control" })
</td>
<td>
<a type="button" class="btn btn-danger btn-xs" href="#">
<span class="glyphicon glyphicon-minus"></span>
</a>
#Html.EditorFor(model => Model[i].Quantity, new { htmlattribute = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => Model[i].Quantity, "", new { #class = "text-danger" })
<a type="button" class="btn btn-success btn-xs" id="plus" href="#">
<span class="glyphicon glyphicon-plus"></span>
</a>
</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" value="Checkout" class="btn btn-primary"/>
</div>
</div>
}
Thanks Everyone for the help.

Working with EditorTemplates and radio buttons

I am showing data in tabular format. the table is generated automatically when working with EditorFor and EditorTemplates.
in each row of table i am showing ID, Name, Country dropdown, checkboxes for hobbies selection and radio button for sex selection.
all are working fine but i am not being able to bind radio buttons for sex.
i am not being able to understand what i am missing for which i am getting error.
please have a look at my code and give me direction what to change for radio buttons.
my full code
controller code
public class HomeController : Controller
{
public ActionResult Index()
{
StudentListViewModel osvm = new StudentListViewModel();
osvm.Sex = osvm.GetSex();
osvm.Countries = osvm.GetCountries();
return View(osvm);
}
[HttpPost]
public ActionResult Index(StudentListViewModel oStudentListViewModel)
{
return View(oStudentListViewModel);
}
}
viewmodel
public class StudentListViewModel
{
//public List<Country> Country { get; set; }
public List<SelectListItem> Countries { get; set; }
public IList<Student> Students { get; set; }
public List<Sex> Sex { get; set; }
public StudentListViewModel()
{
Students = new List<Student>
{
new Student
{
ID=1,Name="Keith",CountryID="0",SexID="F",
Hobbies= new List<Hobby>
{
new Hobby{ID=1,Name="Football",Checked=true},
new Hobby{ID=2,Name="Hocky",Checked=false},
new Hobby{ID=3,Name="Cricket",Checked=false}
}
},
new Student
{
ID=2,Name="Paul",CountryID="2",
Hobbies= new List<Hobby>
{
new Hobby{ID=1,Name="Football",Checked=false},
new Hobby{ID=2,Name="Hocky",Checked=true},
new Hobby{ID=3,Name="Cricket",Checked=false}
}
},
new Student
{
ID=3,Name="Sam",CountryID="3",
Hobbies= new List<Hobby>
{
new Hobby{ID=1,Name="Football",Checked=false},
new Hobby{ID=2,Name="Hocky",Checked=false},
new Hobby{ID=3,Name="Cricket",Checked=true}
}
}
};
}
public List<Sex> GetSex()
{
Sex = new List<Sex>
{
new Sex{ID="M",SexName="Male"},
new Sex{ID="F",SexName="Female"}
};
return Sex;
}
public List<SelectListItem> GetCountries()
{
Countries = new List<SelectListItem>
{
new SelectListItem{Value="1",Text="India"},
new SelectListItem{Value="2",Text="UK"},
new SelectListItem{Value="3",Text="USA"}
};
return Countries;
}
}
Model class
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public string CountryID { get; set; }
public string SexID { get; set; }
public IList<Hobby> Hobbies { get; set; }
}
public class Hobby
{
public int ID { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
}
public class Sex
{
public string ID { get; set; }
public string SexName { get; set; }
}
Main View Index.cshtml
#model EditorTemplateSample.Models.StudentListViewModel
#{
ViewBag.Title = "Home Page";
}
<br /><br />
#using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
<div class="form-group">
<div class="col-md-12 table-responsive">
<table class="table table-bordered table-hover">
<tr>
<th>
ID
</th>
<th>
Name
</th>
<th>
Country
</th>
<th>
Hobbies
</th>
<th>
Sex
</th>
</tr>
<tbody>
#Html.EditorFor(m => m.Students, new { Countries = Model.Countries, Sex = Model.Sex })
</tbody>
</table>
</div>
</div>
}
EditorTemplates\Student.cshtml
#model EditorTemplateSample.Models.Student
<tr>
<td>
#Html.HiddenFor(m => m.ID)
#Html.DisplayFor(m => m.ID)
</td>
<td>
#Html.TextBoxFor(m => m.Name)
</td>
<td>
#Html.DropDownListFor(m => m.CountryID,
new SelectList((List<SelectListItem>)ViewData["Countries"], "Value", "Text", Model.CountryID), "-- Select Country--")
<td>
<td>
#Html.EditorFor(m => m.Hobbies)
<td>
<td>
#Html.EditorFor(m => ((EditorTemplateSample.Models.Sex) ViewData["Sex"]).ID)
<td>
</tr>
EditorTemplates\Hobby.cshtml
#model EditorTemplateSample.Models.Hobby
<div class="checkbox">
#Html.HiddenFor(m => m.ID)
#Html.HiddenFor(m => m.Name)
#Html.CheckBoxFor(m => m.Checked)
#Html.LabelFor(m => m.Checked, Model.Name)
</div>
EditorTemplates\Sex.cshtml
#model EditorTemplateSample.Models.Sex
<td>
<div class="checkbox">
#Html.HiddenFor(m => ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).ID)
#Html.HiddenFor(m => ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).SexName)
#Html.RadioButtonFor(m => ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).ID, ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).ID)
#Html.LabelFor(m => ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).ID, ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).SexName)
</div>
</td>
#Html.EditorFor(m => m.Students, new { Countries = Model.Countries, Sex = Model.Sex }) the above way i pass Sex model data to Student.cshtml file
from Student.cshtml file i try to bind ID #Html.EditorFor(m => ((EditorTemplateSample.Models.Sex) ViewData["Sex"]).ID)
in EditorTemplates\sex.cshtml file
#model EditorTemplateSample.Models.Sex
<td>
<div class="checkbox">
#Html.HiddenFor(m => ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).ID)
#Html.HiddenFor(m => ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).SexName)
#Html.RadioButtonFor(m => ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).ID, ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).ID)
#Html.LabelFor(m => ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).ID, ((EditorTemplateSample.Models.Sex)ViewData["Sex"]).SexName)
</div>
</td>
guide me how could i pass my sex data from main index view to sex view in EditorTemplates folder.
Edit
in main view i add this line
#Html.EditorFor(m => m.Students, new { Countries = Model.Countries, MainModel = Model, Sex = Model.Sex })
in student.cshtml i edit line like #Html.EditorFor(m => ((EditorTemplateSample.Models.StudentListViewModel)ViewData["MainModel"]).Sex, new { Sex = (List<EditorTemplateSample.Models.Sex>)ViewData["Sex"] })
in sex.cshtml for radio button generation i changed line likes
<div class="checkbox">
#Html.HiddenFor(m => m.ID)
#Html.HiddenFor(m => m.SexName)
#Html.RadioButtonFor(m => m.ID,Model.ID)
#Html.LabelFor(m => m.ID, Model.SexName)
</div>
but still no luck. badly stuck due to lack of control over asp.net mvc EditorTemplates now radio buttons are coming but all are selected by default which is wrong. see the latest UI.
please help me to get out of this problem. thanks
Your Student class contains a property string SexID which is what you are wanting to bind the selected radio button value to. But your EditorTemplate is for a model that is typeof Sex, and you Student model does not contain a property which is typeof Sex (and nor should it).
Using an EditorTemplate in this case makes no sense - your binding to a simple property, not a complex object or collection of objects. The radio buttons should be generated in your Student.cshtml template.
#model EditorTemplateSample.Models.Student
<tr>
<td>
#Html.HiddenFor(m => m.ID)
#Html.DisplayFor(m => m.ID)
</td>
<td>#Html.TextBoxFor(m => m.Name)</td>
<td>#Html.DropDownListFor(m => m.CountryID, new SelectList((List<SelectListItem>)ViewData["Countries"], "Value", "Text", Model.CountryID), "-- Select Country--")</td>
<td>#Html.EditorFor(m => m.Hobbies)</td>
<td>
#foreach(var sex in (List<Sex>)ViewData["Sex"])
{
<label>
#Html.RadioButtonFor(m => m.SexID, sex.ID, new { id = "" })
<span>#sex.SexName</span>
</label>
}
</td>
</tr>

Unable to pass value to controller from view through viewmodel

I'm pretty sure my brain is friend and this is something I'm going to laugh at tomorrow morning, but unfortunately I'm stuck on this portion and am asking for assistance.
I have a ViewModel:
public class HousingDetailsViewModel : AppViewModel
{
DataContext db = new DataContext();
public List<string> units { get; set; }
public List<AvailableHousing> availableHousings { get; set; }
public Person person { get; set; }
public OccupiedHousing currentHousing { get; set; }
public OccupiedHousing newHousing;
public HousingDetailsViewModel(int? id)
{
units = db.AvailableHousings.OrderBy(ah => ah.Unit).Select(h => h.Unit).Distinct().ToList();
availableHousings = db.AvailableHousings.Where(h => h.Available == true).OrderBy(h => h.Bed)
.OrderBy(h => h.Room).ToList();
currentHousing = db.OccupiedHousing.Include(o => o.AvailableHousing)
.Include(o => o.Person).Where(o => o.PersonID == id && o.CurrentHousing == true).FirstOrDefault();
person = db.Persons.Find(id);
newHousing = new OccupiedHousing();
}
}
My controller methods for this view:
public ActionResult Details(int? id)
{
return View(new HousingDetailsViewModel(id));
}
[HttpPost]
public ActionResult Move(OccupiedHousing newHousing, int? personID)
{
newHousing.PersonID = personID;
newHousing.DateArrived = DateTime.Now;
newHousing.CurrentHousing = true;
newHousing.AvailableHousingID = housingID;
db.OccupiedHousings.Add(newHousing);
db.SaveChanges();
return RedirectToAction("Index", new HousingViewModel());
}
And my form works fine for all of my fields except for 1, and that's the AvailableHousingID. I've tried setting a hidden value. I put a breakpoint where I set the value of the hidden field and I watched it change, but it didn't make it to the controller. So I changed it to a form submission and tried to catch it as a routevalue and that didn't work either. I'm at a loss, can anyone see where I'm going wrong?
EDIT: Adding View
#model AppName.ViewModels.HousingDetailsViewModel
#{
ViewBag.Title = "Housing Details";
}
#Html.BeginForm("Move", "Housing", new { personID = #Model.person.ID }, FormMethod.Post, new { })
<script>
function setID(id) {
$('#HiddenHousingID').val(id);
$('#HiddenSubmit').click();
}
</script>
<h2>Housing Details</h2>
<div class="row">
<div class="col-xs-12 container">
<div class="col-xs-5">
<img src="//placehold.it/150x200/77CCDD/66BBCC" class="img-responsive" />
</div>
<div class="col-xs-7">
<h4>#Model.person.ComboName</h4>
<h4>#Model.currentHousing.AvailableHousing.Unit - #Model.currentHousing.AvailableHousing.Room - #Model.currentHousing.AvailableHousing.Bed</h4>
<h4>#Model.person.DateOfBirth.Value.ToShortDateString()</h4>
#Html.HiddenFor(m => m.newHousing.AvailableHousingID, new { id = "HiddenHousingID", name = "newHousing.AvailableHousingID")}
</div>
</div>
</div>
<div class="row">
#foreach (var unit in Model.units)
{
<div class="col-xs-6">
<div class="panel panel-primary">
<div class="panel-heading">
<span class="panel-title">
#unit
</span>
</div>
<div class="panel-body">
<table id="MoveHousingTable" class="table table table-condensed table-striped">
<thead>
<tr>
<th>
Available Housing
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var housing in Model.availableHousings.Where(h => h.Unit == unit))
{
<tr>
<td>
#housing.Room -
#housing.Bed
</td>
<td>
<input type="button" value="Select" name="select" onclick="setID(#housing.ID)" />
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
}
</div>
<input type="submit" class="hidden" id="HiddenSubmit">
}
For the route :
#Html.BeginForm("Move", "Housing", new { personID = #Model.person.ID , housingID= #Model.newHousing.AvailableHousingID}, FormMethod.Post, new { })

Create and Index in same view using partial view

Ok I give up after several hours of troubleshooting, but I am sure you all will see a solution right away. I have three models Person, Course and CoursePreference. The CoursePreference model has two foreign keys PersonId and CourseId.
What I want to do:
I want to create a view where the user can add course preferences in a top Create section, and when they click Add, the form would post and refresh the List in the same view. Essentially I am combining Index and Create in one view. So I created an Index View and a partial view called _CreatePartial in the CoursePreference folder.
The problem:
The view displays fine, but with two problems. 1) The CoursePreferenceId field shows a dropdownlist. I want it to be a hidden field since it's an identity. I copied the code exactly from the scaffolded Create View, which hides the Id correctly. Dont know why it's not working in the _CreatePartial view? 2) Most importantly, my _CreatePartial will not add any course preference. It looks as if the form is posting but no record is added.
What gives?
Here are the models, controllers and views:
---------------------------
Models (stripped down versions)
---------------------------
public class CoursePreference
{
public int CoursePreferenceId { get; set; }
public Nullable<int> CourseId { get; set; }
public Nullable<int> PersonId { get; set; }
public virtual Course Course { get; set; }
public virtual Person Person { get; set; }
}
public class Person
{
public int PersonId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public virtual ICollection<CoursePreference> CoursePreferences { get; set; }
}
public class Course
{
public int CourseId { get; set; }
public Nullable<int> ProgramId { get; set; }
public string Name { get; set; }
public virtual ICollection<CoursePreference> CoursePreferences { get; set; }
}
------------------
Controllers
------------------
public ActionResult _CreatePartial()
{
ViewBag.CourseId = new SelectList(db.Courses, "CourseId", "Name");
ViewBag.PersonId = new SelectList(db.People, "PersonId", "LastName");
return View("_CreatePartial");
}
public ActionResult Index()
{
ViewBag.CourseId = new SelectList(db.Courses, "CourseId", "Name");
ViewBag.PersonId = new SelectList(db.People, "PersonId", "LastName");
var coursepreferences = db.CoursePreferences.Include(c => c.Course).Include(c => c.Person);
return View(coursepreferences.ToList());
}
---------------------------
Index View
---------------------------
#model IEnumerable<MyProj.Models.CoursePreference>
#{ ViewBag.Title = "Index";
}
<h4>Add Course Preferences</h4>
<div>
#Html.Partial("~/Views/CoursePreference/_CreatePartial.cshtml", new MyProj.Models.CoursePreference())
</div>
<br />
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Course.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Person.LastName)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Course.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Person.LastName)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.CoursePreferenceId }) |
#Html.ActionLink("Details", "Details", new { id=item.CoursePreferenceId }) |
#Html.ActionLink("Delete", "Delete", new { id=item.CoursePreferenceId })
</td>
</tr>
}
</table>
---------------------------
_CreatePartial View
---------------------------
#model MyProj.Models.CoursePreference
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>CoursePreference</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.CoursePreferenceId, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CoursePreferenceId)
#Html.ValidationMessageFor(model => model.CoursePreferenceId)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CourseId, "CourseId", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("CourseId", String.Empty)
#Html.ValidationMessageFor(model => model.CourseId)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PersonId, "PersonId", new { #class = "control-label col-md-2" }
<div class="col-md-10">
#Html.DropDownList("PersonId", String.Empty)
#Html.ValidationMessageFor(model => model.PersonId)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
Nothing is saved because you don't have a controller method to handle the post. Implement this in your controller:
[HttpPost]
public ActionResult Index(CoursePreference pref)
{
// Insert code to fetch database context into variable "db"
db.CoursePreferences.Add(pref);
db.SaveChanges();
return RedirectToAction("Index");
}
As for the hidden ID-field, you should use:
#Html.HiddenFor(model => model.CoursePreferenceId)
-not EditorFor or LabelFor.

Asp.net Validation just working for one property

Following is my Model for Product
public class Product
{
public int Id { get; set; }
[Required(ErrorMessage = "Please Enter Product Name")]
[StringLength(100)]
public string Name { get; set; }
[Required(ErrorMessage = "Please Enter Short Desciption")]
[StringLength(200)]
. // other properties
. // Removed for brevity
}
And following is my View code
<div class="contentHolder">
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
Html.Telerik().TabStrip()
.Name("TabStrip")
.Items(tabstrip =>
{
tabstrip.Add()
.Text("General")
.ContentHtmlAttributes(new { style = "height:700px" })
.Content(#<text>
<table>
<tr>
<td class="editor-label">
#Html.LabelFor(model => model.Product.Name)
</td>
<td class="editor-field">
#Html.EditorFor(model => model.Product.Name)
#Html.ValidationMessageFor(model => model.Product.Name)
</td>
</tr>
<tr>
<td class="editor-label">
#Html.LabelFor(model => model.Product.ShortDescription)
</td>
<td class="editor-field">
#Html.TextAreaFor(model => model.Product.ShortDescription, new { cols = "50%", rows = "3" })
#Html.ValidationMessageFor(model => model.Product.ShortDescription)
</td>
</tr>
</table>
</text>);
})
.SelectedIndex(0)
.Render();
}
Other than the Name property, validation isn't working.
I found the answer to my problem. It's a bug or error in Asp.net MVC 3 as it is reported here : Unobtrusive Client Hooks Not Generated Via TextAreaFor For Nested Model Properties. This is the reason why the validations are aren't happening for ShortDescription in my case since i am using #Html.TextAreaFor.
Hope this is removed in MVC4

Categories