tags:

views:

383

answers:

4

How do you pass parameters in ASP.net MVC over two views, like a wizard?

Or what is the best practice?

I am working on an inhouse bug tracking ASP.net application being moved to MVC, I can't seem to shake the ASPX out of my head and am going in circles.

In the ASPX application.

Page1 -> Select Project, pass projectId in Querystring
Page2 -> Select IssueType, pass projectId and Issuetype in Querystring
Page3 -> Create New Issue, we can get the projectId and IssueType form querystring

How do we recreate the above flow in MVC?

+2  A: 

A common practice for 'wizard' type systems is to use a single <form> element, with information from previous screens in hidden fields.

Edit: per the request, here's an example (I'm not familiar with ASP.net, so you'll have to translate this into code yourself :( ). Say you have a site that sells cars, and in your ordering form first you ask about the model car, and in subsequent screens you offer options that apply to that vehicle.

<form action="order-wizard.html" method="POST">
    <!-- which step of the wizard are we on? -->
    <input type="hidden" name="step" value="1" />
    <!-- Get some info from the user for the first step of the wizard -->
    <select name="Model">
        <option value="Sedan">Sedan</option>
        <option value="Coup">Coup</option>
        <option value="Pickup">Pickup</option>
        <option value="Van">Van</option>
    </select>
    <input type="submit" />
</form>

When the user selects a model and submits the form, the controller for order-wizard.html can notice that the step option is 1, and knows to check that the user selected a model. Then it gan generate a page like this: (assuming the user selected "pickup")

<form action="order-wizard.html" method="POST">
    <!-- which step of the wizard are we on? -->
    <input type="hidden" name="step" value="2" />
    <!-- Stored results from the previous stage of the wizard -->
    <input type="hidden" name="Model" value="Pickup" />
    <!-- Additional information for the wizard.  More than one option can be
         requested for each stage of the wizard. -->
    <select name="Style">
        <option value="Short Bed">Short Bed</option>
        <option value="Long Bed">Long Bed</option>
        <option value="Extended Cabin">Extended Cabin</option>
        <option value="Dually">Dually</option>
    </select>
    <select name="Interior">
        <option value="Cloth">Cloth</option>
        <option value="Leather"> Leather </option>
    </select>
    <input type="submit" />
</form>

If the wizard needed to collect more information, There could be additional pages produced in this way with hidden inputs. Otherwise, If this was the last page and all information was gathered, the code for that page could then process the form as normal, as though all inputs had been given by the user on a single page request.

The advantage of this technique is that the server does not need to cache any session information. Another advantage is that it provides a sort of RESTful interface, where a single page request from another tool can generate all of the inputs for the form, skipping over the intermediate wizard pages.

A disadvantage is that since the page is generated dynamically based on user requests, the client will have to start over if they happen to navigate away from the page. Bookmarks just won't work. It doesn't save you anything in validation, either, since form inputs of this sort could be faked easily.

TokenMacGuy
Can you please refer to an example on how to accomplish this with single <form> element.
Picflight
+3  A: 

I agree with TokenMacGuy but sometimes that may not viable.

In that case you are going to need to create custom classes in say your controller that your view can bind to. Then as you submit the page you can use UpdateModel to get the values and redirect to another action passing in either the model again to fill in more details, or a new model for that view.

Let's assume you have a two page wizard. Both Views inherit from <MyApp.Controllers.Wizard>.

In your controller you have a class Wizard which may look like this;

public class Wizard
{
  string FirstName {get;set;}
  string LastName {get;set;}
  string eMail {get;set;}
}

eMail is captured in step 2 of your wizard.

At step 1 you have two fields names FirstName and LastName and on the postback you simply use UpdateModel(Wizard object) to get the values in.

This model does have an issue now in that you'll need to pass the whole model to View 2 including the data that's not required from view 1. On the post back of View 2 you then do UpdateModel(Wizard object) again to get the 1st 2 fields and then the email.

FirstName and LastName in the 2nd view might be in hidden fields.

That's 1 way and really not the best now that I see it on screen. You could save each step out to a state service so you don't pass back all the data each time which would make it cleaner.

JamesShannon's idea is also great. Wizards are so outdated I think unless you are in a buy online scenario.

What you can do is use JavaScript to hide and show elements of the wizard so that all entry fields are on the same page. This can be quite effective if you have a scrolling animation to open the next section as you finish the previous section. <= hope that makes sense.

  <script src="/Scripts/jquery-1.3.2.js"></script>
  <script>
      $(document).ready(function() {

          $("#addComment").click(function() {
          if ($("#divStep2").is(':visible')) {
              $("#divStep2").slideUp(300);
          } else {
          $("#divStep2").slideDown(300);
              }
          });

      });
  </script>

The code above will toggle animate a section open and closed as you click on it. This does require JS to be active on the client machine of course so you may want all the sections to be open by default and then use JS to close them on page entry.

if you do that then if the user does have JS they'll see nice animations and if not then they will at least still see all sections and life will be good.

JS would be the best approach me thinks as you don't need to pass irrelevant data to each view and you don't need to save data out to some state service which you might need to clean up if people simply click away from your site 1/2 way through.

Hope this helps.

griegs
Yes please, sample would be very very helpful.
Picflight
For wizard-specific scenarios, there is a very cool JQuery wizard plugin available here:http://blog.jerodsanto.net/2008/08/jquery-wizard-redux/Demo is here:http://jerodsanto.net/src/wizard/demo/
Robert Harvey
That's really cool @robert. We're about to enbark on a wizard and this may help quite a bit. Thanks.
griegs
A: 

Personally, I prefer griegs' solution. I've done this myself and it's much cleaner. Plus, when the requirements change and there's a request that you can move back and forth between the pages, or save your status and return later, then the model is the only way to go.

However... if your example isn't contrived, and your wizard really is as simple as you describe, then I would suggest:

a) Rethink your 3 page wizard idea. Four years ago this would have been the way to go... but now we have AJAX. Combine it onto one page.

b) Failing that... pass the 2 variables through a form (i'd prefer querystrings here).

James

James S
How do you pass the parameters in querystring in MVC? Thanks for making the other suggestions on rethinking the wizard approach.
Picflight
AJAX is great where it works, but It's a pretty good idea to make sure your page still works for users that don't have JavaScript, either because it is disabled or because their browser (like a mobile browser) doesn't support js.
TokenMacGuy
You pass the parameters as part of the form. Querystring vs post variables is just a matter of a different form method (get or post, respectively).So the form on page 2 would look like: <form action="page3" method="get"> <input type="hidden" name="page1_value" value="x"> </form>
James S
A: 

For wizard-specific scenarios, there is a very cool JQuery wizard plugin available here:

http://blog.jerodsanto.net/2008/08/jquery-wizard-redux/

Demo is here:

http://jerodsanto.net/src/wizard/demo/

Robert Harvey