views:

43

answers:

2

A common scenario I encounter is providing notifications / confirmations to users after they have performed an action to inform them of success.

For example, suppose a user provides feedback on a feedback form and then clicks Submit Feedback. You may want to display a 'Thanks for your Feedback' message after you have performed some validation e.g. they have a valid email in the database. Some pseudocode:

public ActionResult SubmitFeedback(string Feedback, int UserID)
{
    MyDataContext db = new DataContext()

    if(db.usp_HasValidEmail(UserID)) //Check user has provided a valid email
        return View("Index"); //Return view and display confirmation
    else
        ModelState.AddModelError("InvalidEmail", "We do not hold an email record for you. Please add one below");
        return View("Index);
}

I understand how to validate entries by using Html.ValidationMessage etc. This is fine and I typically check for invalid entries either using jQuery on the client side or early in my Action (i.e. before I start hitting the database) and exit my action if there are invalid entries.

However, what about the scenario where all entries are valid and you want to display a confirmation message?

Option 1: Have an entirely separate View

This seems to violate DRY principles by having an entirely new View (and ViewModel) to display almost identical information, expect for the user notifcation.

Option 2: Conditional Logic in the View

In this scenario I could have a conditional statement in the View that checks for the presence of some TempData that is passed in the SubmitFeedback Action. Again, pseudocode:

   <% if(TempData["UserNotification"] != null {%>
   <div class="notification">Thanks for your Feedback&#33;</div>
   <% } %>

Option 3: Use jQuery to check for TempData on page load

In this scenario I would have a hidden field that I would populate with TempData via the SubmitFeedback Action. I would then use jQuery to check the hidden field value. More pseudocode:

<%=Html.Hidden("HiddenField", TempData["UserNotification"])%> //in View

$(document).ready(function() {
    if ($("input[name='HiddenField']").length > 0)
        $('div.notification').show();
        setTimeout(function() { $('div.notification').fadeOut(); }, 3000);
});

My initial thoughts on this are:

  • Option 1: Complete separation of Views but seems like overkill and inefficient (violates DRY)
  • Option 2: Simple enough, but has conditional logic in the View (don't I get sacrificed at the MVC altar for this?!?)
  • Option 3: Feels like it is overcomplicating things. It does avoid logic in View though.

What say you? Option 1,2,3 or none? Is there a better way?

Please augment my coding patterns!

+1  A: 

I like option 1. Also you don't need conditions and it works with javascript disabled. Just stick it somewhere in the masterpage and it should be OK:

<div class="notification">
    <%= Html.Encode(TempData["Notification"]) %>
</div>

You could of course progressively enhance/animate this by using some nice plugin such as jGrowl or Gritter or even look at how StackOverflow does it.

Another solution is to write a helper which is probably the neatest:

public static class HtmlExtensions
{
    public static MvcHtmlString Notification(this HtmlHelper htmlHelper)
    {
        // Look first in ViewData
        var notification = htmlHelper.ViewData["Notification"] as string;
        if (string.IsNullOrEmpty(notification))
        {
            // Not found in ViewData, try TempData
            notification = htmlHelper.ViewContext.TempData["notification"] as string;
        }

        // You may continue searching for a notification in Session, Request, ... if you will

        if (string.IsNullOrEmpty(notification))
        {
            // no notification found
            return MvcHtmlString.Empty;
        }

        return FormatNotification(notification);
    }

    private static MvcHtmlString FormatNotification(string message)
    {
        var div = new TagBuilder("div");
        div.AddCssClass("notification");
        div.SetInnerText(message);
        return MvcHtmlString.Create(div.ToString());
    }

}

And then in your master:

<%= Html.Notification() %>
Darin Dimitrov
Thanks - hadn't considered creating an HTML Helper but I can now see how this would be a neat solution and makes sense. Many thanks for helping me out and providing some code that I can build on
Remnant
A: 

For general notification stuff, I usually create a partial view to hide the ugly conditional code, and include it on my master layout.

So, in an appropriate place in the master:

<% Html.RenderPartial("_Notifications") %>

And in a partial view:

<% if(TempData["UserNotification"] != null {%>
    <div class="notification">Thanks for your Feedback&#33;</div>
<% } %>
richeym
Thanks for your input. This would work for me, though I have decided to build a HtmlHelper that Darin suggested. Great work. Thanks again.
Remnant