简体   繁体   中英

Success message from Controller to View

The goal

I want to display in my view some message when some user is added.

The problem

When something goes wrong in our model, there is a method ( ModelState.AddModelError ) to handle unsuccessful messages. But, when the things go okay, how can we handle a message to the user saying that his action was a success?

I found this thread that provides a solution, but about three years passed and I need to know: there's not another way, perhaps more mature? Not that this is not, but we still deal with messages of success on this same way?

Expanding from Brad Christie's answer , I created a NuGet package, BootstrapNotifications , that will do this for you with built-in Bootstrap3 support. This package also supports multiple notification types (error, warning, success, and info) with pre-styled alerts and is easily extendable.

The extension supports multiple notifications per request of the same type and different types elegantly.

The Code

NotificationExtensions.cs :

public static class NotificationExtensions
{
    private static IDictionary<String, String> NotificationKey = new Dictionary<String, String>
    {
        { "Error",      "App.Notifications.Error" }, 
        { "Warning",    "App.Notifications.Warning" },
        { "Success",    "App.Notifications.Success" },
        { "Info",       "App.Notifications.Info" }
    };


    public static void AddNotification(this ControllerBase controller, String message, String notificationType)
    {
        string NotificationKey = getNotificationKeyByType(notificationType);
        ICollection<String> messages = controller.TempData[NotificationKey] as ICollection<String>;

        if (messages == null)
        {
            controller.TempData[NotificationKey] = (messages = new HashSet<String>());
        }

        messages.Add(message);
    }

    public static IEnumerable<String> GetNotifications(this HtmlHelper htmlHelper, String notificationType)
    {
        string NotificationKey = getNotificationKeyByType(notificationType);
        return htmlHelper.ViewContext.Controller.TempData[NotificationKey] as ICollection<String> ?? null;
    }

    private static string getNotificationKeyByType(string notificationType)
    {
        try
        {
            return NotificationKey[notificationType];
        }
        catch (IndexOutOfRangeException e)
        {
            ArgumentException exception = new ArgumentException("Key is invalid", "notificationType", e);
            throw exception;
        }
    }
}

public static class NotificationType
{
    public const string ERROR = "Error";
    public const string WARNING = "Warning";
    public const string SUCCESS = "Success";
    public const string INFO = "Info";

}

_Notifications.cshtml :

@using YourApp.Extensions
@{
    var errorList = Html.GetNotifications(NotificationType.ERROR);
    var warningList = Html.GetNotifications(NotificationType.WARNING);
    var successList = Html.GetNotifications(NotificationType.SUCCESS);
    var infoList = Html.GetNotifications(NotificationType.INFO);
}
<!-- display errors -->
@if (errorList != null)
{
    <div class="alert alert-danger alert-dismissable">
        <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
        @if(errorList.Count() > 1){
            <strong><span class="glyphicon glyphicon-remove"></span> There are @errorList.Count() errors: </strong>
            <ul>
                @foreach (String message in errorList)
                {
                    <li>@Html.Raw(message)</li>
                }
            </ul>
        }
        else{
            <strong><span class="glyphicon glyphicon-remove"></span> Error: </strong>
            @Html.Raw(errorList.First())
        }
    </div>
}

<!-- display warnings -->
@if (warningList != null)
{
    <div class="alert alert-warning alert-dismissable">
        <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
        @if(warningList.Count() > 1){
            <strong><span class="glyphicon glyphicon-warning-sign"></span> There are @warningList.Count() warnings: </strong>
            <ul>
                @foreach (String message in warningList)
                {
                    <li>@Html.Raw(message)</li>
                }
            </ul>
        }
        else{
            <strong><span class="glyphicon glyphicon-warning-sign"></span> Warning: </strong>
            @Html.Raw(warningList.First())
        }
    </div>
}

<!-- display success -->
@if (successList != null)
{
    <div class="alert alert-success alert-dismissable">
        <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
        @if(successList.Count() > 1){
            <strong><span class="glyphicon glyphicon-ok"></span> There are @successList.Count() successful notifications: </strong>
            <ul>
                @foreach (String message in successList)
                {
                    <li>@Html.Raw(message)</li>
                }
            </ul>
        }
        else{
            <strong><span class="glyphicon glyphicon-ok"></span> Success! </strong>
            @Html.Raw(successList.First())
        }
    </div>
}

<!-- display success -->
@if (infoList != null)
{
    <div class="alert alert-info alert-dismissable">
        <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
        @if(infoList.Count() > 1){
            <strong><span class="glyphicon glyphicon-info-sign"></span> There are @infoList.Count() notifications: </strong>
            <ul>
                @foreach (String message in infoList)
                {
                    <li>@Html.Raw(message)</li>
                }
            </ul>
        }
        else{
            <strong><span class="glyphicon glyphicon-info-sign"></span> </strong>
            @Html.Raw(infoList.First())
        }
    </div>
}

To see all of this code and how its used, you can download a full working demo from github .

There are a few ways to skin this cat. You could use the ViewBag:

ViewBag.SuccessMessage = "<p>Success!</p>";

Then in your view you could render it to the page:

@ViewBag.SuccessMessage

I'm not a fan of the ViewBag, so I typically have a ViewModel object created that holds all the data I would need for my particular view. And a success message would be just that kind of data:

public MyViewModel{
    public bool IsSuccess {get;set;}
}

Then in your controller, you would pass this ViewModel to your stongly-typed view

[HttpPost]
public ActionResult Update(MyViewModel vm){
    //Glorious code!

   return View(vm)
}

Finally, just check it in your view and print a message if it succeeds:

@if(vm.IsSuccess){
     <p>Here is an amazing success message!</p>
}

Also, instead of that, you can use TempData, which works like the ViewBag but only lasts until the end of your next request and is then discarded:

TempData["SuccessMessage"] = "Success!";

TempData isn't a bad way to hand one-offs to the UI for the purposes of notifying the user. The great part about them is they persist between action calls, but are removed once they're read. So, in the case of just handing off a "it worked" message, it works great.

You can tie them in several ways, but I'll give you a general example to get you going:

public static class NotificationExtensions
{
    private const String NotificationsKey = "MyApp.Notifications";

    public static void AddNotification(this ControllerBase controller, String message)
    {
        ICollection<String> messages = controller.TempData[NotificationsKey] as ICollection<String>;
        if (messages == null)
        {
            controller.TempData[NotificationsKey] = (messages = new HashSet<String>());
        }
        messages.Add(message);
    }

    public static IEnumerable<String> GetNotifications(this HtmlHelper htmlHelper)
    {
        return htmlHelper.ViewContext.Controller.TempData[NotificationsKey] as ICollection<String> ?? new HashSet<String>();
    }
}

Now in your action you can call this.AddNotification("User successfully added!"); and within your view you can display them using:

@foreach (String notification in Html.GetNotifications())
{
    <div class="notification">
        <p>@notification/p>
        <i class="icon-close"></i>
    </div>
}

(...Or something similar) which could be effectively placed in your main view and used as a general notification method for any action performed. (Almost like how StackOverflow has the gold bar at the top of the page during certain events).

A good solution for this is the TempData collection. Its values are cleared at the end of the request which makes it ideal for one time messages like informing the user that something was successful.

Controller

TempData["Message"] = "Operation successful!";

View

@TempData["Message"]

And yes, this is still currently the best approach.

TempData

Use MVC TempData - TempData

It's only available for that page request. Which is perfect for success messages etc.

Conceptually, I think the answer still holds. If the message is an integral part of the view, it should be part of the ViewModel . TempData is a shortcut for passing data without modifying the ViewModel definition, which some people frown upon.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM