views:

65

answers:

2

I have a partial view (ascx) for displaying the top x articles of a person's RSS feed:

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

    <% foreach (var item in Model) { %>

        <a href="<%: item.Url %>"><%: item.Title %></a><br />
        <%: String.Format("{0:g}", item.Date) %><br />
        <%: item.Body %>
        <br />

    <% } %>

In addition, I have a method that takes an RSS URL and returns an IEnumerable of type Article:

public static class ArticleFeedHelper
{
    public static IEnumerable<Models.Article> GetArticles(string feedUrl)
    {
        // Uncaught exception can happen here (e.g. 404, malformed RSS, etc.)
    }
}

On one of my views, I call the partial view like this:

<div id="articleList" class="section">
    <div class="sectionTitle">My Recent Articles</div>
    <hr />
    <div class="sectionBody">
        <% Html.RenderPartial("ArticleList", ArticleFeedHelper.GetArticles(Model.RSSFeed)); %>
    </div>
</div>

The problem is that I would like to surface an error message to the main view. Just because the RSS feed cannot be retrieved should not be so disruptive that it jettisons the user to a full error page.

RESOLUTION:

The partial view, and ArticleFeedHelper stayed the same.

Using Dave's suggestions, the main view was changed to:

<div id="articleList" class="section">
    <div class="sectionTitle">My Recent Articles</div>
    <hr />
    <div class="sectionBody">
        <% Html.RenderAction("ArticleList", "ArticleList", new { feedUrl = Model.RSSFeed }); %>
    </div>
</div>

A partial controller was added:

public class ArticleListController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [ChildActionOnly]
    public ActionResult ArticleList(string feedUrl)
    {
        try
        {
            IEnumerable<Models.Article> articles = Helpers.ArticleFeedHelper.GetArticles(feedUrl);
            return PartialView(articles);
        }
        catch (Exception ex)
        {
            // Handle the error and return an error partial view
        }
    }
}

And a route was added:

routes.MapRoute(
    "ArticleList", // Route name
    "{controller}/{action}/{feedUrl}", // URL with parameters
    new { controller = "ArticleList", action = "ArticleList", feedUrl = "" } // Parameter defaults
);
+1  A: 

I personally wouldn't include the retrieval of the articles as part of the view template. I don't think that is a clean separation of the collection and processing of data (controllers and models) and the presentation of data (views).

You may want to try and retrieve that list of articles in the controller or even the model depending how you are handling service style dependencies (I'm leaning more on the controller for this). Then have your view just able to handle passing the list of articles to the partial if it has one or display any exceptions that occur during the collection of the data.

Sean Copenhaver
+2  A: 

The cleaner way would be to use RenderAction that calls an action, which loads the feed data and can then catch errors.

RssWidget action on ServicesController (could actually reside in every controller, doesn't matter, but I think, that's a clean solution):
This action loads the articles from the feed. Note that the code is only demo and you have to replace RssService.GetArticles() by your implementation of loading the articles. If all goes right, a PartialView (named RssWidget.ascx) is returned, that shows a list of articles.

public ServicesController : Controller
{
    public ActionResult RssWidget()
    {
        try
        { 
            var articles = RssService.GetArticles() // or whatever the articles'
                                                    // source is
            return PartialView(articles);
        }
        catch
        {
            return PartialView("Error");
        }
    }
}

RssWidget.ascx: This is the (partial) view which is used to render the list of articles. Don't forget to replace Article with your actual Type for the articles:

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

<% foreach(var article in Model) { %>
    <div class="article">
        <h3><%= Html.Encode(article.Title) %></h3>
        ...
    </div>
<% } %>

The View where the RssWidget is displayed. You can render this "widget" in any view, you want to. RenderAction("RssWidget", "Services") tells the framework to execute the RssWidget action on the ServicesController and to insert the result in the actual view.

...
<div id="articleList" class="section">
    <div class="sectionTitle">My Recent Articles</div>
    <hr />
    <div class="sectionBody">
        <% Html.RenderAction("RssWidget", "Services"); %>
    </div>
</div>
...
Dave
I like this idea, but how does the Model get passed to that controller?
Forgotten Semicolon
Sorry, I mixed up some things a bit. I've extended my answer now, and I hope it's now clearer.
Dave