Working with EditorTemplates and radio buttons - c#

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>

Related

ViewModel is null on second AJAX Post

I have PartialView which display pricing information and allow users to enter promocode. when clicking the button on _ListItem.cshtml on the first time, the HttpPost method manage to grab the OrderDetailsViewModel.Details and process in the controller. on the second click of the button, it couldn't capture the data in the Model.
Here are my codes.
NewPurchaseViewModel
public class NewPurchaseViewModel{
public NewPurchaseViewModel()
{
Details = new OrderDetailsViewModel();
Shipping = new Address();
Billing = new Address();
}
public string Email { get; set; }
public string Name { get; set; }
...
public OrderDetailsViewModel Details { get; set; }
}
OrderDetailsViewModel
public class OrderDetailsViewModel{
public OrderDetailsViewModel()
{
Items = new List<ItemsOrderViewModel>();
Shipping = 0.00M;
}
public string Promocode { get; set; }
public string SubTotal{ get; set; }
public string Total{ get; set; }
public string Discount{ get; set; }
public string Shipping{ get; set; }
public List<ItemsOrderViewModel> Items { get; set; }
}
Info.cshtml
#model ~.ViewModels.NewPurchaseViewModel
...
#Html.Partial("~/Views/CheckOut/_ListItem.cshtml", Model.Details, new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "Details"} })
_ListItem.cshtml
#model Nutric.hive.eStore.ViewModels.OrderDetailsViewModel
#using (Ajax.BeginForm("Verify", "PromoCode", null, new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "divPromoCode",
InsertionMode = InsertionMode.Replace
}))
{
<table>
...
<tbody>
#for (var i = 0; i < Model.Items.Count; i++)
{
<tr>
<td>#Model.Items[i].ProductPackage.Name</td>
<td class="qty">#Model.Items[i].Quantity</td>
<td class="price">#Model.Items[i].ItemTotal</td>
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.Id)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.Name)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.Description)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.ImageURL)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.Status)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.RetailPrice)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.TaxCode)
#Html.HiddenFor(Model => Model.Items[i].ProductPackage.PackageCode)
#Html.HiddenFor(Model => Model.Items[i].Quantity)
#Html.HiddenFor(Model => Model.Items[i].ItemTotal)
</tr>
}
...
<tr>
<td colspan="3">
<div class="col-lg-10">
<div class="input-group">
#Html.TextBoxFor(Model => Model.Promocode, new { #class = "form-control", maxlength = 15, placeholder = "Type Voucher Code Here...", type = "text" })
<span class="input-group-btn">
<input type="submit" class="btn btn-secondary" value="Apply !">
</span>
</div>
</div>
</td>
</tr>
</tbody>
</table>
#Html.HiddenFor(Model => Model.Discount)
#Html.HiddenFor(Model => Model.Shipping)
#Html.HiddenFor(Model => Model.Subtotal)
#Html.HiddenFor(Model => Model.Total)
}
#section Script{
#Scripts.Render("~/bundles/jqueryval")
#Scripts.Render("~/bundles/jquery")
}
PromoCodeController.cs
[HttpPost]
public ActionResult Verify(NewPurchaseViewModel postModel)
{
OrderDetailsViewModel odvm = postModel.Details;
//some logic here to check the promo code.
return PartialView("~/Views/CheckOut/_ListItem.cshtml",odvm );
}

List item inside model always null on post - Asp.net MVC

I am trying to post model which contains List<> item looks like below:
public class AddSubscriptionPlanModel
{
public AddSubscriptionPlanModel()
{
AllFeatures = new List<Feature>();
}
public int Id { get; set; }
public int SubscriptionPlanId { get; set; }
public int SubscriptionId { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
[Display(Name = "No Of Users")]
public int? NoOfUsers { get; set; }
[Display(Name = "Duration Type")]
public DurationType DurationType { get; set; }
public int Duration { get; set; }
public decimal Amount { get; set; }
public int SubscriptionPlanTrailId { get; set; }
public int FeatureId { get; set; }
public DateTime CreatedDateUtc { get; set; }
public DateTime LastUpdatedUtc { get; set; }
public int CreatedBy { get; set; }
public int LastUpdatedBy { get; set; }
public List<Feature> AllFeatures { get; set; }
}
I am populating all required fields on get method and feeding List from database
public ActionResult Mapping(int id)
{
var features = FeatureManager.GetAllFeatures();
var model = new Ejyle.DevAccelerate.Web.App.Models.SubscriptionPlan.AddSubscriptionPlanModel();
model.AllFeatures = features;
model.Id = id;
model.SubscriptionId = id;
model.NoOfUsers = 20;
model.SubscriptionPlanId = 1;
model.SubscriptionPlanTrailId = 1;
model.Amount = 1000;
model.CreatedBy = 1;
model.LastUpdatedBy = 1;
model.FeatureId = 1;
model.Duration = 20;
return View(model);
}
And my view code looks like below :
#using (Html.BeginForm("Mapping", "SubscriptionPlans", FormMethod.Post))
{
#Html.HiddenFor(model => model.Id)
#Html.HiddenFor(model => model.Amount)
#Html.HiddenFor(model => model.Duration)
#Html.HiddenFor(model => model.FeatureId)
#Html.HiddenFor(model => model.CreatedBy)
#Html.HiddenFor(model => model.LastUpdatedBy)
#Html.HiddenFor(model => model.NoOfUsers)
#Html.HiddenFor(model => model.SubscriptionId)
#Html.HiddenFor(model => model.SubscriptionPlanId)
#Html.HiddenFor(model => model.SubscriptionPlanTrailId)
#Html.HiddenFor(model => model.AllFeatures)
<table class="table table-striped">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.IsActive)
</th>
</tr>
</thead>
<tbody>
#for (int i=0; i < Model.AllFeatures.Count; i++)
{
<tr>
#Html.HiddenFor(model => model.AllFeatures[i].Id)
#Html.HiddenFor(model => model.AllFeatures[i].Name)
#Html.HiddenFor(model => model.AllFeatures[i].IsActive)
<td>
#Html.TextBoxFor(model => model.AllFeatures[i].Id)
</td>
<td>
#Html.TextBoxFor(model => model.AllFeatures[i].Name)
</td>
<td>
#Html.EditorFor(model => model.AllFeatures[i].IsActive)
</td>
</tr>
}
</tbody>
</table>
<input type="submit" class="btn btn-success" value="Submit" name="Submit"/>
}
On View, the list items rendered properly and even I am able to edit those fields.
But On post, all properties have the value except AllFeatures list item property which always shows count = 0.
Edit 1: This is how my view looks like while rendering features
Edit 2:
Signature of the method posting to:
[HttpPost]
public ActionResult Mapping(AddSubscriptionPlanModel model)
{
}
But On post, all properties have the value except AllFeatures list item property which always shows count = 0.
AllFeatures is a generic list of Feature. When you do this:
#Html.HiddenFor(model => model.AllFeatures)
The Razor view engine will render this:
<input id="AllFeatures"
name="AllFeatures"
type="hidden"
value="System.Collections.Generic.List`1[Namespace.Feature]">
<!--Where Namespace is the Namespace where Feature is defined. -->
In other words, HiddenFor calls the ToString() on the item and simply puts that into the input tag's value attribute.
What happens upon POST
When you post the form, the DefaultModelBinder is looking for a property named AllFeatures but of type string so it can assign this to it:
System.Collections.Generic.List`1[Namespace.Feature]
It does not find one since your model does not have AllFeatures of type string, so it simply ignores it and binds all the other properties.
AllFeatures list item property which always shows count = 0.
Yes, it will and this is not the AllFeatures list which you posted but the one from the constructor which clearly is an empty list with a count 0:
public AddSubscriptionPlanModel()
{
AllFeatures = new List<Feature>();
}
I am not sure why you are sending all the features to the client (browser) and then you need to post it back to the server.
Solution
To fix the issue, simply remove this line:
#Html.HiddenFor(model => model.AllFeatures)
Now it will not cause any confusion during binding and MVC will bind the items in the loop to the AllFeatures property.
In fact, the only code you really need, as far as I can tell from your question is this(I could be wrong if you need the hidden fields for some other reason. But if you just want the user to edit the AllFeatures, you do not need any of the hidden fields):
#for (int i = 0; i < Model.AllFeatures.Count; i++)
{
<tr>
<td>
#Html.TextBoxFor(model => model.AllFeatures[i].Id)
</td>
<td>
#Html.TextBoxFor(model => model.AllFeatures[i].Name)
</td>
<td>
#Html.EditorFor(model => model.AllFeatures[i].IsActive)
</td>
</tr>
}
I think your issue might be the nested property. You may want to review the posted form and see what it looks like.
Your markup looks correct assuming that the model binding would work for a nested property - which I am not sure it does. (Just to make sure I searched for and found this old Haack post that indicates you're doing it right: Haack Post)
As a workaround (and POC), you can try the following: Add a new parameter to your action for your features, post it like your doing now but not nested (so you can't use the Model For HTML helpers) and then in the controller, you can set it back to your main object's features property.
This how to do it, as in the context of binding to a list from a view is incorrect.
View:
#model Testy20161006.Controllers.AddSubscriptionPlanModel
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Mapping</title>
</head>
<body>
#*I am using controller home*#
#using (Html.BeginForm("Mapping", "Home", FormMethod.Post))
{
#Html.HiddenFor(model => model.Id)
#Html.HiddenFor(model => model.Amount)
#Html.HiddenFor(model => model.Duration)
#Html.HiddenFor(model => model.FeatureId)
#Html.HiddenFor(model => model.CreatedBy)
#Html.HiddenFor(model => model.LastUpdatedBy)
#Html.HiddenFor(model => model.NoOfUsers)
#Html.HiddenFor(model => model.SubscriptionId)
#Html.HiddenFor(model => model.SubscriptionPlanId)
#Html.HiddenFor(model => model.SubscriptionPlanTrailId)
#Html.HiddenFor(model => model.AllFeatures)
<table class="table table-striped">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.IsActive)
</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.AllFeatures.Count; i++)
{
<tr>
#*BE SURE to get rid of these*#
#*#Html.HiddenFor(model => model.AllFeatures[i].Id)
#Html.HiddenFor(model => model.AllFeatures[i].Name)
#Html.HiddenFor(model => model.AllFeatures[i].IsActive)*#
<td>
#Html.TextBoxFor(model => model.AllFeatures[i].Id)
</td>
<td>
#Html.TextBoxFor(model => model.AllFeatures[i].Name)
</td>
<td>
#Html.EditorFor(model => model.AllFeatures[i].IsActive)
</td>
</tr>
}
</tbody>
</table>
<input type="submit" class="btn btn-success" value="Submit" name="Submit" />
}
</body>
</html>
Controller:
[HttpPost]
public ActionResult Mapping(AddSubscriptionPlanModel addSubscriptionModel, FormCollection formCollection)
{
var AllFeatures = String.Empty;
var index = "AllFeatures[";
foreach (var item in formCollection)
{
if (item.ToString().Contains(index))
{
AllFeatures += " " + formCollection.GetValues(item.ToString())[0];
}
}
//put breakpoint here to see userValues

Multiple models in one view using IEnumerable

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.

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.

Updating 'Parent' entity instead of creating it

I'm listing a series of items and i want top implement an option where you click and it add a child object for that entity, let me explain:
public class SupportItem
{
[Display(Name = "Categoría")]
[ConcurrencyCheck, Required]
public string Type { get; set; }
[Key, HiddenInput(DisplayValue = false)]
public int SupportItemId { get; set; }
[Display(Name = "Nombre")]
[ConcurrencyCheck,Required]
public string Name { get; set; }
[ConcurrencyCheck]
[Display(Name = "Descripción Corta")]
[DataType(DataType.MultilineText)]
[Required]
public string Description { get; set; }
[HiddenInput(DisplayValue = false)]
public virtual SupportItem Father { get; set; }
[Display(Name = "Descripción detallada")]
[DataType(DataType.MultilineText)]
[Required]
public string LongDescription { get; set; }
[HiddenInput(DisplayValue = false)]
public bool Children { get; set; }
}
Now as u can see, this entity has a Father which is of type SupporItem. Now what I want to do is to list them all and add an option which will let you easily add a child for that item you select, heres the view definition:
#model IEnumerable<Domain.Entities.SupportItem>
#{
ViewBag.Title = "IndexSupportItems";
Layout = "~/Views/Shared/_AdminLayout.cshtml";
}
<h2>Index Support Items</h2>
<p>
#Html.ActionLink("Crear nuevo item principal", "Create")
</p>
<table class="Grid">
<tr>
<th>
Tipo
</th>
<th>
Nombre
</th>
<th>
Descripción
</th>
<th>
Acciones
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>#item.Type</td>
#if(item.Children)
{
<td>#Html.ActionLink(item.Name,"ListChildren", new{item.SupportItemId})</td>
}
else
{<td>#item.Name</td>
}
<td>#item.Description</td>
<td>
#Html.ActionLink("Delete","DeleteSupportItem", new{item.Father.SupportItemId})<br />
#Html.ActionLink("Add subitem sub-item","AddSubitem", new{item.SupportItemId})<br />
#Html.ActionLink("Edit","EditSupportItem", new{item.SupportItemId})
</td>
</tr>
}
</table>
Now as you can see, the action link for doing this points to a method called AddSubitem, which is implemented as follows:
public ViewResult AddSubitem(int supportItemId)
{
SupportItem child = new SupportItem() { Father = repo.GetSupportItemFromId(supportItemId) };
return View(child);
}
As you can see, I recieve a supportItemId which is the id from the parent entitity (the one to whom i want to add the new child), find it on my database context and create the new object and point the Father object i just found. After doing that the view it returns is this:
#model Domain.Entities.SupportItem
#{
ViewBag.Title = "AddSubitem";
}
<h2>AddSubitem</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Support Item</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Type)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Type)
#Html.ValidationMessageFor(model => model.Type)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Description)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Description)
#Html.ValidationMessageFor(model => model.Description)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.LongDescription)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.LongDescription)
#Html.ValidationMessageFor(model => model.LongDescription)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
In this view, the user will set some of the variables such as name and description and then submit the object so I can persist it to the database, the problem is the object I get from this view has its fathers id as its own id and the Father attribute is null thus i end up updating the Father object to which i want to add a child with this method:
public bool SaveSupportItem(SupportItem supportItem)
{
bool retorno = false;
if (supportItem.SupportItemId == 0)
{
context.SupportItems.Add(supportItem);
supportItem.Father.Children = true;
retorno = true;
}
else
{
SupportItem itemDB = context.SupportItems.Find(supportItem.SupportItemId);
if (itemDB != null)
{
itemDB.Name = supportItem.Name;
itemDB.Type = supportItem.Type;
itemDB.LongDescription = supportItem.LongDescription;
itemDB.Description = supportItem.Description;
retorno = true;
}
}
context.SaveChanges();
return retorno;
}
What am I doing wrong here? why can't I create a new object?
Thanks for taking the time and reading this, any help will be really appreciated!
Well, try this:
Add this to your SupportItem class
public class SupportItem
{
[Key]
[HiddenInput(DisplayValue = false)]
[ForeignKey("Father"), DatabaseGenerated(DatabaseGeneratedOption.None)]
public int SupportItemId { get; set; }
public virtual Father Father { get; set; }
...................
...................
}
Then change:
#Html.ActionLink("Add subitem sub-item","AddSubitem", new{item.SupportItemId})<br />
to
#Html.ActionLink("Add subitem sub-item","AddSubitem", "Controller Name here" new{SupportItemId = #Model.FatherId})<br />
Also because we need FatherId here new{SupportItemId = #Model.FatherId}, the ActionLink needs to be in lets say Father view e.g in Father Details where only single Father is current or something as you have to associate the supportItem with to specific father.
Your controller might look like this, assuming that you are using a ViewModel:
[HttpGet]
public ActionResult CreateSuppo(int supportItemId)
{
var model = new CreateSupportItemViewModel ();
model.SupportItemId= supportItemId;
return View(model);
}
[HttpPost]
public ActionResult Create(CreateSupportItemViewModel viewModel)
{
if(ModelState.IsValid)
{
var father= db.Fathers.Single(f => f.FatherId == viewModel.SupportItemId);
var supportItem= new SupportItem();
supportItem.Name = viewModel.Name;
....................
.................
father.SupportItems.Add(supportItem);
db.SaveChanges();
}
return View(viewModel);
}

Categories