Sie sind hier: Weblog

Angular Frontends for Micro Services using iframes

Foto ,
12.12.2017 01:00:00

Even though the word iframe causes bad feelings for most web devs, it turns out that using them for loading SPAs in a micro service based environment -- aka micro frontends -- is a good choice. For instance, they allow for a perfect isolation between clients and for a separate deployment. Because of the isolation they also allow using different SPA frameworks. Besides iframes, there are other approaches to use SPAs in micro service architectures -- of course, each of them has their own pros and cons. A good overview can be found here.

As Asim Hussain shows in this blog article, using iframes can also be a nice solution for migrating an existing AngularJS application to Angular.

The approach described here uses a "meta router" to load different spa clients for micro services in iframes. It takes care about the iframes creation and about synchronizing their routes with the shells url. It also resizes the iframe dynamically to prevent a scrolling bar within it. Ive written this router in VanillaJS for two reasons: 1) It allows very slim shell applications that dont base upon any framework and 2) it allows using different frameworks for different micro frontends.

The router and an example that shows how to use it can be found in my GitHub repo.

This is what a shell that uses the meta router looks like:

<!-- index.html in shell -->
<a href="javascript:router.go(a)">Route to A</a> |
<a href="javascript:router.go(b)">Route to B</a> |
<a href="javascript:router.go(a, a)">Jump to A within A</a>
<a href="javascript:router.go(a, b)">Jump to B within A</a>

<div id="outlet"></div>

<script src="polyfills.js"></script>
<script src="router.js"></script>
<script>
  var config = [
      {
          path: a,
          app: /app-a/dist
      },
      {
          path: b,
          app: /app-b/dist
      }
  ];

  router.config(config);
  router.init();
  router.preload();
</script>

The router creates the iframes as children of the element with the id outlet and allows switching between them using the method go. As you see in the example, it also allows to jump to a subroute within an application.

The routed applications use the child-app.js script and define an appId which is the same as the used path above:

<!-- index.html in routed app -->
<script src="../../child-app.js"></script>
<script>
    childApp.config({ appId: a });
    childApp.init();
</script>

To synchronize the shells url with the routed applications urls, we need to use Angulars Router service:

// app.component.ts in routed app

import { Router, NavigationEnd } from @angular/router;
import { Component } from @angular/core;
import { filter } from rxjs/operators;

declare let childApp: any;

@Component({
  selector: app-root,
  templateUrl: ./app.component.html,
  styleUrls: [./app.component.css]
})
export class AppComponent {
  title = app;

  constructor(private router: Router) {
    this.initChildRouter();
  }

  // Sync Subroutes
  initChildRouter() {
    // send route to shell  
    this.router.events.pipe(filter(e => e instanceof NavigationEnd)).subscribe((e: NavigationEnd) => {
      childApp.sendRoute(e.url);
    });

    // get route from shell
    childApp.registerForRouteChange(url => this.router.navigateByUrl(url));
  }

}

The source code can be found in my GitHub repo.