asp mvc list product details in view using View Model

Shaiju T picture Shaiju T · Aug 6, 2015 · Viewed 11.8k times · Source

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.

Answer

trashr0x picture trashr0x · Aug 6, 2015

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.