1

I currently have a form with a submit and cancel button. Depending on some logic, each time the page loads, I want that same cancel button to redirect to different other pages in the application. This is the code I have at the moment in my aspx view that changes the location.href based on my property

   <% if (Model.MyProperty.Equals("something"))
      { %>
       <input class="btnCancel" type="button" value="" onclick="location.href='<%: Url.Action("MyAction","MyController", new {Area="MyArea"},null)%>'" />
   <% } %>
   <% else if (Model.MyProperty.Equals("somethingelse"))
      { %>
       <input class="btnCancel" type="button" value="" onclick="location.href='<%: Url.Action("MyOtherAction","MyOtherController", new {Area="SomeOtherArea"},null)%>'" />
   <% } %>

Is this the correct and elegant way to do this? I would rather reduce the multiple IF-ELSE conditions if there was a way to do it.

Thanks for your time.

user20358
  • 14,182
  • 36
  • 114
  • 186

3 Answers3

5

The way I've always handled multiple redirect options is by setting the href value in the controller action.

The View is generic, but the controller action is specific to the context of the page your rendering. So in your model, make a property called CancelUrl. Now, in the controller action, set it to the link you want it to go to.

model.CancelUrl = Url.Action("action", "controller");

This way, all you have to do in your View is say

<a href="@Model.CancelUrl">Text</a>
mccow002
  • 6,754
  • 3
  • 26
  • 36
  • 1
    setting a url as a string messes up everything when you deploy the application, because now you don't have the virtual directory or it is not the same as your developer machine. I always thought using Url.Action is the better way. Correct me if I am wrong... – user20358 May 02 '12 at 15:48
  • I meant use the Url.Action method in the controller. This solves the issue you're referring to. I've updated my answer. – mccow002 May 02 '12 at 15:53
  • Thanks, but the return url wont be set in my controller as I don't have any logic there. To set it in my business project I will need to instantiate the UrlHelper class for which I will need to reference System.Web.Mvc. A {System.Web.Mvc} reference in the business project? And then after making that reference I also need to send in the RequestContext to it. not clean IMO. any other way you had in mind? – user20358 May 02 '12 at 16:15
  • I am actually thinking of having 3 string properties now. One each for Action, Controller, Area... and using that in my aspx view. – user20358 May 02 '12 at 16:17
  • 1
    Your building your model, meant for the presentation layer, in the business layer? I think you might want to revisit your architecture. – mccow002 May 02 '12 at 16:46
  • The ViewModel is just a class containing properties. These properties will decide what is displayed on the screen and will also post the values from the screen back to the business layer via same or different properties. The business layer has VM assemblers, which based on business logic decides what needs to be shown on screen, where the flow needs to go next after a page load. I didn't think it was right to have those decisions made in the presentation layer, since the presentation layer as well as the controller should be light. Atleast that is what I have read in some articles. – user20358 May 03 '12 at 10:56
  • I've always been of the opinion that the ViewModel gets built in the controller. Your absolutely right when you say that the business decisions should not be made in the controller. However, I've always felt that the controller is responsible for getting the data needed for the ViewModel from the business layer, and then sending the data from a postback ViewModel to the business layer. This way when you need to set presentation specific logic, such as a cancel url, you can do it in the presentation layer. – mccow002 May 03 '12 at 16:36
  • Most online examples introducing MVC do the same thing. I however, have kept my controllers very thin. All they do is instantiate and call the business method required to complete an action [HydrateView/DoInsert/DoUpdate] All values I receive from the UI are converted in the controller to a data packet class. This class is part of the business layer. The business class used by my controller receives this data packet class as a constructor or method parameter and the business method being called returns a View and corresponding ViewModel. That is more or less the architecture in a nut shell. – user20358 May 04 '12 at 07:47
  • With this I am now planning the view model to have three more properties that will be used to set the Url.Action parameters in my input tag. I would have loved to use the approach you suggested if I could find a way to get Url.Action to work in my business layer. Although I do feel having the System.Web reference in the business layer is not right.. I could be wrong though. Comments? – user20358 May 04 '12 at 07:54
1

You can create a cancel method that takes your property as a parameter and redirect appropriately within the controller. This logic should probably not be in your view anyway as views should have almost 0 logic anyway

Justin Pihony
  • 66,056
  • 18
  • 147
  • 180
0

I would put the property that will be used to decide the cancel action in the view model (as you already have), alongside any other required properties.

For example:

public class IndexModel
{
    //any other properties you need to define here
    public string MyProperty { get; set; }
}

Then your view would look similar to:

@model IndexModel

@using (Html.BeginForm())
{
    //other information you may want to submit would go here and in the model.

    @Html.HiddenFor(m => m.MyProperty)
    <button type="submit" name="submit" value="submit">submit</button>
    <button type="submit" name="cancel" value="cancel">cancel</button>
}

And finally, your post action should decide the next action that should be returned:

[HttpPost]
public ActionResult Index(IndexModel model)
{
    if (!string.IsNullOrEmpty(Request["submit"]))
    {
        if (ModelState.IsValid)
        {
            //any processing of the model here
            return RedirectToAction("TheNextAction");
        }
        return View();
    }

    if (model.MyProperty.Equals("something"))
    {
        return RedirectToAction("MyAction", "MyController", new { area = "MyArea" });
    }
    else //assumes the only other option is "somethingelse"
    {
        return RedirectToAction("MyOtherAction", "MyOtherController", new { area = "SomeOtherArea" });
    }
}
Dangerous
  • 4,818
  • 3
  • 33
  • 48