#Html.Editor Model binding Issue - asp.net-mvc

I am trying to generate Two sets of List of checkboxes on a view. It all working apart from Post action. On submit,
ParentViewModel is not binding the ChildViewModel properly
Model. FirstCheckboxList
Model. SecondCheckboxList
Above both are coming as null.
I am not sure what I am missing. Any help on this would be great.
Thanks in advance.
CheckboxItems.cshtml
#model List<CheckboxItem>
#{
for (int i = 0; i < Model.Count(); i++)
{
<div>
#Html.CheckBoxFor(x => x.ElementAt(i).Checked, new { #id = Model.ElementAt(i).Id, onclick = "GetValue()" })
<span id="Padded">#Model.ElementAt(i).Text</span>
</div>
}
}
MainView.cshtml
#Html.BeginForm(){
#Html.EditorFor(m=> m.FirstCheckboxList,"CheckboxItems")
#Html.EditorFor(m=> m.SecondCheckboxList, "CheckboxItems")
}
#Html.TextBoxFor(m => m.FSelected, new Dictionary<string,object>() {{"readonly",true}})
#Html.TextBoxFor(m => m.FUniverse,new Dictionary<string,object>() {{"readonly",true}})
<input type="submit" name="nextBtn" value ="Next" />
}
ParentViewModel
public class ParentViewModel
{
public int PId { get; set; }
public IEnumerable<CheckboxItem> FirstCheckboxList{ get; set; }
public IEnumerable<CheckboxItem> SecondCheckboxList{ get; set; }
public Int64 FSelected { get; set; }
public Int64 FUniverse { get; set; }
}
CheckboxItem : child view model
public class CheckboxItem
{
public int Id { get; set; }
public string Text { get; set; }
public bool Checked { get; set; }
}
controller action
[HttpPost]
public ActionResult MyCheckboxView(int planid, ParentViewModel model, string nextBtn)
{
// do something
return View(Model);
}

Try changing your viewmodel for the ParentViewModel to use a List<CheckboxItem> instead of an IEnumerable<CheckboxItem>:
public class ParentViewModel
{
public int PlanId { get; set; }
public List<CheckboxItem> FirstCheckboxList{ get; set; }
public List<CheckboxItem> SecondCheckboxList{ get; set; }
public Int64 FSelected { get; set; }
public Int64 FUniverse { get; set; }
}
The model binder needs a data structure like a List or an Array so that it can correctly bind elements at specified indexes. IEnumerable is just an interface and doesn't support indexes like this.
Edit
Also, as a side-note, you don't have to bother with the for loop in your EditorTemplate because MVC can do all this for you. Just change the model type to be #model CheckboxItem, remove the loop and get rid of the id attribute so it looks like this:
#model CheckboxItem
#{
<div>
#Html.CheckBoxFor(x => x.Checked, new { onclick = "GetSelectedFrame()" })
<span id="Padded">#Model.Text</span>
</div>
}
}
Also, make sure your EditorFor call doesn't supply the EditorTemplate's name, as this messes up the "MVC Magic" (see this question which explains that it automatically iterates the list without the template name and doesn't with the template name):
#Html.BeginForm(){
#Html.EditorFor(m=> m.FirstCheckboxList)
#Html.EditorFor(m=> m.SecondCheckboxList)
}

Related

Using Multiple model in a single view ASP.NET, MVC [duplicate]

This question already has answers here:
Multiple models in a view
(12 answers)
Closed 6 years ago.
I am devolaping a 2nd hand item selling like OLX, I have two models
1: Product_model
2: Customer_model
so i want to use the the product description and customer information in a single view.
Models:
[Table("Product")]
public class ProductModel
{
[Key]
public string ProductId { get; set; }
public string ProductName { get; set; }
public decimal ProductPrice { get; set; }
public string ProductDescription { get; set; }
public string ProductCategory { get; set; }
public string ProductAddress { get; set; }
public string ProductImage1 { get; set; }
public string ProductImage2 { get; set; }
public string ProductImage3 { get; set; }
public string CustomerId { get; set; }
// public DateTime ProductSubTime { get; set; }
}
[Table("Customer")]
public class CustomerModel
{
[Key]
[Required(ErrorMessage = "Please provide Customer ID",
public string CustomerFullName { get; set; }
public int CustomerContact { get; set; }
public string CustomerEmail { get; set; }
public string CustomerGender { get; set; }
public string CustomerAddress { get; set; }
}
My controller:
public ActionResult Index()
{
return View(cm.Products.ToList());
}
My view:
#model IEnumerable<WebApplication11.Models.ProductModel>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/MyTemplate.cshtml";
}
<div class="col-md-4"style="border: 2px solid yellow" >
<div class="single-product-widget" style="border: 2px solid black">
<h2 class="product-wid-title">Recently Viewed</h2>
View All
#{ int i = 0;}
#foreach (var item in Model)
{
<div class="single-wid-product" style="border: 2px dashed black">
<img src="~/images/#item.ProductImage1" alt="No Photo" class="product-thumb">
<h2>#Html.DisplayFor(model => item.ProductName)</h2>
</div>
}
<div>
</div>
For understanding purpose i deleted some part of view.
Please guide me accessing both models in particular view.
I tried also some methods but not able to get it.
Thanks in advance...
You can create a view model with 2 properties like following code
public class MyModel
{
public List<ProductModel> Products { get; set; }
public List<CustomerModel> Customers { get; set; }
}
In controller:
public ActionResult Index()
{
var model = new MyModel
{
Products = cm.Products.ToList(),
Customers = cm.Customers.ToList()
};
return View(model);
}
In Veiw
#foreach (var item in Model.Products)
{
#*Write your razor code here*#
}
There are several options to access several models in a particular view. I assume that you just want to show some customer information in page corner or something. Your layout view may have a requirement to include this customer info (but it does not matter anyway).
Most preferable option from my point of view is to use child action.
Create CustomerController
public class CustomerController : Controller {
[ChildActionOnly]
public ViewResult CustomerInfo() {
CustomerModel customer = ...
return PartialView("_CustomerPartial", customer);
}
}
.chstml view will have #Html.Action("CustomerInfo", "Customer") where you want customer info to be displayed.
This way you will have a separate CustomerModel creation logic.
Another option is to use ViewData or ViewBag to store secondary model information like customer info.
Controller code:
public ActionResult Index()
{
var products = cm.Products.ToList();
ViewBag.Customer = ... // get customer
return View(products);
}
View code:
#model IEnumerable<WebApplication11.Models.ProductModel>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/MyTemplate.cshtml";
var customer = (CustomerModel)ViewBag.Customer;
}
#* display customer info *#
#* either { *#
<div>Welcome #customer.CustomerFullName. You email is #customer.CustomerEmail</div>
#* } or { *#
#Html.Partial("_CustomerPartial", customer)
#* } *#
#foreach(var product in Model) {
#* display product info *#
}
Previous option can be changed in a way to use strongly typed model. It is also can be known as view model - usually a class that exists only to store necessary info for the view. In your case these are some customer and products information.
ViewModel:
public class ProductListViewModel {
public CustomerModel Customer { get; set; }
public IEnumerable<ProductModel> Products { get; set; }
}

Cannot use indexing in view because of IEnumerable in Model?

In my Model I have IEnumerable, and due to this I cannot use for loop in view. If I use foreach the Html generated doesn't have indexing, which is what I need. How do I solve this problem.
I'm trying to use same view model to create and edit and I'm having problem in the edit part.
public class CreateModule
{
//Empty form to handle form serialization
public CreateModule()
{
}
public long Id { get; set; }
[Required]
public string ModuleId { get; set; }
[DataType(DataType.DateTime)]
public DateTime DateEntered { get; set; }
public string KindName { get; set; }
public string TypeName { get; set; }
public string SelectedModuleTypeName { get; set; }
public IEnumerable<SelectListItem> TypeNames { get; set; }
public IEnumerable<Property> Properties { get; set; }
}
public class Property
{
public string Name { get; set; }
public string Value { get; set; }
}
Here is my view where I have used both for and foreach, but I have commented for as I cannot use indexing in it currently
#*
#for (int i = 0; i < Model.Properties.Count(); i++)
{
<label class="label">#Model.Properties[i].Value</label>
<div class="input-block-level">#Html.TextBoxFor(model => Model.Properties[i].Value, new { #value = Model.Properties[i].Value })</div>
}
*#
#foreach (var properties in Model.Properties)
{
<div class="label">#properties.Name</div>
<div class="input-block-level">#Html.TextBoxFor(model => properties.Value, new { #value = properties.Value })</div>
<br/>
}
Currently Html.Textbox for generates following two names which doesn't have indexing in it
properties.value
because of this the values that are submitted during the post are Null. If I use for loop then I think my properties.value will change to properties[0].value and properties[1].value??? I'm not sure, but that is what I'm trying to achieve.
If I change my model from
public IEnumerable<Property> Properties { get; set; }
to
public List<Property> Properties { get; set; }
then my following method in repository would not work as it reutrns IQueryable for value properties, and casting from IQueryable to List would not be possible.
public CreateModule GetModuleDetails(long id)
{
var module = (_dbSis.Modules.Where(t => t.Id == id).Select(m => new CreateModule
{
Id = id,
ModuleId = m.ModuleId,
TypeName = m.ModuleType.TypeName,
KindName = m.ModuleType.ModuleKind.KindName,
Properties = m.PropertyConfiguration.PropertyInstances.Select(
x => new Property { Name = x.Property.Name, Value = x.Value })
}));
return (module.FirstOrDefault());
}
Create a partial view like below, name it Property.cshtml, and put it under Views/Shared/EditorTemplates.
#model MyApp.Models.Property
<div class="label">#Model.Name</div>
<div class="input-block-level">#Html.TextBoxFor(model => Model.Value)</div>
<br/>
Then, replace the foreach loop in your View with this:
#EditorFor(model => model.Properties)

Model nested type is null when after modelbinding

I have a ViewModel as below:
public class CheckoutViewModel
{
public string ProductNumber { get; set; }
public string Name { get; set; }
public int Price { get; set; }
public Input UserInput;
public class Input
{
public string Email { get; set; }
public string Phone { get; set; }
}
}
And an action like this:
[HttpPost]
public ActionResult Index(CheckoutViewModel model)
{
// ...
return View();
}
And my model has bound as below:
#model GameUp.WebUI.ViewModels.CheckoutViewModel
#using (Html.BeginForm("Index", "Checkout", FormMethod.Post))
{
#Html.AntiForgeryToken()
<!-- some HTML -->
#Html.LabelFor(m => m.UserInput.Email)
#Html.TextBoxFor(m => m.UserInput.Email)
#Html.LabelFor(model => model.UserInput.Phone)
#Html.TextBoxFor(model => model.UserInput.Phone)
<button>Submit</button>
}
When I submit the form, the UserInput is null. I know ASP.NET MVC is able to bind nested types but in this code is not. Also I can get the Email and Phone values by:
var email = Request.Form["UserInput.Email"];
var phone = Request.Form["UserInput.Phone"];
Maybe I do something wrong! It's a simple model binding you can find everywhere in the web.
You forgot to put a setter in your UserInput, I don't think the setter is automatic. Anyway you can make it work by just putting a getter/setter in your UserInput and no need to do extra in your controller method:
public Input UserInput { get; set; }
Your complete model:
public class CheckoutViewModel
{
public string ProductNumber { get; set; }
public string Name { get; set; }
public int Price { get; set; }
public Input UserInput { get; set; }
public class Input
{
public string Email { get; set; }
public string Phone { get; set; }
}
}

asp.net mvc rendering partial view on-demand

I'm a newbie to asp.net mvc and I'd like to know if what I do is correct.
I want to create a view for searching people. Here's what I have so far:
The business model class:
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Address Address { get; set; }
public DateTime DOB { get; set; }
public List<Telephone> Telephones { get; set; }
}
The ViewModel class:
public class SearchPersonViewModel
{
public int Id { get; set; }
public string FullName { get; set; }
public string LicencePlate { get; set; }
public string CarMake { get; set; }
public string CarModel { get; set; }
}
The partial view :
#model IEnumerable<MvcApplication2.Models.SearchPersonViewModel>
#foreach (var item in Model)
{
#Html.DisplayFor(m => item.Id)
#Html.DisplayFor(m => item.FullName)
}
The view from which the partial view is called:
#Html.Action("Search", "Person");
*The controller method in the PersonController:
[ChildActionOnly]
public ActionResult Search()
{
List<SearchPersonViewModel> model = new List<SearchPersonViewModel>();
model.Add(new SearchPersonViewModel() { FullName = "test", Id = 3 });
return PartialView("_SearchPerson", model);
}
Now the problem is that the Search method is called whenever the main View is loaded. What I want is to add a search textbox on the mainview for filtering the collection rendered in the partial view. How could I do that ?
This way your partial will render on click only
<script>
(function($){
$('#btn').click(function(){
$('#searchresult').load('#Url.Content("~/Person/Search")');
}
</script>
Make an ajax request to /search and append the data to your page.
<div id = 'searchresult'>
#Html.Action("Search", "Person");
</div>
whenever your want to filter call something like $('#searchresult').load('/search?q=xxx');
You could use 2 ways:
microsoft ajax helpers
jquery
For both of them you need to remove [ChildActionOnly] and #Html.Action("Search", "Person");
Look at this example: Using Ajax.BeginForm with ASP.NET MVC 3 Razor

How can a single view access multiple models in mvc3

i am developing an application in MVC3.
Basically it is an Question Forum where on Question page user will see all questions as link and a post comment box at the end.and when he clicks on question link he is moved to the answers page.now the answer page is using another model class and their i am not able to acess my answer class data
what i found was every Controller has a view and a model
but i want my view to access multiple models and i dont knw how to go about it..
I tried this:
public class MainClass
{
public Question_Page question{ get; set; }
public Answer_Page answer { get; set; }
}
public class Question_Page
{
public virtual int Id { get; set; }
public virtual string Question_name { get; set; }
public virtual DateTime Created_Date { get; set; }
public virtual DateTime Modified_Date { get; set; }
public virtual int Created_By { get; set; }
public virtual int Modified_By { get; set; }
public virtual char Deleted { get; set; }
public Question_Page()
{
this.Deleted='F';
}
}
public class Answer_Page
{
public virtual int Id { get; set; }
public virtual string Answer { get; set; }
public virtual DateTime Created_Date { get; set; }
public virtual DateTime Modified_Date { get; set; }
public virtual int Created_By { get; set; }
public virtual int Modified_By { get; set; }
public virtual char Deleted { get; set; }
public virtual Question_Page Question { get; set; }
public Answer_Page()
{
this.Deleted='F';
}
}
Now inside the view of Questions List where i display a list of questions:
#model IEnumerable<Core.Model.MainPage>
#{
ViewBag.Title = "Index";
}
<style type="text/css">
ul
{
list-style-type: none;
}
</style>
<h2>All Questions</h2>
<hr />
#using (Html.BeginForm("Index","Question",FormMethod.Post))
{
#Html.ValidationSummary(true)
<ul>
#foreach (var item in Model)
{
<li>#Html.ActionLink(item.Question_name, "answer", new { Qry = item.Id })</li>
}
</ul>
<label for="PostyourQuestion:">Post your Question:</label><br /><br />
#Html.TextArea("textID")
<br />
<input type="submit"/>
}
after doing this much i am gettin errorin line:
#foreach (var item in Model)
This is my controller:
public ActionResult Index()
{
return View(new StudentService().GetAllStudents());
}
[HttpPost]
public ActionResult Index(Question_Page question,string textID)
{
question.Question_name = textID;
question.Created_Date = DateTime.Now;
question.Modified_Date = DateTime.Now;
question.Created_By = 101;
question.Modified_By = 101;
new StudentService().SaveOrUpdateStudent(question);
return View(new StudentService().GetAllStudents());
}
StudentService is a class where i hv defined the methods GetAllStudents(),SaveOrUpdateStudent()
Please help me
Your answer is rather simple I think?
#foreach (var item in Model)
should be
#foreach (var item in Model.Question_Page)
Your model is MainPage?
can you post that model here as well?
assume that GetAllStudents() return IEnumerable<Student>
in your controller
return View(new StudentService().GetAllStudents());
the actual Model send to View is IEnumerable<Student>
if you need IEnumerable<Core.Model.MainPage> as Model just return proper objects from your controller
IEnumerable<Core.Model.MainPage> model = //... define function that return Enumerable MainPage
return View(model);
If You want to use the same model on different views
public ActionResult Index()
{
//I am assuming, You are returning IEnumerable<MainClass>
List<MainClass> mainClass=new StudentService().GetAllStudents();
if(mainClass==null)
mainClass=new List<MainClass>(); //Incase you are not checking null value return
return View(mainClass); //return a none null IEnumerable<MainClass> object
}
[HttpPost]
public ActionResult Index(Question_Page question,string textID)
{
question.Question_name = textID;
question.Created_Date = DateTime.Now;
question.Modified_Date = DateTime.Now;
question.Created_By = 101;
question.Modified_By = 101;
new StudentService().SaveOrUpdateStudent(question);
return View(new StudentService().GetAllStudents());
}
Once your first controller method works, you will need to work on binding your model, for the second post method to work.
try viewModel. A model that have references to your desired model. so if a view is bound to your view model, then all the models referenced in the ViewModel will also get bonded.

Resources