36

I have a requirement to display a message confirming a successful database update in an ASP.NET MVC application. Currently the application only shows messages (using a ValidationSummary helper) when an error occurs. On a successful operation, the application currently redirects to a suitable point in the navigation.

Goals are:

  • Display the confirmation message in an appropriate way
  • Minimise user actions required to proceed after reading message
  • Avoid an extra post / round-trip to display the message
  • Minimise development effort and risk inserting a message at multiple points in the application

My preference would be some sort of tool-tip type message display near the submit button and then a mechanism for removing the message and proceeding with the existing redirection after success.

That seems to suggest an Ajax call rather than the existing HTTP POST to submit the form. How would I go about this?

Paul Taylor
  • 5,651
  • 5
  • 44
  • 68

4 Answers4

61

I Would use TempData["key"]

This is like ViewData["key"] however the data persists for the next HttpRequest and is disposed automatically by asp.net after this

So you can do this.

Controller Action

[HttpPost]
public ActionResult SomePostAction(SomeViewModel vm)
{
   if(ModelState.IsValid) // Is User Input Valid?
   {
       try
       {
           CommitData();
           TempData["UserMessage"] = new MessageVM() { CssClassName = "alert-sucess", Title = "Success!", Message = "Operation Done." };
           return RedirectToAction("Success");
       }
       catch(Exception e)
       {
           TempData["UserMessage"] =  new MessageVM() { CssClassName = "alert-error", Title = "Error!", Message = "Operation Failed." };
           return RedirectToAction("Error");
       }

   }

   return View(vm); // Return View Model with model state errors
}

_Layout.cshtml

<!DOCTYPE html>
   <html>
     <head>

     </head>
     <body>
      @if(TempData["UserMessage"] != null)
      { 
          var message = (MessageVM)TempData["UserMessage"];
          <div class="alert @message.CssClassName">
               <strong>@message.Title</strong> 
               @message.Message
          </div>
      }
          @RenderBody()
     </body>
</html>

More Info: http://www.devcurry.com/2012/05/what-is-aspnet-mvc-tempdata.html

SimonGates
  • 5,961
  • 4
  • 40
  • 52
  • 1
    I like this because it gives me the option to show the message on the page to which I redirect after the successful update. If I take this approach, one option would be to show the message in a tooltip type panel that automatically disappears after a predetermined time, requiring no extra user action. It's also low impact in terms of the refactoring required. Thanks! – Paul Taylor Nov 25 '12 at 17:45
  • I like your answer, my only concern is the redirect to error. This would then stop the user re submitting their post. Or maybe that's the idea? The success sure, stops duplicate responses., but the error might require resubmission. – Tod Feb 01 '16 at 11:16
  • Hi @Tod I see your point however I think your concerns are more to do with data validation. If there is a problem with the data submitted by the user then this would be handled using `ModelState.IsValid` and then you can `return View(vm)` i'll update my answer to be a little clearer. – SimonGates Feb 01 '16 at 11:27
  • I've just started implementing this, why have a user message if you're redirecting to two different actions, and assumedly two different views. Just put the messages on the Views. I am fairly new to MVC and I'm just trying to understand. – Tod Feb 01 '16 at 11:36
  • No problem - I'll try to explain a possible use case. With this implementation you could use the `_Layout.cshtml` to display the message - this could be any message. That way you can send a message to the UI from any `controller action` by setting the `TempData["UserMessage"]` - of course the `View` has to use the appropriate `_Layout`. – SimonGates Feb 01 '16 at 14:27
  • @Tod I've updated my answer to give you a more complete example using http://www.w3schools.com/bootstrap/bootstrap_alerts.asp and show you what you could potentially do. please note, I've just wrote this out off the top of my head and isn't tested what-so-ever. – SimonGates Feb 01 '16 at 14:36
  • This doesn't work. https://stackoverflow.com/questions/4862122/mvc-razor-dynamic-model-object-does-not-contain-definition-for-propertyname – v.oddou Mar 20 '17 at 02:29
  • 2
    @TempData["UserMessage"].CssClassName (or . whatever) gives the error that object does not contain a definition for CssClassName. Is there some syntax tweak that would lookup CssClassName? – Chuck Krutsinger May 12 '17 at 21:22
  • @ChuckKrutsinger try creating a concrete class rather than an anonymous class. – SimonGates May 12 '17 at 21:35
  • Is it really worth adding in failure messages in this too, though? They are already handled through ModelState. I agree with the success message but think adding this in for errors is implementing something you already have access to. – perustaja Jan 21 '20 at 05:08
  • @perustaja This serves a slightly different use case. ModelState is typically for form validation, required fields etc. But what happens if the form validations passes but there is some kind of error when you try to persist the data? Out of disk space, database is unavailable, it's for those kind of errors that ModelState won't be aware of. – SimonGates Jan 21 '20 at 09:40
  • @SimonGates Okay that's true, I suppose you could but the message will be handled by the validation summary and as you said that should really be limited to form validation. I was using that before however I see that it isn't really correct to show such an error in the form validation summary. – perustaja Jan 21 '20 at 21:11
16

On a successful operation ,you just store the success message description into ViewBag like as

ViewBag.successMessage="Success" 

then in view check the ViewBag value is null or not? through javascript ,if not null show the message in Div

if('@ViewBag.successMessage'!="")
{
   $('#divSuccessMessage').show();
} 
else
{
  $('#divSuccessMessage').hide();
}

default in page load hide the div

Anand
  • 163
  • 2
  • Thanks, I now see it's more straightforward than I thought. To manage the redirection to another page after displaying the success message, I guess I can add an onclick handler to the message div. – Paul Taylor Nov 25 '12 at 17:41
1

the following links might help you (posting links as it would require better explanation):

http://msdn.microsoft.com/en-us/magazine/ff797575.aspx

http://ofps.oreilly.com/titles/9781449320317/ch_AJAX.html

RohitWagh
  • 1,999
  • 3
  • 22
  • 43
0

As others mentioned, TempData is one of the most straight forward options to use. Its main drawback in regular ASP.NET in my opinion is that it uses the session storage in to store its contents. This means that you'll have extra work getting it to function on a web farm, or that you need to turn on sessions in the first place.

TempData is a string based dictionary you can put anything in it, and by default get it out only once on any later request. Before calling RedirectToAction() you set your message and on the next request you check for messages and display them. By retrieving the messages they are automatically deleted at the end of the request.

As an alternative you could use cookies for transporting the message between the two requests. Essentially you could either roll your own solution, or implement a custom ITempDataProvider which transports the contents of TempData via cookies. Given that the messages are short, the performance impact is minimal. Note that you need to properly secure cookies.

I was facing the same problem you did and created a solution for it called FlashMessage. It's available on NuGet. Usage is simple: you simply queue a message before you call RedirectToAction() as follows:

FlashMessage.Confirmation("Your message");
return RedirectToAction("AdminUsers", "Admin");

In your view you include the following statement to render any previously queued messages:

@Html.RenderFlashMessages()
Christ A
  • 519
  • 5
  • 5