47

Our site has multiple "wizards" where various data is collected over several pages, and cannot be committed to the database until the last step.

What is the best/correct way to make a wizard like this with ASP.Net MVC

edit: My boss is now saying "no javascript" - any thoughts on how to get around that restriction?

8 Answers8

46

I don't believe there is a best/correct way, but the way I'd do it is...

Each wizard gets its own page. Each step gets its own div. All steps are in the same form.

The previous/next buttons would essentially hide/show the div in each step of the process. The last step's submit button submits the entire form. It would be pretty trivial to implement this using jQuery, and it would be easy to maintain as all the wizard steps are in a single ViewPage.

On the controller side, you'd have two controller methods, the HttpVerbs.Get version that would prepare the form for viewing and the HttpVerbs.Post version that would take a FormsResult and parse it to get out the information required to submit the user's answers to storage/other processes.


Wow, your boss stinks.

This answer almost gracefully works for those ****** who have javascript disabled (yes, both of them). You can tweak it to hide the next-previous buttons via CSS and unhide them in your javascript code. This way people with javascript see the wizard and people without javascript will see the entire form (without next/prev buttons).

The other option is to create a view for each step in the wizard. You can store the intermediate results of the form in the Session. This way would cost plenty of time and effort to implement, which means you could probably squeeze some overtime out of your boss when you demonstrate, in about twenty minutes of effort you spend during lunch, how easy the javascript route is to implement.

  • 10
    Just to add something more,,, you can place each step of the wizard in a Partial view, and do a Render.Partial on each DIV. – Hector Minaya Nov 10 '09 at 19:42
  • 8
    There is a problem with using a single view and javascript to hide/show like you mention - your page is going to get absolutely huge as the wizard grows. Sure, a wizard with 2-3 steps might be okay, but more than that and you are just asking for trouble. – Charles Boyung Mar 09 '10 at 21:19
  • 3
    @charles What number becomes worse than prematurely optimizing? –  Mar 10 '10 at 11:19
  • 1
    @Will There is no set number, and I don't know that what you are implying here is accurate either. "Prematurely optimizing"? You're trying to incite something by saying that. It's not like we're talking about some huge amount of work, just storing this serialized object on the server, instead of passing it back and forth for every single page request. – Charles Boyung Mar 10 '10 at 14:08
  • 4
    @charles You were saying that more than 3 steps is going to cause the page to get "absolutely huge". So, your concern is that the page will grow large, slowing down requests. In other words, before you actually know that you need to you're trying to optimize response times. Its as simple as that, really. Not trying to incite, just responding to your concerns. They are valid, of course, but only if your wizard page is causing an issue. Or were you just trying to "incite something" by saying more than 3 steps = trouble? –  Mar 10 '10 at 14:50
  • 3
    And don't forget, that data, saved in Session can be lost anytime when .NET decides that memory is not enough. – denis_n Apr 10 '11 at 21:34
  • What about wizards where a choice in an early step defines the data to be fetched or filled out in later steps? That scenario will not work using a single page form... – Erik Oosterwaal May 23 '11 at 08:04
  • @RicoSuave: Obviously, that wouldn't fit here. Instead of a 10 page wizard, you have a 1 page form with the postback being turned into a 9 page wizard. –  May 23 '11 at 10:23
  • The trouble with a design based on loading all the HTML at once is that it doesn't scale. If your page gets too big then you won't be optimising, you'll be rebuilding. – Ian Warburton Jun 12 '13 at 18:35
  • Saved Session also has that bad effects on if you moved to a different section of the application and came back to this wizard section, the session parameters that are not cleared may have adverse effects on your app. Session = Trouble. I like hminaya's idea of partial view. That way the view is not too large. I would have a div and then partialViewRender logic and then a sub div. If the outer div is hidden that won't render anything. – Mukus Mar 18 '14 at 00:55
20

If you can't use JavaScript, then make each step a view, with a method in the controller, and keep your data in session until ready to submit to the database.

You can make your Next and Prev buttons using the ActionLink HtmlHelper method.

Matthew
  • 2,062
  • 13
  • 11
  • 1
    I would do this but just use TempData. Yah it is stored in Session too, but no need to manage it since it gets removed for you. – anonymous Nov 18 '08 at 18:58
  • 5
    For a wizard, you want the information across several pages, so you don't want it removed until you submit. Keeping it in session allows you to travel forward, backwards or restart and keep the data that has allready been entered. – Matthew Dec 04 '08 at 03:50
  • What happens if the user suddenly decides to navigate to a different area of the application? The session information should be cleared everytime the user decides to go elsewhere. – Mukus Mar 18 '14 at 00:57
12

Another way is to save the incomplete object that you are building with the wizard to the database and just pass the primary key to the next step of the wizard. I know that this means you need to make some of the database fields nullable, but it does have the added advantage that you can save the primary key in a cookie and allow the user to come back to the wizard at some later time. This option doesn't require javascript or session state.

Ben Mills
  • 27,454
  • 14
  • 42
  • 38
  • 2
    To improve security you should hash the primary key, otherwise a hacker could take someone else's object – jao Jan 30 '10 at 14:47
  • You don't need nulls if each step has its own table in the database. – Ian Warburton Jun 12 '13 at 17:45
  • What if the user choose to cancel, or the wizard is aborted in some other way? Then you will end up with incomplete garbage entries in the database. – awe Sep 13 '13 at 06:40
  • Cancelling can allow the system to clear data. But if the user clicks on a different link in the application, that needs to call the clear method too. So you then want to make the user follow a strict workflow or your application starts to break. – Mukus Mar 18 '14 at 00:59
4

Make the different panels all be client side ... all in the same form ... and when the final submit button is pressed, you can post all of the values at the same time.

Joel Martinez
  • 46,929
  • 26
  • 130
  • 185
3

If you can't use Javascript and don't want to spend server resources with Session variables, you can also serialize and deserialize the values being entered in the different steps, and pass it back and forth using a hidden input field. A bit like ViewState in ASP.NET Webforms.

rodbv
  • 5,214
  • 4
  • 31
  • 31
  • The problem with this solution is the amount of data you end up storing in your hidden input field, which increases the size of both the HTTP request and its respective response. Both of these are going to affect the performance of your site for the user and other users as well (since it will affect your bandwidth). The latter may be negligible, depending on how often the wizard is being accessed, but it could cause a problem. If you are going to serialize the data, you are better to store it in a DB table temporarily or the session. – Charles Boyung Mar 09 '10 at 21:24
  • @charles, if you expect so huge traffic in the wizard, then the session object will overfill much faster than you will get performance issues with serializing/deserializing of the previous steps (or even bandwith issues) Solution above is definitely more scalable and safer(performance-wise). Remember that we are talking about asp.net mvc, so I'm 99% sure that the session is not distributed, or at least on the state server.. Beware of the microoptimalizations on the wrong place.. – jhexp May 20 '10 at 10:38
  • @jhexp, I have no idea how you can say that using a hidden input field is more scalable and safer for performance than using a temporary database table. There is absolutely no way you are going to get better performance passing a huge string back and forth on every request than you would get by pulling it out of the database each time. Oh, and as for session, why do you think that the session is not distributed? That's totally up to the implementer, MVC has no restriction on that. – Charles Boyung May 20 '10 at 15:17
  • @charles You want to tell us that full database query per each step is faster and more scalable than having a hidden field in the form?? And I don't say that MVC restricts implementation of distributed sessions, but its a very very rare case. Many highly loaded apps are stateless on the server anyway. – jhexp May 21 '10 at 11:27
  • @jhexp you obviously have no idea how slow an HTTP request is in comparison to a simple database call. Adding to the size of that request and response is definitely slower than a single database call. And that's with a high-speed connection, don't even get started on dial up (yes, people still DO use dial up). – Charles Boyung May 21 '10 at 13:14
  • You are right, but its a wrong measure.. More important is the resource utilization, not if the browser downloads the response few ms faster. Lets say you will have three steps wizard, each step will add 5kb to the serialized string(which is really enough for a single page form), you transfered back and forward 30kb more in total in all three steps. As opposed to three inserts and one select to the db. – jhexp May 21 '10 at 15:13
  • @jhexp - You've clearly never had to build or optimize a website for performance. The key factor in improving website performance is almost always minimizing the amount of traffic sent back and forth between the browser and the server. Why do you think that Ajax and partial page refreshes have become so popular? And yes, I would say that you're definitely going to have better overall system performance performing the DB inserts/select over the extra transfered KB. – Charles Boyung Jan 04 '11 at 06:03
  • @jhexp - "I don't say that MVC restricts implementation of distributed sessions, but its a very very rare case"????? What exactly is rare about it? It is incredibly simple AND incredibly common to have your session distributed across servers in ANY version of Asp.Net, MVC or not. What is rare about it? And what do you mean that "many highly loaded apps are stateless on the server anyway"? You think that Facebook is stateless? Twitter? Yeah, right. Get a clue. – Charles Boyung Jan 04 '11 at 06:07
  • @charles.. First it would be nice to leave out personal invectives from a discussion about some technical approach. And yes, I have quite a good experience with a very big database applications. – jhexp Jan 07 '11 at 15:16
  • @charles The core of this issue is that it is much easier to scale out web server (no matter how you solve server side session state, whether you will have distributed sessions, or use some form of "sticky sessions" on the load balancer side) than scale out sql databases, which is both very expensive on the licenses and a technical hassle. Thats why it's better to save the sql server queries. – jhexp Jan 07 '11 at 15:25
  • @jhexp - I ever said anything about "very big database applications". I said optimizing websites for performance. Very different things. And you never answered my other question - what exactly is rare about having distributed session in an Asp.Net MVC app? Scaling out the web server vs. the database server is not issue here. The issue is where are performance bottlenecks in a data-driven website. The bottleneck almost always turns out to be the link between client and web server. The only way to minimize that issue is by minimizing size and number of HTTP requests. – Charles Boyung Jan 07 '11 at 19:11
  • I believe this is how some ASP.net Web Forms controls work. They render all the steps HTML to the page and all the step data in view state. Gets really slow because the pages get huge. – Ian Warburton Jun 12 '13 at 17:50
  • Usually bandwidth on the client side is more of an issue than server side memory consumption in session. – awe Sep 13 '13 at 06:47
2

I put together a login wizard and documented the ideas behind it on my blog if that helps: link text

1

You can use the simple component MVCWizard.Wizard available on NuGet. The WizardController allows you to create a wizard using partial view. There is also the AutoWizardController that renders the entire wizard in a single view. All these components operate with the session to store the model state.

Massimo Zerbini
  • 3,125
  • 22
  • 22
1

There is a very simple, flexible and extensible method in this question: How to simplify my statefull interlaced modal dialogs in ASP.NET MVC

Community
  • 1
  • 1