Search code examples
htmlangulartypescriptangular-materialswagger-ui

Update embedded Swagger UI on toggle button change


I want to provide three different OpenApi definitions in a webapp, so users can read the documentation of different APIs.
The plan is to have a toggle button group with three buttons at the top and the swagger ui underneath it.
My problem is, that the swagger ui won't update if I click on a button. My approach looks like this:

api-docs.component.html

<mat-card>
    <mat-button-toggle-group style="width: auto; display: flex;" (change)="toggleApiDoc($event)">
        <mat-button-toggle checked value="mainPlattform" style="width: 100%">Main Plattform</mat-button-toggle>
        <mat-button-toggle value="adapterHttp" style="width: 100%">Adapter HTTP</mat-button-toggle>
        <mat-button-toggle value="adapterMqtt" style="width: 100%">Adapter MQTT</mat-button-toggle>
    </mat-button-toggle-group>
    <app-swagger-ui [url]=activeApiDoc></app-swagger-ui>
</mat-card>

api-docs.component.ts

import { Component } from '@angular/core';
import { MatButtonToggleChange } from '@angular/material/button-toggle';
import { environment } from 'src/environments/environment';

@Component({
  selector: 'app-api-docs',
  templateUrl: './api-docs.component.html',
  styleUrls: ['./api-docs.component.scss']
})
export class ApiDocsComponent {

  readonly mainApiDoc = environment.main_api_doc;
  readonly httpAdapterApiDoc = environment.http_adapter_doc;
  readonly mqttAdapterApiDoc = environment.http_adapter_doc;

  activeApiDoc = this.mainApiDoc;

  constructor() {
  }

  toggleApiDoc(event: MatButtonToggleChange) {
    switch (event.value) {
      case 'mainPlattform':
        this.activeApiDoc = this.mainApiDoc;
        break;
      case 'adapterHttp':
        this.activeApiDoc = this.httpAdapterApiDoc;
        break;
      case 'adapterMqtt':
        this.activeApiDoc = this.mqttAdapterApiDoc;
        break;
      default:
        this.activeApiDoc = this.mainApiDoc;
        break;
    }
  }
}

swagger-ui.component.html

<div id="swagger"></div>

swagger-ui.component.ts

import { Component, Input, OnInit } from '@angular/core';
import SwaggerUI from 'swagger-ui';

@Component({
  selector: 'app-swagger-ui',
  templateUrl: './swagger-ui.component.html',
  styleUrls: ['./swagger-ui.component.scss']
})
export class SwaggerUiComponent implements OnInit {

  @Input() url: string = "";
  constructor() { }

  ngOnInit(): void {
    const ui = SwaggerUI({
      url: this.url,
      dom_id: '#swagger'
  });
  }

}

environment.ts

export const environment = {
  main_api_doc: 'https://petstore.swagger.io/v2/swagger.json',
  http_adapter_doc: 'https://raw.githubusercontent.com/hjacobs/connexion-example/master/swagger.yaml'
};

As you can see I use random yaml files to test this. The first one gets rendered. I have an complete Swagger UI embedded in my webapp, but it won't render another Swagger UI, when I click a different toggle button. It just stays the same. As you can tell, I'm not so good with typescript and angular. So I guess it shouldn't be too hard. But I can't tell whats wrong here.


Solution

  • The problem seems to be the angular lifecycle. When I tried to view all docs at the same time I saw that still only one would get rendered. I changed the lifecycle hook function, where I create the Swagger UI and now it works.

    import { Component, Input, OnChanges } from '@angular/core';
    import SwaggerUI from 'swagger-ui';
    
    @Component({
      selector: 'app-swagger-ui',
      templateUrl: './swagger-ui.component.html',
      styleUrls: ['./swagger-ui.component.scss']
    })
    export class SwaggerUiComponent implements OnChanges {
    
      @Input() url: string = "";
    
      constructor() { }
    
      ngOnChanges() {
        const ui = SwaggerUI({
          url: this.url,
          dom_id: '#swagger'
        });
      }
    
    }