Snippet support

Completed

This unit describes the snippet and code that you can use to create interfaces in AL. Additionally, the following sections describe the tinterface snippet and will use it to build an example of creating and using an interface in AL.

Tinterface snippet

Typing the shortcut tinterface will create the basic layout for an interface object when you are using the AL Language extension in Visual Studio Code.

Screenshot of the interface object with the snippet interface in AL Language.

Interface example

The following example defines an IAddressProvider interface, which has one getAddress method with a certain signature.


interface IAddressProvider
{
    procedure GetAddress(): Text;
}

The CompanyAddressProvider and PrivateAddressProvider codeunits implement the IAddressProvider interface, and each defines a different implementation of the getAddress method, which in this case is a simple variation of address value.


codeunit 65100 CompanyAddressProvider implements IAddressProvider
{
    procedure GetAddress(): Text;
    begin
        exit('Company address \ Denmark 2800')
    end;
}


codeunit 65101 PrivateAddressProvider implements IAddressProvider
{
    procedure GetAddress(): Text;
    begin
        exit('My Home address \ Denmark 2800')
    end;
}

MyAddressPage is a simple page with an action that captures the choice of address and calls, based on that choice, an implementation of the IAddressProvider interface.


page 65100 MyAddress
{
    PageType = Card;
    ApplicationArea = All;
    UsageCategory = Administration;

    layout
    {
        area(Content)
        {
            group(GroupName)
            {
            }
        }
    }

    actions
    {
        area(Processing)
        {
            action(WhatsTheAddress)
            {
                ApplicationArea = All;
                Caption = 'What's the Address?';
                ToolTip = 'Select the address.';
                Image = Addresses;

                trigger OnAction()
                var
                    iAddressProvider: Interface IAddressProvider;
                begin
                    AddressproviderFactory(iAddressProvider);
                    Message(iAddressProvider.GetAddress());
                end;
            }

            action(SendToHome)
            {
                ApplicationArea = All;
                Image = Home;
                Caption = 'Send to Home.';
                ToolTip = 'Set the interface implementation to Home.';
                trigger OnAction()
                begin
                    sendTo := sendTo::Private
                end;
            }

            action(SendToWork)
            {
                Image = WorkCenter;
                Caption = 'Send to Work.';
                ToolTip = 'Set the interface implementation to Work.';
                ApplicationArea = All;

                trigger OnAction()
                begin
                    sendTo := sendTo::Company
                end;
            }
        }
    }

    local procedure AddressproviderFactory(var iAddressProvider: Interface IAddressProvider)
    var
        CompanyAddressProvider: Codeunit CompanyAddressProvider;
        PrivateAddressProvider: Codeunit PrivateAddressProvider;
    begin

        if sendTo = sendTo::Company then
            iAddressProvider := CompanyAddressProvider;

        if sendTo = sendTo::Private then
            iAddressProvider := PrivateAddressProvider;

    end;

    var
        sendTo: enum SendTo;
}

Extensible enums also support implementing an interface. You can assign the enum value to an interface variable that can initialize the interface. Then, you can use the enum to choose the specific implementation to use for a given interface. The SendTo enum currently holds two values: Company and Private.


enum 65100 SendTo
{
    Extensible = true;

    value(0; Company)
    {
    }

    value(1; Private)
    {
    }
}

Obsolete support

Interfaces can be obsoleted like other AL object types by using the ObsoleteState, ObsoleteReason, and ObsoleteTag properties and the Obsolete attribute. This approach can help with the process of managing interface change.

Handle removed enum extension values

You can use enums to select an interface implementation. However, because of the pluggable nature, an enum extension and its corresponding interface implementation could have been uninstalled from the tenant, while the setting for the enum is kept, which will now point to an unknown value. Instead of having an app code add the validation logic for handling this scenario, the platform should catch when a non-valid enum value is provided and allow for an extensible way to handle that issue. This scenario is related to, but not the same as, fallback to a default value that is used when no enum value is provided.

The UnknownValueImplementation property specifies the implementers that are used for ordinal values that aren't included in the defined list of enum values.


UnknownValueImplementation = Interface = InterfaceImplementation;

The following example illustrates different implementations of the IFoo interface. SomeEnum has UnknownValueImplementation set to catch the case where some extension uses an unknown enum value.


pageextension 50130 CustListExt extends "Customer List"
{

    trigger OnOpenPage()
    var
        ifoo: Interface IFoo;
        e: enum SomeEnum;
    begin
        e := SomeEnum::Yes;
        ifoo := e;
        ifoo.Foo(); // => YesFooImpl specified in Implementation on Yes value

        e := SomeEnum::No;
        ifoo := e;
        ifoo.Foo(); // => DefaultFooImpl specified in DefaultImplementation

        e := 2; // No enum value matches this.
        ifoo := e;
        ifoo.Foo(); // => UnknownFooImpl specified in UnknownImplementation
    end;
}

interface IFoo
{
    procedure Foo();
}

codeunit 50145 ErrorFooImpl implements IFoo
{
    procedure Foo()
    begin
        Message('Bad FOO')
    end;
}

codeunit 50146 DefaultFooImpl implements IFoo
{
    procedure Foo()
    begin
        Message('Default FOO')
    end;
}

codeunit 50147 YesFooImpl implements IFoo
{
    procedure Foo()
    begin
        Message('Yes FOO')
    end;
}

enum 50135 SomeEnum implements IFoo
{
    Extensible = true;
    UnknownValueImplementation = IFoo = ErrorFooImpl;
    DefaultImplementation = IFoo = DefaultFooImpl;

    value(0; Yes)
    {
        Implementation = IFoo = YesFooImpl;
    }
    value(1; No)
    {
        // Nothing specified. Using defaults
    }
}

The UnknownValueImplementation property applies to enums. Uninstalling enum extensions can result in persisted values becoming unknown. The UnknownValueImplementation provides a generic error handling in such cases. Enums are often used to select an interface implementation. However, because of the nature of an extensible development model, an enum extension and its corresponding interface implementation can be uninstalled from a tenant, while the value for the specific enum is still available, which will now point to an unknown value. Using the UnknownValueImplementation property when defining an enum prevents the throwing of a technical error message in the UI and allows for a more user-friendly error handling.

Related to UnknownValueImplementation is the DefaultImplementation property, which is used for fallback to a default value when no enum value is provided.