Enabling buttons on the fly, and showing/hiding containers

Enabling buttons on the fly, and showing/hiding containers

  

Hello everyone.  I have both a "how do you..." question and a best practices question.

I am working on a screen that uses a "Progressive Disclosure" pattern - like a wizard, but it reveals each step on the same screen.  For example, you could start with:

As soon as you make a choice and complete Step 1, it hides the Form part and takes you on to step 2:

Question 1: For the first step, I want to have the Complete Step 1 button disabled until you choose either then A or the B option.  I have set the button's Enabled property to an expression that checks for the value of the radio buttons' variable.  If it's empty, the button should be disabled.  As soon as it has a value from either of the radio buttons, it is not currently becoming enabled.

Based on this post, it looks like I could attach some JavaScript code to the onclick or onchange event for the radio buttons using their extended properties.  I can't figure out how to get the button's ID, however.  I'm also not clear on where the RunJavaScript action is so that I can try that.  Am I on the right track with this, or is there a simpler way to enable the button?


Question 2: What is the best way to show and hide the containers and elements in the form?  In the example above, I have an entity called Process, and it has attributes called isStep1Complete, isStep2Complete, isStep3Complete, and TheQuestion.  Clicking a Complete... button passes in the step number, and it updates the corresponding attribute, then refreshes the form.  The containers are in If blocks to be shown or hidden based on the data in the entity.  Is this the right way to show and hide things, or is there a more elegant solution.


Question 2.B: I created three attributes in the entity to store the current progress through the steps.  I'm coming from a JavaScript background, and my first thought was to have one attribute with three keys inside of it (like a JS object).  Could I have used a single attribute, or is it better to create one for each step?  Or, should I use another approach entirely?

Many thanks!

Solution

Hello Scott. Answers to your questions:

Question 1: Your solution of refreshing the button is fine, and if it works I wouldn't change it. Granted, it requires a server roundtrip just to disable the button, but it's such a small request that I wouldn't bother with it.

If you do want to change it to a client-side solution, then you are missing the following pieces of the puzzle. The RunJavascript action is in an extension called HttpRequestHandler, so you will need to add it as a dependency. You can refer to the button id by using {ButtonName}.Id in an expression, where {ButtonName} is the button's name. Buttons are not named by default, so you will need to add a name to it before you can use its id. Also note that the id is generated, and that's why you shouldn't hardcode the id on your javascript, and instead use that dynamic expression to get the id at runtime.

Question 2: Again, your solution of putting each step in an If widget is fine.

I would like to mention that there is a wrong way of hiding/showing things, which is relying on the Display=False property of a container. The Display property only hides the container by using CSS (display: none), which can be useful when you want to use javascript to manipulate the container, but is the incorrect solution when you really want to hide information from your client.

Question 2.B: Entities represent database tables, and in relational databases a column cannot have structure. So you cannot have an attribute with three fields inside of it. I think you are doing it right.

Just to give an alternative solution, you could model the progress with a structure. Structures are not stored on the database, so they can have an attribute whose type is another structure. You could have a structure called WizardProgress, with a field called CompletedSteps, and this field would be another structure with three boolean fields. Or the CompletedSteps could be a record list.

If you don't need to store the progress on the database, then you can have a local variable on the screen of type WizardProgress, and you just need to read and write from that variable. I think this would be a simpler solution, but the progress will not be stored in the database, so I'm not sure if it would meet all your requirements.

Solution

Thank you very much Leonardo.  I was able to get the RunJavaScript action working, so I think that's my preferred method.  I did notice that a by-product of the fact that we enter the JS as a string means that it's not so easy to inspect and set breakpoints in the Chrome dev tools.  For small interactions like this that shouldn't be a problem.

As for 2.B, we do want to save progress through the wizard/disclosure page.  Ideally, I think it may make sense to inspect the data for a record after it's retrieved and then create/populate a local variable to manage the actual showing and hiding of sections based on what data we have so far.

Thanks again!