Upgrade Your StrMenus to Confirmation Pages

Exhibit A: StrMenu when posting a sales document

Prompting the User

Sometimes we want to ask users to make a decision before executing some business logic. Often we just want a yes/no answer and can use a simple Confirm.

But what if you can’t phrase the question to give a yes/no answer? Or if there are more than two options for the user to select between?

Traditionally we’ve used the built-in StrMenu command for that. Think of the menu that you’re given when posting a sales, purchase or warehouse document.

StrMenu is nice and easy to use. Just provide a comma-separated string of the options that you want the user to select between optionally with a default and some instructions and get an integer value back. 0 indicates that the user clicked the Cancel button, otherwise the return value corresponds to the options in the string.

Selection := StrMenu(ShipInvoiceQst, DefaultOption);
Ship := Selection in [1, 3];
Invoice := Selection in [2, 3];
if Selection = 0 then
    exit(false);

That’s cool, it works nicely. Let’s not over-complicate things unnecessarily, but you might want to try out an alternative in some situations.

Shortcomings

What’s not to love? Give the user some options, get their selection back. Well…

UI

You’ve got no control over the prompt that the user is given. That might be a good thing if you don’t care what the prompt looks like, or might be a problem if you’ve got specific requirements.

Extensibility

It’s difficult to allow someone else to extend. You could have an event before and after the StrMenu I suppose – allow someone to change the string and read the return value. If you’ve got more than one subscriber this is going to get messy pretty quickly though.

Maintenance

Comma-separated values – you should put the values in a label so that it can be translated, but what if you want to use those values more than once? Maintain them and translate them in more than one place? A set of possible values is a first class citizen in AL now that we’ve got enums.

Confirmation Pages

An alternative to StrMenu is to use a confirmation page to display the values defined by an enum. Have that enum implement an interface and you’ve got a better recipe for maintaining your code and allowing others to extend it.

Example

Imagine that you are creating something to allow users to interact with phone numbers. We’ll have some assist-edit or drilldown code on a phone number field and allow the user to take some action.

Initially we decide that the user will be able to either:

  • Create a new contact and add the phone no. to it
  • Add the phone no. to an existing contact

Phone No. Action Enum

First, create an enum that defines the available options.

enum 50100 "Phone No. Action"
{
    Extensible = true;

    value(0; "Create a new contact")
    {
        Caption = 'Create a new contact';
    }
    value(1; "Add to existing contact")
    {
        Caption = 'Add to existing contact';
    }
}

I’ve left Extensible = true because I want to allow other devs to add new phone no. capabilities later.

ConfirmationDialog Page

Now a page to present the options in the enum to the user. Because it’s a page we’ve more control over what is displayed and how it works. Add instructional text, add the phone no. for information etc. The PageType is ConfirmationDialog. When you add an enum control to the page it will be rendered as radio buttons rather than the usual dropdown list.

page 50100 "Select Phone No. Action"
{
    PageType = ConfirmationDialog;
    ApplicationArea = All;
    UsageCategory = Administration;
    Caption = 'Select Phone No. Action';

    layout
    {
        area(Content)
        {
            group(General)
            {
                ShowCaption = false;
                InstructionalText = 'What would like to do?';
                field(PhoneNoCtrl; PhoneNo)
                {
                    ApplicationArea = All;
                    Editable = false;
                    Caption = 'Phone No.';
                }
                field(PhoneNoAction; PhoneNoAction)
                {
                    ApplicationArea = All;
                    ShowCaption = false;
                }
            }
        }
    }

    var
        PhoneNoAction: Enum "Phone No. Action";
        PhoneNo: Text;

    internal procedure Intialize(PhoneNo2: Text)
    begin
        PhoneNo := PhoneNo2;
    end;

    internal procedure GetResult(): Enum "Phone No. Action";
    begin
        exit(PhoneNoAction);
    end;
}
Sample ConfirmationDialog page

Which gives a page that looks like this. OK, looking good. I’ve also created a codeunit which initialises and calls the page and then retrieves the result.

Having fetched the selected enum value we can call code to create a new contact, have the user select an existing contact or whatever we want.

That’s great, but it could still be better. If another dev adds an enum value it will be displayed on the page correctly but how will their code get called? In a case statement? Should they subscribe to an event? No.

Phone No. Action Interface

We should define an interface that we expect all Phone No. Actions to implement. For now we probably only need one method. I’ll just call it Handle. You could imagine this being more complex in a real example depending on the type of number (Mobile, Landline), country code or some other factors.

interface "Phone No. Action"
{
    procedure Handle(PhoneNo: Text);
}

Now we make the Phone No. Action enum implement that interface. I’ve created a new codeunit for each action. Those codeunits will also implement the interface and have the guts of the business logic for each action.

enum 50100 "Phone No. Action" implements "Phone No. Action"
{
    Extensible = true;

    value(0; "Create a new contact")
    {
        Caption = 'Create a new contact';
        Implementation = "Phone No. Action" = "Create New Contact";
    }
    value(1; "Add to existing contact")
    {
        Caption = 'Add to existing contact';
        Implementation = "Phone No. Action" = "Add to Exist Contact";
    }
}

Calling the Confirmation Page and Interface

Finally, this is some sample code that calls the confirmation page to have the user make a selection and then handle their response. Assign the selected action to the interface variable and call its Handle method.

procedure Select(PhoneNo: Text)
var
    SelectAction: Page "Select Phone No. Action";
    PhoneNoAction: Interface "Phone No. Action";
begin
    SelectAction.Intialize(PhoneNo);
    if SelectAction.RunModal() = Action::OK then begin
        PhoneNoAction := SelectAction.GetResult();
        PhoneNoAction.Handle(PhoneNo);
    end;
end;

It should be pretty easy to extend this in the future. Let’s say that you want to add an option to call the number or send a message. Just

  1. Extend the enum with the new action
  2. Create a new codeunit which implements the interface and makes the phone call, sends the message or does whatever you need
  3. Link your new enum value with the new codeunit
  4. (there is no step 4, just three easy steps)

Conclusion

Obviously, this is a lot more work than just using StrMenu, especially if some of these concepts are new to you. I’m not going to tell you that you ought to do this or that it is necessarily always worth the extra effort – but it’s good to have the option.

Maybe you’d start with a StrMenu and later refactor it into a confirmation dialog when you know that you’ve got a case for it. Go wild.

6 thoughts on “Upgrade Your StrMenus to Confirmation Pages

  1. Hi James,

    Thanks, for sharing with us your thoughts. Do you maybe know are we able to dynamically control appearance of options in StrMenu or Enum’s on some page. For example I want just to show first and third option, and to control that on some setup page.

    Like

    1. Hi,

      There is a ValuesAllowed property which can be set on the enum control. You can set the list of enums values that you want the user to be able to select between and other values will not be shown. I don’t think there is any way to control this dynamically though. I’ve got a feeling that I’ve seen this discussed on the dynamicsnavdev Yammer group and there may already be an idea on aka.ms/bcideas to allow us to set this dynamically.

      In the mean time you might be able to have the enum control in different groups with different display options and set the visibility of those groups dynamically instead. It’s a tedious workaround, but it might do what you need.

      Like

  2. Please can you share your code for this. It is not clear for me what the codeunit looks like – even if it is just pseudo code.

    Like

Leave a comment