Search code examples
asp.netasp.net-mvcviewviewmodelviewdata

Can you help with this MVC ViewModel issue?


I have a problem with an MVC view that I just cannot seem to solve. Here it is.

1) I have an index view that displays a list of retailers with data from the retailers table. So far so good.

2) I also want to include the retailer categories for each retailer which are stored in a RetailersCategories table where each retailer can have multiple categories

I have tried a few things but cannot seem to make this work. The closest I came to what I wanted was using a view model. I have included the code below.

I actually get the right data back but I get all of the retailer records and then all of the category records.

What I need is one retailer record at a time with all of the categories that relate to that retailer.

Can anyone show me how I can achieve this?

//Controller   

public ActionResult Index(int? page, int country)
{
    var viewdata = new retailersIndexViewModel(_retailerRepository.GetAllRetailersByCountry(country),  _retailerRepository.GetRetailerCategories());
    return View(viewdata);       
}

// ViewModel

public class RetailersIndexViewModel
{        
    public IEnumerable<RetailersShipping> RetailerShipping { get; set; }
    public IEnumerable<RetailersCategory> RetailerCategories { get; set; }

    public RetailersIndexViewModel(IEnumerable<RetailersShipping> retailer, IEnumerable<RetailersCategory> retailercategory)
    {
        this.RetailerShipping = retailer;
        this.RetailerCategories = retailercategory;
    }
}


//IndexView

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Inner.Master" Inherits="System.Web.Mvc.ViewPage<RetailersIndexViewModel>" %>

<% Html.RenderPartial("RetailerSummaryPartial", this.ViewData.Model.RetailerShipping); %>
<div id="retailer_index_categories">

<%
    foreach (RetailersCategory category in ViewData.Model.RetailerCategories) 
    {%>
        <% Html.RenderPartial("RetailerCategoryPartial", category); %>
    <% } %>


// RetailerSummaryPartial

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<RetailersShipping>>" %>

<div id="retailer_partial_summary">
<% foreach (var retailer in Model)
{ %>
    <div id="retailer_index_image">
        <img src="<%=Html.Encode(retailer.Retailer.Country.Image) %>" title="<%= Html.Encode(retailer.Retailer.Name) %>>" alt="<%= Html.Encode(retailer.Retailer.Name) %>" class="main-image" />
        <br />
    </div>
    <div id="retailer_index_logo">
        <img src="<%=Html.Encode(retailer.Retailer.Logo) %>" title="<%= Html.Encode(retailer.Retailer.Name) %>>" alt="<%= Html.Encode(retailer.Retailer.Name) %>" class="main-image" />
    </div>
    <div id="retailer_index_name_comment">
        <%= Html.Encode(retailer.Retailer.Name)%><br />
        <span><%if (retailer.Retailer.CountryId == retailer.Retailer.CountryId) %>
                <%= Html.Encode(retailer.Retailer.LocalComment)%>
                <%= Html.Encode(retailer.Retailer.IntComment)%>
        </span>
    </div>

<% } %>
</div>


//RetailerCategoryPartial

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<RetailersCategory>" %>
<div id="retailer_index_categories">
    <%= Html.Encode(Model.Category.CategoryName) %>
</div>

Solution

  • I'd refactor your view models. If you're going to be representing a collection within a collection it's best your view models reflect that.

    public class RetailersIndexViewModel {
    
        public IEnumerable<RetailersShippingViewModel> RetailerShippings { get; set; }
    
        public RetailersIndexViewModel(IEnumerable<RetailersShipping> shippings)
        {
            this.RetailerShippings = shipping;
            foreach( var shipping in shippings)
            {
                shipping.RetailerCategories = shipping.Categories // assuming Categories is referenced in your Retailer Shipping class;
            }
        }
    }
    
    public class RetailerShippingViewModel {
    
        public IEnumerable<RetailersCategory> RetailerCategories { get; set; }
    
        public RetailersIndexViewModel(IEnumerable<RetailersCategory> retailercategories)
        {
            this.RetailerCategories = retailercategories;
        }
    }
    

    and render it like so

    <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Inner.Master" Inherits="System.Web.Mvc.ViewPage<RetailersIndexViewModel>" %>
    
       <% foreach(var shipping in Model.RetailerShippings)
          {
              Html.RenderPartial("RetailerSummaryPartial", shipping); 
          }%>
    

    call this in your RetailerSummaryPartial instead of the index view

    <%
        foreach (var category in ViewData.Model.RetailerCategories) 
           {%>
        <% Html.RenderPartial("RetailerCategoryPartial", category); %>
        <% } %>