Advanced customization options for the Loginapp REST API layout

  1. To overcome the limitations of only 3 customizable default HTML files for header, content, and footer layout, you have the following options:
  2. Setting up separate non-default HTML files by referencing a set of custom HTML files in the JavaScript file. In addition to the standard default HTML customization, this option allows you to store multiple layouts, for example.
  3. Adding inline HTML in JavaScript. This option also allows multiple layouts but can be more complex and less flexible to manage, compared to a set of separate plain HTML files.

Both customization approaches require the modification of the JavaScript file but i.e. provide methods to adapt the layout to page events.

Modification of the JavaScript file

The JavaScript file is placed here:

copy
$CUSTOM_DIRECTORY/src/assets/custom/js/iam-custom.js

The basic structure when using a non-default HTML layout is as follows:

copy
iam.api.customizing.initialize = function () { 
  
    // Define a layout 
    class MyCustomLayout extends iam.api.types.CustomLayout { 
        // ... 
    } 
  
    // Register the layout: 
    iam.api.customizing.registerLayout(new MyCustomLayout()); 
};

The layout has to be defined by extending from:

copy
iam.api.types.CustomLayout

The defined layout has to be registered subsequently, using:

copy
iam.api.customizing.registerLayout

As a reminder to this section, the surrounding function iam.api.customizing.initialize is not shown in all code examples. However, all JavaScript customizations must happen within this function.

Selecting applicable layouts with JavaScript

In order for a layout to be applicable to a specific page, its isApplicable method must be evaluated as true when the page is loaded:

class MyCustomLayout extends iam.api.types.CustomLayout { 
  
    isApplicable({pageId, flowId, stepId, uiTenantId}) { 
        return true; 
    } 
}

By default, the isApplicable method is inherited from CustomLayout and always returns true. This means that the layout will always be applied, greatly simplifying the case when only one layout is defined.
Otherwise, isApplicable has to be modified to select the appropriate layout based on the following parameters:

  • pageId – a unique ID for each page of the UI.
  • flowId – the flow currently in progress (can be undefined for non-flow pages).
  • stepId – the flow currently in progress (can be undefined for non-flow pages and flow pages without an explicit flow ID).
  • uiTenantId - provides the identity of the client in OAuth, OIDC and SAML use cases. Is empty in all other cases.

Note that the Loginapp REST UI SDK allows more than just one layout to be applicable, all of which will be applied to the page being displayed. When no layout is applicable, the default layout as provided by the product is used.

Extending from iam.api.types.CustomLayout

To customize header, footer, and content frame, implement the following methods:

class MyCustomLayout extends iam.api.types.CustomLayout { 
      
    headerHtml() { 
        return ...; 
    } 
      
    contentHtml() { 
        return ...; 
    } 
      
    footerHtml() { 
        return ...; 
    } 
}

When inheriting from CustomLayout, no HTML is defined for header, footer, and content. Each method must therefore be implemented when specific HTML is required.

In contrast to header and footer, the contentHTML() method needs to define where the actual content of the Loginapp REST UI should be rendered to. To do so, it is required to mark an HTML element using HTML ID iamContentTargetContainer.

Example 1– contentHtml in separate file:

contentHtml() { 
    return this.fetchHtml('assets/custom/templates/my-custom-content.html'); 
}

The content of the separate HTML would follow :

<div class="iam-container iam-content-container"> 
                <div class="iam-row"> 
                    <div id="iamContentTargetContainer" class="iam-col-12"></div> 
                </div> 
            </div>
  • When using separate (non-inline) HTML:
  • For optimal performance, the SDK will then inline the file's content at build time. In order for this to work, the path of the file to be loaded has to be a plain string all in one line. Using variables or otherwise concatenating the file path will not work.
  • In production mode, the build fails if a referenced file does not exist.
  • In development mode, file deletions or renames are not properly detected and the UI may still show content that does no longer exists. A restart of the development mode helps in this case.

Example 2 – contentHtml as inline code:

contentHtml() { 
    return `<div class="iam-container iam-content-container"> 
                <div class="iam-row"> 
                    <div id="iamContentTargetContainer" class="iam-col-12"></div> 
                </div> 
            </div>`; 
}

Content HTML that lacks the marking HTML ID will lead to a broken and unpredictable layout. On the other side, any content within the marked element will be cleared.

Full layout definition example for JavaScript with inline HTML

The following example for inline HTML code illustrates how to define a layout that applies to all pages of the Loginapp REST UI. It uses the Airlock IAM standard Loginapp HTML that is provided by default.

The example code and can easily be adapted for separately stored/managed HTML files, which would be the preferred method in most cases.

Show moreShow lesscopy
iam.api.customizing.initialize = function () { 
    class MyCustomLayout extends iam.api.types.CustomLayout { 
  
        isApplicable({pageId, flowId, stepId, uiTenantId}) { 
            return true; 
        } 
  
        headerHtml() { 
            return `<div class="iam-container iam-header-container" role="header"> 
                        <div class="iam-row"> 
                            <div class="iam-col-12"> 
                                <div id="iamLanguageSwitcher"> 
                                    <header class="iam-header" id="header"> 
                                        <div class="iam-language-switcher"> 
                                            {{#availableLanguages}} 
                                                <a 
                                                    id="switchLanguageTo-{{languageKey}}" 
                                                    class="iam-language-switcher-link {{#languageActive}}iam-active{{/languageActive}}" 
                                                    data-lang="{{languageKey}}" 
                                                    href="javascript:void(0)"> 
                                                    {{languageText}} 
                                                </a> 
                                            {{/availableLanguages}} 
                                        </div> 
                                    </header> 
                                </div> 
                            </div> 
                        </div> 
                    </div>`; 
        } 
  
        contentHtml() { 
            return `<div class="iam-container iam-content-container"> 
                        <div class="iam-row"> 
                            <div id="iamContentTargetContainer" class="iam-col-12"></div> 
                        </div> 
                    </div>`; 
        } 
  
        footerHtml() { 
            return `<div class="iam-container iam-footer-container" role="footer"> 
                        <div class="iam-row"> 
                            <div class="iam-col-12"> 
                                <footer class="iam-footer" id="footer"> 
                                    <div class="iam-logo"></div> 
                                </footer> 
                            </div> 
                        </div> 
                    </div>`; 
        } 
    } 
  
    // Register the layout: 
    iam.api.customizing.registerLayout(new MyCustomLayout()); 
}