Multiple Swagger UI components on one page

Viewed 60

I have a requirement for loading multiple Swagger-UI components on one page on AEM6.5.

My component template in AEM6.5 looks like this:

<sly data-sly-use.model="xxx.aem.component.content.SwaggerModel"
     data-sly-use.template="core/wcm/components/commons/v1/templates.html"
     data-sly-use.clientlib="/libs/granite/sightly/templates/clientlib.html">
</sly>

<sly data-sly-call="${clientlib.all @ categories='xxx.ui.swagger-ui'}"/>

<sly data-sly-call="${template.placeholder @ isEmpty=!model.defined}"></sly>

<div data-sly-test="${model.defined}">
    <h1 class="t-title">
        ${model.title}
    </h1>
    <div id="${model.uniqueId}"></div>
    <sly data-sly-test="${model.defined}" data-sly-call="${swaggerScript @ path=model.swaggerYamlPath, name=model.swaggerYamlName, dom_id=model.uniqueId}"></sly>

</div>
<template data-sly-template.swaggerScript="${ @ path, name, dom_id}">
    <script>
        window.onload = function () {

            window.ui = SwaggerUIBundle({
                urls: [
                    {url: "${path @ context='text'}", name: "${name @ context='text'}"},
                ],
                dom_id: '#${dom_id @ context='text'}',
                deepLinking: true,
                filter: true,
                presets: [
                    SwaggerUIBundle.presets.apis,
                    SwaggerUIStandalonePreset
                ],
                plugins: [
                    SwaggerUIBundle.plugins.DownloadUrl
                ],
                layout: 'StandaloneLayout'
            });

        };
    </script>
</template>

The problem is that no matter I use the unique ID only one swagger component will render on the page and the other will not. I can see the swagger components are correctly loaded in the DOM but as already mentioned only one will render. In the example below only the id="swagger-ui-1110306185" will render and id="swagger-ui-1671020283" will not render.

<div class="swagger">
    
    
    
        
    <link rel="stylesheet" href="/etc.clientlibs/xxx/clientlibs/static/swagger-ui.min.css" type="text/css">
    <script src="/etc.clientlibs/xxx/clientlibs/static/swagger-ui.min.js"></script>
    
    
    
    
    
        
    
    
    <div>
        <h1 class="t-title">
            Here goes the optional title for the Swagger component
        </h1>
        <div id="swagger-ui-1671020283"></div>
        
        <script>
            window.onload = function () {
    
                window.ui = SwaggerUIBundle({
                    urls: [
                        {url: "/content/dam/yaml-files/account-information-service-api-v2-of-fi.yaml", name: "account-information-service-api-v2-of-fi"},
                    ],
                    dom_id: '#swagger-ui-1671020283',
                    deepLinking: true,
                    filter: true,
                    presets: [
                        SwaggerUIBundle.presets.apis,
                        SwaggerUIStandalonePreset
                    ],
                    plugins: [
                        SwaggerUIBundle.plugins.DownloadUrl
                    ],
                    layout: 'StandaloneLayout'
                });
    
            };
        </script>
    
    
    </div>
    </div>
   
        
            
<div class="swagger">
    
    
    
        
    
    
    
    
    
        
    
    
    <div>
        <h1 class="t-title">
            
        </h1>
        <div id="swagger-ui-1110306185"></div>
        
        <script>
            window.onload = function () {
    
                window.ui = SwaggerUIBundle({
                    urls: [
                        {url: "/content/dam/yaml-files/consent-flow-api-v2-of-fi.yaml", name: "consent-flow-api-v2-of-fi"},
                    ],
                    dom_id: '#swagger-ui-1110306185',
                    deepLinking: true,
                    filter: true,
                    presets: [
                        SwaggerUIBundle.presets.apis,
                        SwaggerUIStandalonePreset
                    ],
                    plugins: [
                        SwaggerUIBundle.plugins.DownloadUrl
                    ],
                    layout: 'StandaloneLayout'
                });
    
            };
        </script>
    
    
    </div>

</div>
2 Answers

I believe the problem may be that you have redefined window.onload in each one. Only the last version defined at the time of the onload event (with the last ID) is still there when the window loads. Consider creating a framework where you create one window.onload function in the head, perhaps, and add to an array in it so that all the functions are called properly. (Or utilizing a free framework that already exists such as a pub/sub.)

Here is what worked:

AEM Template:

<div data-sly-test="${model.defined}" data-sly-attribute="${model.dataAttributes}">
        <h1 class="t-title">
            ${model.title}
        </h1>
        <div id="${model.uniqueId}"></div>
</div>

AEM TypeScript:

declare global {
    const SwaggerUIStandalonePreset: any;
    const SwaggerUIBundle: any;
}

export class Swagger {

    static ID = 'swagger';

    static selectors = {
        self: `[data-${CoreComponentUtil.NS}-is='${Swagger.ID}']`,
    };

    private config: CompConfig;
    private name: string;
    private path: string;
    private domId: string;

    constructor(config: CompConfig) {
        this.init(config);
    }

    private init(config: CompConfig): void {
        this.config = config;

        // prevents multiple initialization
        config.element.removeAttribute(`data-${CoreComponentUtil.NS}-is`);

        this.name = config.element.getAttribute('data-swagger-name');
        this.path = config.element.getAttribute('data-swagger-path');
        this.domId = config.element.getAttribute('data-swagger-dom-id');
        try {
            new SwaggerUIBundle({
                urls: [
                    {url: this.path, name: this.name},
                ],
                dom_id: '#' + this.domId,
                deepLinking: true,
                filter: true,
                presets: [
                    SwaggerUIBundle.presets.apis,
                    SwaggerUIStandalonePreset,
                ],
                plugins: [
                    SwaggerUIBundle.plugins.DownloadUrl,
                ],
                layout: 'StandaloneLayout',
            });
        } catch (e) {
            console.log('Could not create Swagger object for ' + this.path);
        }
    }

}

onReady(() => {
    initComponent(Swagger);
});

AEM Model:

 public String getSwaggerYamlPath() {
        return swaggerYamlPath;
    }

    public String getSwaggerYamlName() {
        return parseYamlFileName(swaggerYamlPath);
    }

    public String getUniqueId() {
        return new StringBuilder().append("swagger-ui").append("-").append(String.valueOf(Math.abs(resource.getPath().hashCode() - 1))).toString();

    }

    @Override
    public Map<String, String> getDataAttributes() {
        Map<String, String> attributes = new HashMap<>(super.getDataAttributes());
        if (isDefined()) {
            attributes.put("data-swagger-name", getSwaggerYamlName());
            attributes.put("data-swagger-path", getSwaggerYamlPath());
            attributes.put("data-swagger-dom-id", getUniqueId());
        }
        return attributes;
    }
Related