views:

240

answers:

1

My setup:

  • Have a view for a route like: /Pages/Details/2
  • The page details view has <% Html.RenderAction("CreatePageComment", "Comments"); %> to render a comment form
  • Comment form posts to Comments/CreatePageComment
  • /Comments/CreatePageComment returns RedirectToAction when a comment is created successfully
  • This all works nicely

My question:

If there is a validation error, how should I return to /Pages/Detail/1 and show the error in the comment form?

  • If I use RedirectToAction, it seems validation is tricky; should I even be using the Post-Redirect-Get pattern for validation errors, instead of just returning?
  • If I return View() it kicks me back to showing the CreateComment.aspx view (with validation, but just a form on a white page), not the /Pages/Details/2 route that called the RenderAction.

If the PRG pattern should be used, then I think I just need to learn how to do validation while using PRG. If not — and to me this seems better handled by returning View() — then I don't know how to get the user returned to the initial view, showing the form errors, while using RenderAction.

This feels like the game where you tap your head and rub your belly at the same time. I wasn't good at that one either. I'm new at MVC, so that's likely the problem here.

+3  A: 

I believe the answer is to use TempData, for example:

In my view (/Steps/Details) I have:

<!-- List comments -->
<% Html.RenderAction("List", "Comments", new { id = Model.Step.Id }); %>

<!-- Create new comment -->
<% Html.RenderAction("Create", "Comments", new { id = Model.Step.Id }); %>

In my comments controller I have my POST method:

    // POST: /Comments/Create
    [HttpPost]
    public ActionResult Create([Bind(Exclude = "Id, Timestamp, ByUserId, ForUserId")]Comment commentToCreate)
    {
        if (ModelState.IsValid)
        {
            //Insert functionality here

            return RedirectToAction("Details", "Steps", new { id = commentToCreate.StepId });

        }

    //If validation error
        else
        {

            //Store modelstate from tempdata
            TempData.Add("ModelState", ModelState);

            //Redirect to action (this is hardcoded for now)
            return RedirectToAction("Details", "Steps", new { id = commentToCreate.StepId });
        }
    }

Also in the comments controller is my GET method:

    //
    // GET: /Comments/Create

    public ActionResult Create(int id)
    {

        if (TempData.ContainsKey("ModelState"))
        {
            ModelStateDictionary externalModelState = (ModelStateDictionary)TempData["ModelState"];
            foreach (KeyValuePair<string, ModelState> valuePair in externalModelState)
            {
                ModelState.Add(valuePair.Key, valuePair.Value);
            }
        }
        return View(new Comment { StepId = id });
    }

This works great for me, but I'd appreciate feedback on whether this is a good practice, etc.

Also, I noticed that MvcContrib has a ModelStateToTempData decoration that appears to do this, but in a cleaner way. I'm going to try that next.

Roger Rogers
I got a similar problem of what you had. The solution you propose seems working, but I'm curious if there's not a cleaner way of doing it. For me, your solution seems to be more a hack than a real solution. I don't know if anybody else have any other opinion on this ?
Melursus
Yeah, it does feel hackish, but this is all I could find that works. Like you, I'd welcome a cleaner solution.
Roger Rogers
In my research, I find that MvcContrib library have something call SubController that maybe can solve that kind of problem. I don't look further, but it's seems interesting.
Melursus
@Melursus. I have now played with renderaction more, including with forms and AJAX, and it works quite well. The brilliant thing is that partials also work really well in the renderaction view, including with AJAX callbacks.
Roger Rogers