Thursday, August 11, 2011

abstract a grid of data from a View to a Partial

Let's say you have CRUD screens for Foo. The entry to CRUD capabilities is a View that has a few links on it and otherwise gives a list of Foo with different columns for different parameters. Cool.

<table>

   <tr>

      <th>Bar</th>

      <th>Name</th>

      <th>Happy?</th>

      <th>Description</th>

   </tr>

   @foreach (var item in Model)

   {

      <tr>

         <td>@Html.DisplayFor(modelItem => item.Bar.Name)</td>

         <td>@Html.DisplayFor(modelItem => item.Name)</td>

         <td>@Html.DisplayFor(modelItem => item.IsHappy)</td>

         <td>@Html.DisplayFor(modelItem => item.Description)</td>

      </tr>

   }

</table>

 
The model binding at the top of View looks like this:

@model IEnumerable<App.Web.UI.Models.Foo>

 
OK. So what is Bar? Let's say Bar is a parent object of Foo and let's say that when we view details on an instance of Bar from another controller that we want to list all of the Foo for Bar. Sound fair? It sounds like we need to do some refactoring. It sounds like the list needs to be abstracted to a partial. Moreover, it will be silly to show the column for Bar when viewing the list from the Bar controller as the data their will not vary and will be heavily implied by everything else displayed in the same View as the list. Alright, I purpose we end up with a partial that looks like this:

@model object[]

@* the object array should contain two objects *@

@* the first is the model *@

@* the second, a bool for whether to show the category column *@

@{

   IEnumerable<Foo> foos = (IEnumerable<Foo>)Model[0];

}

<table>

   <tr>

      @if ((bool)Model[1])

      {

         <th>Bar</th>

      }

      <th>Name</th>

      <th>Happy?</th>

      <th>Description</th>

   </tr>

   @foreach (var item in foos)

   {

      <tr>

         @if ((bool)Model[1])

         {

            <td>@Html.DisplayFor(modelItem => item.Bar.Name)</td>

         }

         <td>@Html.DisplayFor(modelItem => item.Name)</td>

         <td>@Html.DisplayFor(modelItem => item.IsHappy)</td>

         <td>@Html.DisplayFor(modelItem => item.Description)</td>

      </tr>

   }

</table>

 
What does this teach us beyond how to make Razor comments? It teaches us that the real model has to be wrapped in a greater object model in the name of passing in something else with it. The "official" model is now an object array and one piece of it will be the bool for whether or not to show the Bar column.

The view that used to hold this data references like so:

@model IEnumerable<App.Web.UI.Models.Foo>

@{

   var showBarColumn = true;

   var modelPlus = new object[] { Model, showBarColumn };

}

...some code...

@{Html.RenderPartial("_ListOfFoo",modelPlus);}

 
When the other controller spins up a view for Bar details, it will be like so:

@model App.UI.Models.Bar

@{

   var showBarColumn = false;

   var modelPlus = new object[] { Model.GetAllFoo(), showBarColumn };

}

...some code...

@{Html.RenderPartial("~/Views/Foo/_ListOfFoo.cshtml",modelPlus);}

No comments:

Post a Comment