asp mvc list product details in view using View Model

孤街浪徒 提交于 2020-01-03 18:57:10

问题


I am trying to list single product details in a view. The product specification changes dynamically because specifications are added row-wise in table, which means we can add huge number of specifications for each product (as done in ecommerce sites). Right now I am able to meet the requirement using ViewBag, but I am deciding to use ViewModel as a better practice.

Model class:

// Product:
public partial class ProductTable
{
    public ProductTable()
    {
        this.SpecificationsTable = new HashSet<SpecificationsTable>();
    }

    public int ProductID { get; set; }
    public string Title { get; set; }
    public string SmallDescription { get; set; }
    public string FullDescription { get; set; }

    public virtual ICollection<SpecificationsTable> SpecificationsTable { get; set; }
    }

//Specifications:
public partial class SpecificationsTable
{
    public int SpecificationsID { get; set; }
    public string SpecificationName { get; set; }
    public string SpecificationValue { get; set; }
    public Nullable<int> ProductID { get; set; }
    public virtual ProductTable ProductTable { get; set; }
}

ViewModel:

public class DetailsViewModel
{
    public int ProductID { get; set; }
    public string Title { get; set; }
    public string SmallDescription { get; set; }
    public string FullDescription { get; set; }
    public string SpecificationName { get; set; }
    public string SpecificationValue { get; set; }
}

ActionMethod

public ActionResult ProductDetails(int id)
{
    var details = (from c in dbo.ProductTable
                   join s in dbo.SpecificationsTable 
                   on c.ProductID equals s.ProductID
                   where c.ProductID == id
                   select new DetailViewModel
                   {
                       Title = c.Title,
                       SmallDescription = c.SmallDescription,
                       FullDescription = c.FullDescription
                   }).ToList();

     // To remove repeated product title , small and full description
     var distinctItems = details.GroupBy(x => x.ProductID).Select(y => y.First());

     // To show product title, small and full description for this product

     ViewBag.ProductDetails = distinctItems;

     var specifications = (from c in dbo.ProductTable
                             join s in dbo.SpecificationsTable 
                             on c.ProductID equals s.ProductID
                             where c.ProductID == id
                             select new DetailViewModel
                             {
                                 SpecificationName = s.SpecificationName,
                                 SpecificationValue = s.SpecificationValue
                             }).ToList();

    // To show list of specifications for this product
    ViewBag.Specifcations = specifications;
    return View();
}

expected output in view:

Details:

Title: New Samsung offer

SmallDescription : Something small 

FullDescription : Something full

Specifcations:

Mobile Name :Samsung

Model : 2015

Price : 70 $

Color:  White

I am using database first method and I am trying to learn how we can use view model here.


回答1:


You current view model does not reflect what you want to display in the view, which is multiple specification for each product, so you need a collection property. Change your view models to

public class SpecificationVM
{
  public string Name { get; set; }
  public string Value { get; set; }
}
public class ProductVM
{
  public string Title { get; set; }
  public string SmallDescription { get; set; }
  public string FullDescription { get; set; }
  IEnumerable<SpecificationVM> Specifications { get; set; }
}

Then in the controller, populate you view model using

public ActionResult ProductDetails(int id)
{
  var product = db.ProductTable.Where(p => p.ProductID == id).FirstOrDefault();
  // note you may need to add .Include("SpecificationsTable") in the above
  if (product == null)
  { 
    return new HttpNotFoundResult();
  }
  ProductVM model = new ProductVM()
  {
    Title = product.Title,
    SmallDescription = product.SmallDescription,
    FullDescription = product.FullDescription,
    Specifications = product.SpecificationsTable.Select(s => new SpecificationVM()
    {
      Name = s.SpecificationName,
      Value = s.SpecificationValue
    })
  };
  return View(model);
}

Then in the view

@model yourAssembly.ProductVM
<h2>Details</h2>
@Html.DisplayNameFor(m => m.Title)
@Html.DisplayFor(m => m.Title)
.... // ditto for SmallDescription and FullDescription 
<h2>Specifications</h2>
@foreach(var item in Model.Specifications)
{
  @Html.DisplayFor(m => item.Name)
  @Html.DisplayFor(m => item.Value)
}



回答2:


Viewmodels are indeed the way to go here. I won't elaborate on what viewmodels are as you already have some declared in your code so I'm assuming you already know their purpose and functionality. However—for the sake of completeness—viewmodels represent only the data that you want displayed in your view; assuming a view displaying the first and last name of an employee, there's no reason to send back an Employee object when you only really need two of it's properties.

As per the short viewmodel definition provided above, to return a viewmodel instead of attaching the returned object on ViewBag, you simply create a class which will only hold the data that you want presented in the view. So, based on your "expected output in view", your viewmodel would look similar to this:

public class YourViewModel 
{
    public string Title { get; set; }
    public string SmallDescription { get; set; }
    public string FullDescription { get; set; }
    public string MobileName { get; set; }
    public int ModelYear { get; set; }
    public decimal Price { get; set; }
    public string Color { get; set; }
}

Once you have your viewmodel instantiated and populated, pass it to the view:

var yourViewModel = new YourViewModel();
//populate it and pass it to the view
return View(yourViewModel);

On the top part of your view, declare your @model variable to be of type YourViewModel:

@model YourViewModel

..and you're good to go. So if you wanted to print out the mobile name in the view:

@Model.MobileName

Bear in mind that while you can only have one @model per view, you can still have multiple viewmodels. This is possible by creating a parent viewmodel to hold all the view-related viewmodels and setting that as the @model instead:

public class ParentViewModel
{
    //all the viewmodels which are relevant to your view
    public DetailsViewModel DetailsViewModel { get; set; }
    public YourViewModel YourViewModel { get; set; }
    //...etc        
}

Once you have your viewmodel instantiated and populated, pass it to the view:

var parentViewModel = new ParentViewModel();
var yourViewModel = new YourViewModel();
//populate it and attach it to the parent viewmodel
parentViewModel.YourViewModel = yourViewModel;
return View(parentViewModel);

This time, on the top part of your view, declare your @model variable to be of type ParentViewModel instead:

@model ParentViewModel

..and you're good to go. Using the same example, if you wanted to print out the mobile name in the view:

@Model.YourViewModel.MobileName

Bear in mind that I haven't really given much attention to how you have structured your viewmodels, but rather explained how to pass one (or more) viewmodels back to your view and use those instead of ViewBag (as per your question). For how your viewmodels should be actually populated and look like, Stephen Muecke's answer is the way to go.



来源:https://stackoverflow.com/questions/31854913/asp-mvc-list-product-details-in-view-using-view-model

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!