The ViewBag content isn't being visualized

2019-08-16 08:33发布

问题:

I'm brand new to ASP.NET MVC web development. And I'm trying to build a website that basically has access to a database which can be filled by user.

Alright, all good, that part works fine. What I'm struggling with, is that whenever the user insert data into the database, I wanna show a message saying that his insertion has been done succesfully, or something like that. I wanna show that down in the form panel.

This is the code that I got so far:

This is the method being called when the user clicks the submit button:

[HttpPost]
public ActionResult Create(PersonModels person)
{
    try
    {
        // TODO: Add insert logic here
        //Adding to database and holding the response in the viewbag.
        string strInsertion = ConnectionModels.insertPerson(person);
        ViewBag.InsertionResult = strInsertion;

        return RedirectToAction("Index");
    }
    catch
    {
        return View("Index");
    }
}

And this is my Index action:

public ActionResult Index()
{
    return View();
}

And finally, this is what I'm trying to d in my index.cshtml:

<div>
     <label>
         @ViewBag.InsertionResult
     </label>
</div>

I'm not gonna post it completely 'cause it'd be quite extensive. But that's beneath the div of the form panel.

I hope you can give me a hand.

Thanks in advance! :)

回答1:

You can not pass a ViewBag value while redirecting to another action. If you are in the same controller you can use TempData to pass values in a session otherwise you can pass the message as a parameter to RedirectionResult like below:

return RedirectToAction("Index", new {message="Your Message"});

and then get it back like this:

public ActionResult Index(string message)
{
    ViewBag.ViewBag.InsertionResult = message;
    return View();
}

This is a general way how to pass messages but I would recommend something like this:

Use a BaseController where all controllers inherits from this one:

Here you can make a custom logic how to handle global message like error messages, notification messages, info messages etc.

For that you need to create a model like below:

I am keeping it simple here:

public class GlobalMessage
{
   public string Message { get;set;}
   public AlertType AlertType {get;set;}
}
public enum AlertType
{
   Success, Info, Error, Danger//etc
}

In BaseController you would have something like this:

public abstract class BaseController : Controller
{
    protected GlobalMessage GlobalMessage;
    protected override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.Result is ViewResult)
        {
            if (GlobalMessage!= null)
            {
                filterContext.Controller.ViewBag.GlobalMessage = GlobalMessage;
            }
            else
            {
                GlobalErrorViewModel globalErrorModelView = TempData["GlobalMessage"] as GlobalMessage;

                if (globalErrorModelView != null)
                {
                    filterContext.Controller.ViewBag.GlobalErrorViewModel = globalErrorModelView;
                }
            }
        }
        base.OnActionExecuted(filterContext);
    }

}

In this moment you just have to register new GlobalMessage in Tempdata like below:

public PeopleController : BaseController
{
    [HttpPost]
    public ActionResult Create(PersonModels person)
    {
        try
        {
            // TODO: Add insert logic here
            //Adding to database and holding the response in the viewbag.
            string strInsertion = ConnectionModels.insertPerson(person);
            TempData["GlobalMessage"] = new GlobalMessage{ AlertType = AlertType.Info, Message = "You have successfully added a new person" }

            return RedirectToAction("Index");
        }
        catch
        {
            return View("Index");
        }
    }
}

Then here is the final step how to show data in the view:

I personally use popup or modal window to do this: For example in bootstrapp you would write something like this:

GlobalMessage globalMessage = ViewBag.GlobalMessage as GlobalMessage;
   @if (globalMessage != null)
    {
        <!-- Modal -->
        <div class="modal fade" id="globalMessage" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
            <div class="modal-dialog">
                <div class="modal-content panel-@globalMessage .AlertType.ToString().ToLower() remove-border-radius">
                    <div class="modal-header panel-heading">
                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                    </div>
                    <div class="modal-body">
                        <p class="h-text-primary">@Html.Raw(globalMessage .Message)</p>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-@globalMessage .AlertType.ToString().ToLower() remove-border-radius" data-dismiss="modal">Close</button>
                    </div>
                </div><!-- /.modal-content -->
            </div><!-- /.modal-dialog -->
        </div><!-- /.modal -->
    }

Trigger the modal if there is a message:

@if (globalMessage != null)
{
    <script type="text/javascript">
        $(document).ready(function () {
            $('#globalMessage').modal('show');
        });
    </script>
}

This example was to show how you can make a system to show different messages. In short terms whatever you want!



回答2:

ViewBag and ViewData objects will return null values after a redirect. Their contents only survive for the duration of the current request.

There's another property, TempData, whose contents will endure the current and subsequent request, but only the subsequent request. However, this means that you can use it to pass data when you call RedirectToAction.

[HttpPost]
public ActionResult Create(PersonModels person)
{
    try
    {
        // TODO: Add insert logic here
        //Adding to database and holding the response in the viewbag.
        string strInsertion = ConnectionModels.insertPerson(person);
        TempData[InsertionResult] = strInsertion;

        return RedirectToAction("Index");
    }
    catch
    {
        return View("Index");
    }
}

[HttpGet]
public ActionResult Index()
{
    string strInsertion = TempData[InsertionResult];
    return View("Index", new { InsertionResult = strInsertion });
}

<label>
    @Model.InsertionResult
</label>

A good explanation is given here: http://rachelappel.com/when-to-use-viewbag-viewdata-or-tempdata-in-asp.net-mvc-3-applications/

An alternative approach would be for your Index view to take an optional view model containing the result of the HttpPost that created the Person in your database. You could use that model to populate the contents of your label. Strongly typed models FTW!