Rendering a child view with Html.RenderAction

In the previous recipe, we took a look at how you can split up the view code into multiple partial views to make managing presentation code easier and more reusable. In most cases, this is exactly what you will need, as a view usually has a view model that is specially built just for it. In our previous recipe, we took pieces of the overall view's model and pushed bits of it off to the partial views.

In other cases though you may have totally separate models that are displayed by separate views. Sometimes they may even be handled by different actions in different controllers. In this case, you might try to render an action directly and put the result of that view into another view. In this way, we won't have to worry about cross-pollinating models and views from various controllers.

How to do it...

  1. Create a new MVC application.
  2. Then we will create a quick model to play with. We need two distinctly different models to be able to demonstrate why we would need to render one action from within another. We will create a Post class to represent some blog posts. And we will create a Product class to represent a list of suggested products, which we will display next to our blog posts.

    Models/Post.cs:

    public class Post
    {
    public DateTime Created { get; set; }
    public string Title { get; set; }
    public string Body { get; set; }
    }
    

    Models/Product.cs:

    public class Product
    {
    public string Name { get; set; }
    public string Description { get; set; }
    public double Cost { get; set; }
    }
    
  3. Then we need to create a couple of service classes from which we will generate a list of our new object models. We will create a ProductService class and a BlogService class. Each of these classes will have Get methods on them to get a list of the specific objects we need.

    Models/BlogService.cs:

    public class BlogService
    {
    public List<Post> GetPosts(int count)
    {
    List<Post> result = new List<Post>();
    for (int i = 0; i < count; i++)
    {
    Post p = new Post();
    p.Created = DateTime.Now;
    p.Title = "A really great post";
    p.Body = @"Lorem ipsum dolor sit amet, ...";
    result.Add(p);
    }
    return result;
    }
    }
    

    Models/ProductService.cs:

    public class ProductService
    {
    public List<Product> GetProducts(int count)
    {
    List<Product> result = new List<Product>();
    Random r = new Random();
    for (int i = 0; i < count; i++)
    {
    Product p = new Product();
    p.Cost = r.Next(5, 50);
    p.Name = "Really great product";
    p.Description = @"Lorem ipsum ...";
    result.Add(p);
    }
    return result;
    }
    }
    
  4. Now that we have the ability to generate a list of working data, we can next turn our attention to creating two controllers to handle views for each of our two object types. We will create a BlogController and a ProductController. The BlogController will expose an Index action to show a list of recent blog posts. The ProductController will have a SuggestedProducts action that will return a list of products.

    Models/BlogController.cs:

    public class BlogController : Controller
    {
    public ActionResult Index()
    {
    return View(new BlogService().GetPosts(5));
    }
    }
    

    Models/ProductController.cs:

    public class ProductController : Controller
    {
    public ActionResult SuggestedProducts()
    {
    return View(new ProductService().GetProducts(7));
    }
    }
    
  5. The next thing for us to do is to generate a view for each of our controllers. We will start with the ProductController, as its view is the easiest. For this controller, we will generate a strongly typed partial view based on a Product using the details view. Once the view is generated, we have to change the model from a single instance of Product to a List of Product. Then we need to wrap the details view that was generated with a loop.

    Views/Product/SuggestedProducts.aspx:

    <%@ Page Title="" Language="C#" Inherits= "System.Web.Mvc.ViewPage<List<MvcApplication1.Models.Product>>" %>
    <%@ Import Namespace="MvcApplication1.Models" %>
    <fieldset>
    <legend>Suggested Products</legend>
    <% foreach (Product p in Model) { %>
    <div class="display-field"><b><%: p.Name%></b></div>
    <div class="display-field">
    <i><%: String.Format("{0:C}", p.Cost)%></i>
    </div>
    <div class="display-field"><%: p.Description%></div>
    <% } %>
    </fieldset>
    
  6. Now we need to generate our primary view, which will be responsible for showing a list of blog posts. In addition to displaying blog posts, we will also render the list of suggested products. Similar to our previous view, we will start by generating the view from the Index action of the BlogController. This will be a strongly typed details view based on the Post class. Once it is generated, we will need to wrap the generated view code with a table so that the list of blog posts can sit next to a list of suggested products. I also added a bit of styling to get things to line up a bit better.

    Views/Blog/Index.aspx:

    <h2>My Blog</h2>
    <table style="width:800px;">
    <tr>
    <td style="width:200px;vertical-align:top;">
    <!-- Suggested products... -->
    </td>
    <td style="vertical-align:top;">
    <fieldset>
    <legend>Recent Posts</legend>
    <%
    foreach (Post p in Model)
    { %>
    <div class="display-field"><b><%: p.Title%></b></div>
    <div class="display-field"><i>
    <%: String.Format("{0:g}", p.Created)%></i></div>
    <div class="display-field"><%= p.Body%></div>
    <% } %>
    </fieldset>
    </td>
    </tr>
    </table>
    
  7. Now that we have our primary view created, we can turn our attention to rendering a list of suggested products. All that we need to do is add a call to Html.RenderAction where we currently have this comment <!-- Suggested products...-->. In order to make this call, we only need to specify the name of the action and the controller that we want to render.

    Note

    If, while rendering this recipe, you see two master pages, then the odds are that you didn't generate a partial view for your SuggestedProducts view!

    Views/Blog/Index.aspx:

    ...
    <td style="width:200px;vertical-align:top;">
    <% Html.RenderAction("SuggestedProducts", "Product"); %>
    </td>
    ...
    
  8. Now you can run the application. Navigate to /Blog/Index and you should see a list of blog posts. Next to that you should also see a list of suggested products. Notice that our blog controller didn't have to know anything about products in order for it to use the SuggestedProducts view. This is the power of the RenderAction helper method.
    How to do it...

How it works...

In this recipe, we saw how we can separate the logic that views require into multiple controllers while still being able to use those views together. This functionality is built into the MVC framework and exposed through the HtmlHelper class.

There's more...

Be aware that using this method has one gotcha. If you want to use output caching on the rendered view—don't. It won't work (at the time of this writing). You can put an OutputCache attribute on the SuggestedProducts view, but when you render the SuggestedProducts partial view from within the product's Index, the OutputCache attribute is simply ignored from the parent view. Check out the chapter on caching and you will find a workaround to this issue!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset