Hi I have included nested class within a class to use it in a view but it doesnt show up the properties of that nested class below is the class, I want to use sku in the view :
View:
#model Nop.Web.Models.Catalog.CategoryModel
<div class="product-item">
<h2 class="product-title">
#Model.Name
</h2>
<div class="description">
**#Model.ProductVariantModels.Select(x => x.Sku)//doesnt works** // partial class productvariant
</div>
<div class="add-info">
#Model.Name <br/> #Model.FullDescription //values from class CategoryModel
</div>
</div>
Model:
public class CategoryModel : BaseEntityModel
{
public CategoryModel()
{
ProductVariantModels = new List<ProductVariantModel>();
}
public string Name { get; set; }
public string FullDescription { get; set; }
public string MetaKeywords { get; set; }
public string MetaDescription { get; set; }
public string MetaTitle { get; set; }
public IList<ProductVariantModel> ProductVariantModels { get; set; }
public class ProductVariantModel : BaseNopEntityModel
{
public string Name { get; set; }
public bool ShowSku { get; set; }
public string Sku { get; set; }
public string Description { get; set; }
}
}
ProductVariantModels is a List. You have to enumerate the List.
#foreach (var pvModel in Model.ProductVariantModels) {
#pvModel.Sku
}
If you're using HtmlHelpers, you have to use a for loop with an index rather than a foreach loop.
As explained really well here, the automatic model binding expects field input names to have a 'dot' notation like Property.SubProperty.SubSub... to match the instance properties when assigning -- but if you render them in a foreach loop they won't have the full expression, and thus won't output the full 'dot' notation.
Also see MVC 4 binding nested list of lists result
Related
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; }
}
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)
I am having problems sending a model from a form on my view to my controller. The main class I has the data in it but the sub classes have lost their values.
I have a main Quotation class like this:
public class Quotation
{
public string QuotationID { get; set; }
public TaxiPartner taxiPartner { get; set; }
public TaxiCompany taxiCompany { get; set; }
public int maxNumberOfSeats { get; set; }
public int maxNumberOfBags { get; set; }
public double price { get; set; }
}
Then for example I have the TaxiPartner class:
public class TaxiPartner
{
public string Id { get; set; }
public string Name { get; set; }
public List<Address> Addresses { get; set; }
public bool supportsPasswordReset { get; set; }
public bool supportsValidatebysms { get; set; }
}
Here is the form in my view
#if (Model.listOfQuotations.Count > 0)
{
<h3>You can book online with:</h3>
foreach (var item in Model.listOfQuotations)
{
using (Html.BeginForm("BookingPage1", "SearchResults", FormMethod.Post))
{
<div class="well">
<div>
<h4>#item.taxiCompany.Name</h4>
</div>
<div style="float: right;">
<h2>#string.Format("{0:C}", item.price)</h2>
</div>
<div>
<b>Product Type:</b>
<img style="width: 15px;" src="~/Content/images/icon-#item.taxiPartner.cabforceProductType-orange-40px.png" />
(1 = Best Value, 2 = Executive, 3 = Minibus)
</div>
<div><b>Services:</b> #item.taxiCompany.services (0 = Outdoor pickup, 1 = Meet & greet pickup)</div>
<div><b>Maximum Number of Seats:</b> #item.maxNumberOfSeats <b>Maximum Number of Bags:</b> #item.maxNumberOfBags</div>
<div>#Html.ActionLink("Book Now", "BookingPage1", item, new { #class = "btn btn-success" })</div>
</div>
}
}
}
I then have the following action in my controller:
[HttpPost]
public ActionResult BookingPage1(Quotation chosenQuote)
{
return View();
}
When my controller action is called I can see the values for QuotationID, maxNumberOfSeats, maxNumberOfBags, and price but the values that were contained in taxiPartner and taxiCompany are all NULL?
I have debugged and the value of "item" in the view does contain the values I need but they have disappeared once it gets to the controller.
Can anyone see what I am doing wrong please?
Only the model fields assigned to input types will available in the controller
Merely displaying the values isn't enough for it to be passed back. If you have a value that you don't have an input for, you should use a hidden value:
#Html.HiddenFor(x=>x.TaxiPartner.Name)
Also, something you may want to think about: if you have a bunch of properties that aren't being used/displayed in this view, you may want to think about creating a separate view model that only holds the values you need for that view rather than putting a bunch of hidden input fields to pass around values that aren't ever needed.
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)
}
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; }
}
}