Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC 4 - Redirect after Modal Save using Child Action


UPDATE - There are a lot of posts regarding the Child actions are not allowed to perform redirect actions on SO and ASP.NET forums. The solution below seems to be the simplest workaround that I've seen yet for this problem. If you have this issue - hopefully you'll find it helpful. If there is a better one, I'm certainly open to suggestions.


I've been wracking my brain with this all day...

I have a FranchiseSet model and a Franchise model - the relationship is that a FranchiseSet has-many Franchises.

On the FranchiseSet index view there is a table of Franchise Sets where each row is clickable. When I click a row, I return a view from the Details method of the FranchiseSetController - through AJAX.

Within that view there is a also a table that shows all related Franchises for that Franchise set. Essentially, it is a Master/Detail View - Here is what that looks like:

enter image description here

There is a Twitter Bootstrap modal being used to display the Create form for franchises. Here's what that looks like:

<div class="modal hide fade in" id="myModal" )>
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal">×</button>
        <h3>Modal header</h3>
      </div>
      <div id="create_franchise_modal_body" class="modal-body">
        @Html.Action("Create", "Franchise", new { FranchiseSetId = Model.FranchiseSetID })       
      </div>
      <div class="modal-footer">
        <a href="#" class="btn" data-dismiss="modal">Close</a>
        <a href="#" class="btn btn-primary">Save changes</a>
      </div>
    </div>

Inside the body of the modal, I'm using an @Html.Action helper to call the FranchiseController's Create method:

@Html.Action("Create", "Franchise", new { FranchiseSetId = Model.FranchiseSetID })

Here is the modal that is generated from that:

enter image description here

The Create method is successfully saving the new Franchise, however, I get this error:

Child actions are not allowed to perform redirect actions.

I've seen LOTS of posts on StackOverflow and the ASP.NET Forums on this issue, and I've tried to implement some of the solutions - even using JavaScript redirects, but I still haven't been able to get this to work.

I understand that since I am already inside of the FranchiseSet Controller, that it is already trying to render a view based on the FranchiseSet model, so the Child view - of the Franchise model - is undefined. So what is the ideal way to deal with interactions among parent and child actions?

This seems to be a very common problem without a common solution. Or at least not one that I can find. I'm relatively new to .net mvc so it may just be me.

Is using @Html.Action a bad design choice in this scenario? I have tried to use @Html.RenderPartial but that causes a 500 Internal Server error - even if I have the Create method setup to return a partial view.

Here are the FranchiseController Create actions - both GET and POST - that I am using:

//
// GET: /Franchise/Create

public ActionResult Create(int FranchiseSetId)
{
    ViewBag.PossibleFranchiseSets = franchisesetRepository.All;
    var franchise = new Franchise { FranchiseSetId = FranchiseSetId };
    return PartialView(franchise);
} 

//
// POST: /Franchise/Create

[HttpPost]
public ActionResult Create(Franchise franchise)
{
    if (ModelState.IsValid) {
        franchiseRepository.InsertOrUpdate(franchise);
        franchiseRepository.Save();
        return RedirectToAction("Index", "FranchiseSet");
    } else {
        ViewBag.PossibleFranchiseSets = franchisesetRepository.All;
        return View();
    }
}

The return RedirectToAction("Index", "FranchiseSet"); in conjunction with the @Html.Action from the view seems to be the source of the problem.

Any advice is much appreciated!


SOLUTION


I used the modified modal code that Russell suggested below, however I switched the @Html.RenderPartial method for the @Html.RenderAction method within a code block.

Here is what it looks like now.

<div class="modal hide fade in" id="myModal" )>
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal">×</button>
        <h3>Modal header</h3>
      </div>
      @using (Html.BeginForm("Create", "Franchise", FormMethod.Post))
      {
         <div id="create_franchise_modal_body" class="modal-body">
           @{ Html.RenderAction("Create", "Franchise", new { FranchiseSetId = Model.FranchiseSetID }); }
         </div>
         <div class="modal-footer">
           <a href="#" class="btn" data-dismiss="modal">Close</a>
           <button type="submit" class="btn btn-primary">Save changes</button>
         </div>
      }
  </div>

I also modified the controller's Create method like Russell suggested above.

Using this: @{ Html.RenderAction("Create", "Franchise", new { FranchiseSetId = Model.FranchiseSetID }); }

I'm putting it within the @{ } tags because of what I learned here: Html.RenderPartial giving me strange overload error?

I was able to get the FranchiseController to redirect back to its parent - the FranchiseSetController's Index method - with no problems. Plus it didn't require me to return JSON and try to do a JavaScript redirect - it's all Razor view code.

I hope this is solves the problem of how to Redirect from Child actions back to the Parent.

like image 533
PhillipKregg Avatar asked Nov 04 '22 21:11

PhillipKregg


1 Answers

Try this for your modal:

<div class="modal hide fade in" id="myModal" )>
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal">×</button>
        <h3>Modal header</h3>
      </div>
      @using (Html.BeginForm("Create", "Franchise", FormMethod.Post))
      {
         <div id="create_franchise_modal_body" class="modal-body">
           @Html.RenderPartial("Create")
         </div>
         <div class="modal-footer">
           <a href="#" class="btn" data-dismiss="modal">Close</a>
           <button type="submit" class="btn btn-primary">Save changes</button>
         </div>
      }
 </div>

And then in your controller, fix your GET method so it returns a partial view result

//
// GET: /Franchise/Create

public PartialViewResult Create(int FranchiseSetId)
{
    ViewBag.PossibleFranchiseSets = franchisesetRepository.All;
    var franchise = new Franchise { FranchiseSetId = FranchiseSetId };
    return PartialView(franchise);
} 

I've made a couple of assumptions in this fix.

  1. Your "Create" view is supposed to be a partial view that has all of your form inputs
  2. When you call @Html.Action("Create", "Franchise", new { FranchiseSetId = Model.FranchiseSetID }) you are wanting to render that partial view
  3. You're wanting the "Save Changes" link at the bottom of the modal to actually save your modal and not the "Create" button that is displayed

If I've missed something let me know and I'll update my answer to try and provide a better solution.

like image 166
Russell Durham Avatar answered Nov 11 '22 14:11

Russell Durham