{"id":2372,"date":"2018-05-04T15:01:20","date_gmt":"2018-05-04T13:01:20","guid":{"rendered":"https:\/\/www.angulararchitects.io\/?p=2372"},"modified":"2018-05-04T15:01:20","modified_gmt":"2018-05-04T13:01:20","slug":"micro-apps-with-web-components-using-angular-elements","status":"publish","type":"post","link":"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/","title":{"rendered":"Micro Apps with Web Components using Angular Elements"},"content":{"rendered":"<div class=\"article\">\n<hr>\n<div style=\"font-size:14px\">\n<b style=\"font-weight: bold\">Table of Contents<\/b><\/p>\n<p>This blog post is part of an article series about Micro Apps:<\/p>\n<ul class=\"toc\">\n<li><a href=\"https:\/\/www.angulararchitects.io\/post\/2017\/12\/28\/a-software-architect-s-approach-towards-using-angular-and-spas-in-general-for-microservices-aka-microfrontends.aspx\">A Software Architect's Approach Towards Using Angular (And SPAs In General) For Microservices Aka Microfrontends<\/a><\/li>\n<li><a href=\"https:\/\/www.angulararchitects.io\/post\/2018\/05\/04\/microservice-clients-with-web-components-using-angular-elements-dreams-of-the-near-future.aspx\">Micro Apps With Web Components Using Angular Elements<\/a><\/li>\n<li>\n    <a href=\"https:\/\/www.angulararchitects.io\/post\/2018\/08\/19\/angular-react-vue-js-and-co-peacefully-united-thanks-to-micro-apps-and-web-components.aspx\" target=\"_blank\" rel=\"noopener\">Angular, React, Vue.Js and Co. peacefully united thanks to Micro Apps and Web Components<\/a>\n<\/li>\n<\/ul>\n<p><b style=\"font-weight: bold\">Related series about Web Components with Angular Elements:<\/b><\/p>\n<ul class=\"toc\">\n<li><a href=\"https:\/\/www.angulararchitects.io\/post\/2018\/07\/13\/angular-elements-part-i-a-dynamic-dashboard-in-four-steps-with-web-components.aspx\">Angular Elements, Part I: A Dynamic Dashboard In Four Steps With Web Components<\/a><\/li>\n<li><a href=\"https:\/\/www.angulararchitects.io\/post\/2018\/07\/29\/angular-elements-part-ii-lazy-and-external-web-components.aspx\">Angular Elements, Part II: Lazy And External Web Components<\/a><\/li>\n<li><a href=\"https:\/\/www.angulararchitects.io\/post\/2018\/07\/06\/angular-elements-without-zone-js.aspx\">Angular Elements, Part III: Angular Elements without Zone.js<\/a><\/li>\n<\/ul>\n<\/div>\n<hr>\n<p><span style=\"font-weight: bold;\">Update on 2018-05-04:<\/span> Updated for @angular\/elements in Angular 6<br \/><span style=\"font-weight: bold;\">Update on 2018-08-19:<\/span> Added option to use the CLI for building a self-contained bundle for each micro app<\/p>\n<p><span style=\"font-weight: bold;\">Source code:<\/span>&nbsp;<a href=\"https:\/\/github.com\/manfredsteyer\/angular-microapp\" target=\"_blank\" style=\"background-color: rgb(255, 255, 255);\" rel=\"noopener\">https:\/\/github.com\/manfredsteyer\/angular-microapp<\/a><\/p>\n<p>In <a href=\"https:\/\/www.angulararchitects.io\/post\/2017\/12\/28\/a-software-architect-s-approach-towards-using-angular-and-spas-in-general-for-microservices-aka-microfrontends.aspx\">one of my last blog posts<\/a> I've compared several <a href=\"https:\/\/www.angulararchitects.io\/post\/2017\/12\/28\/a-software-architect-s-approach-towards-using-angular-and-spas-in-general-for-microservices-aka-microfrontends.aspx\">approaches for using Single Page Applications, esp. Angular-based ones, in a microservice-based environment<\/a>. Some people are calling such SPAs <a href=\"https:\/\/medium.com\/@tomsoderlund\/micro-frontends-a-microservice-approach-to-front-end-web-development-f325ebdadc16\">micro frontends<\/a>; other call them Micro Apps.<\/p>\n<p>As you can read in the mentioned post, there is not the one and only perfect approach but several feasible concepts with different advantages and disadvantages.<\/p>\n<p>In this post I'm looking at one of those approaches in more detail: Using Web Components. For this, I'm leveraging the new Angular Elements library (@angular\/elements) which is available beginning with Angular 6. The <a href=\"https:\/\/github.com\/manfredsteyer\/angular-microapp\" target=\"_blank\" rel=\"noopener\">source code <\/a>for the case study decribed can be found in my <a href=\"https:\/\/github.com\/manfredsteyer\/angular-microapp\" target=\"_blank\" rel=\"noopener\">GitHub repo<\/a>.<\/p>\n<h2 id=\"case-study\">Case Study<\/h2>\n<p>The case study presented here is as simple as possible. It contains a shell app that dynamically loads and activates micro apps. It also takes care about routing between the apps (meta-routing) and allows them to communicate with each other using message passing. They are just called Client A and Client B. In addition, Client B also contains a widget from Client A.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/i.imgur.com\/2gd44H2.png\" width=\"600\" alt=\"Client A is activated\"><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/i.imgur.com\/bq60vSt.png\" width=\"600\" alt=\"Client B with widget from Client A\"><\/p>\n<h2 id=\"project-structure\">Project structure<\/h2>\n<p>Following the ideas of micro services, each part of the overall solution would be a separate project. This allows different teams do work individually on their parts without the need of much coordination.<\/p>\n<p>To make this case study a bit easier, I've decided to use one CLI project with a sub project for each part. This is something, the CLI supports beginning with version 6.<\/p>\n<p>You can create a sub project using <code>ng generate application my-sub-project<\/code> within an existing one.<\/p>\n<p>Using this approach, I've created the following structure:<\/p>\n<pre><code>  + projects\n    +--- client-a\n         +--- src\n    +--- client-b\n         +--- src\n  + src \n<\/code><\/pre>\n<p>The outer <code>src<\/code> folder at the end is the folder for the shell application.<\/p>\n<h2 id=\"micro-apps-as-web-components-with-angular-elements\">Micro Apps as Web Components with Angular Elements<\/h2>\n<p>To allow loading the micro apps on demand into the shell, they are exposed as Web Components using Angular Elements. In addition to that, I'm providing further Web Components for stuff I want to share with other Micro Apps.<\/p>\n<p>Using the API of Angular Elements isn't difficult. After npm installing <code>@angular\/elements<\/code> you just need to declare your Angular Component with a module and put it also into the <code>entryComponents<\/code> array. Using <code>entryComponents<\/code> is necessary because Angular Elements are created dynamically at runtime. Otherwise the compiler would not know about them.<\/p>\n<p>Than you have to create a wrapper for your component using <code>createCustomElement<\/code> and register it as a custom element with the browser using its <code>customElements.define<\/code> method:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-keyword\">import<\/span> { createCustomElement } from <span class=\"hljs-string\">'@angular\/elements'<\/span>;\n\n[...]\n\n@NgModule({\n  [...]\n  bootstrap: [],\n  entryComponents: [\n    AppComponent,\n    ClientAWidgetComponent\n  ]\n})\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">class<\/span> AppModule { \n  <span class=\"hljs-keyword\">constructor<\/span>(private injector: Injector) {\n  }\n\n  ngDoBootstrap() {\n    <span class=\"hljs-keyword\">const<\/span> appElement = createCustomElement(AppComponent, { injector: <span class=\"hljs-keyword\">this<\/span>.injector})\n    customElements.define(<span class=\"hljs-string\">'client-a'<\/span>, appElement);\n\n    <span class=\"hljs-keyword\">const<\/span> widgetElement = createCustomElement(ClientAWidgetComponent, { injector: <span class=\"hljs-keyword\">this<\/span>.injector})\n    customElements.define(<span class=\"hljs-string\">'client-a-widget'<\/span>, widgetElement);\n\n  }\n}\n<\/div><\/code><\/pre>\n<p>The <code>AppModule<\/code> above only offers two custom elements. The first one is the root component of the micro app and the second one is a component it shares whit other micro apps. Please note that it does not bootstrap a traditional Angular component. Hence, the <code>bootstrap<\/code> array is empty and we need to introduce an <code>ngDoBootstrap<\/code> method intended for manual bootstrapping.<\/p>\n<p>If we had traditional Angular components, services, modules, etc., we could also place this code inside of them.<\/p>\n<p>After this, we can use our Angular Components like ordinary HTML elements:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">client-a<\/span> [<span class=\"hljs-attr\">state<\/span>]=<span class=\"hljs-string\">\"someState\"<\/span> (<span class=\"hljs-attr\">message<\/span>)=<span class=\"hljs-string\">\"handleMessage($event)\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">client-a<\/span>&gt;<\/span>\n<\/div><\/code><\/pre>\n<p>While the last example uses Angular to call the Web Component, this also works with other frameworks and VanillaJS. In this case, we have to use the respective syntax of the hosting solution when calling the component.<\/p>\n<p>When we load web components in an other Angular application, we need to register the <code>CUSTOM_ELEMENTS_SCHEMA<\/code>:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-keyword\">import<\/span> { NgModule, CUSTOM_ELEMENTS_SCHEMA } from <span class=\"hljs-string\">'@angular\/core'<\/span>;\n\n[...]\n\n@NgModule({\n  declarations: [AppComponent\n  ],\n  imports: [BrowserModule],\n  schemas: [CUSTOM_ELEMENTS_SCHEMA],\n  providers: [],\n  bootstrap: [AppComponent]\n})\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">class<\/span> AppModule { }\n<\/div><\/code><\/pre>\n<p>This is necessary to tell the Angular compiler that there will be components it is not aware of. Those components are the web components that are directly executed by the browser.<\/p>\n<p>We also need a polyfill for browsers that don't support Web Components. Hence, I've npm installed <code>@webcomponents\/custom-elements<\/code> and referenced it at the end of the <code>polyfills.ts<\/code> file:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'@webcomponents\/custom-elements\/custom-elements.min'<\/span>;\n<\/div><\/code><\/pre>\n<p>This polyfill even works with IE 11.<\/p>\n<h2 id=\"routing-across-micro-apps\">Routing across Micro Apps<\/h2>\n<p>One thing that is rather unusual here, is that whole clients are implemented as Web Components and hence they are using routing:<\/p>\n<pre class=\"hljs\"><code><div>@NgModule({\n  imports: [\n    ReactiveFormsModule,\n    BrowserModule,\n    RouterModule.forRoot([\n      { path: <span class=\"hljs-string\">'client-a\/page1'<\/span>, component: Page1Component },\n      { path: <span class=\"hljs-string\">'client-a\/page2'<\/span>, component: Page2Component },\n      { path: <span class=\"hljs-string\">'**'<\/span>, component: EmptyComponent}\n    ], { useHash: <span class=\"hljs-literal\">true<\/span> })\n  ],\n  [...] \n})\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">class<\/span> AppModule {\n  [...]\n}\n<\/div><\/code><\/pre>\n<p>An interesting thing about this simple routing configuration is that it uses the prefix <code>client-a<\/code> for all but one route. The last route is a catch all route displaying an empty component. This makes the application disappear, when the current path does not start with its prefix. Using this simple trick you can allow the shell to switch between apps very easily.<\/p>\n<p>Please note that I'm using hash based routing as after changing the hash all routers in our micro apps will update their route. Unfortunately, this isn't the case with the default location strategy which leverages the push API.<\/p>\n<p>When bootstrapping such components as Web Components we have to initialize the router manually:<\/p>\n<pre class=\"hljs\"><code><div>@Component([...])\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">class<\/span> ClientAComponent {\n\n  <span class=\"hljs-keyword\">constructor<\/span>(private router: Router) {\n    router.initialNavigation(); \n    <span class=\"hljs-comment\">\/\/ Manually triggering initial navigation<\/span>\n  }\n\n}\n<\/div><\/code><\/pre>\n<h2 id=\"build-process---option-1-angular-cli\">Build Process - Option 1: Angular CLI<\/h2>\n<p>To get one self-contained bundle for our Web Component, we can use the CLI. Unfortunately, it always creates several bundles by default. To change this behavior, I'm using my CLI-extension called ngx-build-plus:<\/p>\n<pre><code>npm i ngx-build-plus --save-dev\n<\/code><\/pre>\n<p>It contains builders that tell the CLI which build steps to perform. To register the builder for getting a self-contained bundle, modify your <code>angular.json<\/code>:<\/p>\n<pre class=\"hljs\"><code><div>[...]\n<span class=\"hljs-string\">\"architect\"<\/span>: {\n  <span class=\"hljs-attr\">\"build\"<\/span>: {\n    <span class=\"hljs-attr\">\"builder\"<\/span>: <span class=\"hljs-string\">\"ngx-build-plus:build\"<\/span>,\n    [...]\n  }\n}\n[...]\n<\/div><\/code><\/pre>\n<p>In addition, I'm using the following npm scripts for building the shell as well as the micro apps:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-string\">\"build\"<\/span>: <span class=\"hljs-string\">\"npm run build:shell &amp;&amp; npm run build:clients\"<\/span>,\n<span class=\"hljs-string\">\"build:clients\"<\/span>: <span class=\"hljs-string\">\"npm run build:client-a &amp;&amp; npm run build:client-b\"<\/span>,\n<span class=\"hljs-string\">\"build:client-a\"<\/span>: <span class=\"hljs-string\">\"ng build --prod --project client-a --single-bundle true --output-hashing none --vendor-chunk false --output-path dist\/shell\/client-a\"<\/span>,\n<span class=\"hljs-string\">\"build:client-b\"<\/span>: <span class=\"hljs-string\">\"ng build --prod --project client-b <\/span><span style=\"background-color: transparent; color: inherit; font-size: inherit;\">--single-bundle true <\/span><span class=\"hljs-string\" style=\"background-color: transparent; color: inherit; font-size: inherit;\">--output-hashing none --vendor-chunk false --output-path dist\/shell\/client-b\"<\/span><span style=\"background-color: transparent; color: inherit; font-size: inherit;\">,<\/span><\/div><div><span class=\"hljs-string\">\"build:shell\"<\/span>: <span class=\"hljs-string\">\"ng build --project shell\"<\/span>,\n<\/div><\/code><\/pre>\n<p>Please note that the output-path switch puts the micro apps client-a and client-b into a subfolder of the shell. Hence, the shell can fetch their bundles via a relative path. While this is not necessary, it makes testing easier.<\/p>\n<p>In an <a href=\"https:\/\/github.com\/manfredsteyer\/Angular_MicroApps_Different_Technologies\">similar example<\/a> that can be found <a href=\"https:\/\/github.com\/manfredsteyer\/Angular_MicroApps_Different_Technologies\">here<\/a>, I'm copying the Micro App's bundles over to the shell's assets folder using the node package <code>cpr<\/code>. This makes debugging even easier.<\/p>\n<h2 id=\"build-process---option-2-webpack\">Build Process - Option 2: Webpack<\/h2>\n<p>As an alternative, I'm using a modified version of the webpack configuration from <a href=\"https:\/\/medium.com\/vincent-ogloblinsky\/export-angular-components-as-custom-elements-with-angular-elements-a2a0bfcd7f8a\">Vincent Ogloblinsky's blog post<\/a>.<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-keyword\">const<\/span> AotPlugin = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'@ngtools\/webpack'<\/span>).AngularCompilerPlugin;\n<span class=\"hljs-keyword\">const<\/span> path = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'path'<\/span>);\n<span class=\"hljs-keyword\">const<\/span> PurifyPlugin = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'@angular-devkit\/build-optimizer'<\/span>).PurifyPlugin;\n<span class=\"hljs-keyword\">const<\/span> webpack = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'webpack'<\/span>);\n\n<span class=\"hljs-keyword\">const<\/span> clientA = {\n  entry: <span class=\"hljs-string\">'.\/projects\/client-a\/src\/main.ts'<\/span>,\n  resolve: {\n    mainFields: [<span class=\"hljs-string\">'browser'<\/span>, <span class=\"hljs-string\">'module'<\/span>, <span class=\"hljs-string\">'main'<\/span>]\n  },\n  <span class=\"hljs-built_in\">module<\/span>: {\n    rules: [\n      { test: <span class=\"hljs-regexp\">\/\\.ts$\/<\/span>, loaders: [<span class=\"hljs-string\">'@ngtools\/webpack'<\/span>] },\n      { test: <span class=\"hljs-regexp\">\/\\.html$\/<\/span>, loader: <span class=\"hljs-string\">'html-loader'<\/span>,  options: { minimize: <span class=\"hljs-literal\">true<\/span> } },\n      {\n        test: <span class=\"hljs-regexp\">\/\\.js$\/<\/span>,\n        loader: <span class=\"hljs-string\">'@angular-devkit\/build-optimizer\/webpack-loader'<\/span>,\n        options: {\n          sourceMap: <span class=\"hljs-literal\">false<\/span>\n        }\n      }\n\n    ]\n  },\n  plugins: [\n\n    <span class=\"hljs-keyword\">new<\/span> AotPlugin({\n      skipCodeGeneration: <span class=\"hljs-literal\">false<\/span>,\n      tsConfigPath: <span class=\"hljs-string\">'.\/projects\/client-a\/tsconfig.app.json'<\/span>,\n      hostReplacementPaths: {\n        <span class=\"hljs-string\">\".\/src\/environments\/environment.ts\"<\/span>: <span class=\"hljs-string\">\".\/src\/environments\/environment.prod.ts\"<\/span>\n      },\n      entryModule: path.resolve(__dirname, <span class=\"hljs-string\">'.\/projects\/client-a\/src\/app\/app.module#AppModule'<\/span> )\n    }),\n\n    <span class=\"hljs-keyword\">new<\/span> PurifyPlugin()\n\n  ],\n  output: {\n    path: __dirname + <span class=\"hljs-string\">'\/dist\/shell\/client-a'<\/span>,\n    filename: <span class=\"hljs-string\">'main.bundle.js'<\/span>\n  },\n  mode: <span class=\"hljs-string\">'production'<\/span>\n};\n\n<span class=\"hljs-keyword\">const<\/span> clientB = { [...] };\n\n<span class=\"hljs-built_in\">module<\/span>.exports = [clientA, clientB];\n<\/div><\/code><\/pre>\n<p>In addition to that, I'm using some npm scripts to trigger both, the build of the shell as well as the build of the micro apps. For this, I'm copying the bundles for the micro apps over to the shell's <code>dist<\/code> folder. This makes testing a bit easier:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-string\">\"scripts\"<\/span>: {\n  <span class=\"hljs-attr\">\"start\"<\/span>: <span class=\"hljs-string\">\"live-server dist\/shell\"<\/span>,\n  <span class=\"hljs-attr\">\"build\"<\/span>: <span class=\"hljs-string\">\"npm run build:shell &amp;&amp; npm run build:clients \"<\/span>,\n  <span class=\"hljs-attr\">\"build:clients\"<\/span>: <span class=\"hljs-string\">\"webpack\"<\/span>,\n  <span class=\"hljs-attr\">\"build:shell\"<\/span>: <span class=\"hljs-string\">\"ng build --project shell\"<\/span>,\n  [...]\n}\n<\/div><\/code><\/pre>\n<h2 id=\"loading-bundles\">Loading bundles<\/h2>\n<p>After creating the bundles, we can load them into a shell application. A first simple approach could look like this:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">client-a<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">client-a<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">client-b<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">client-b<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"client-a\/main.bundle.js\"<\/span>&gt;<\/span><span class=\"undefined\"><\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"client-b\/main.bundle.js\"<\/span>&gt;<\/span><span class=\"undefined\"><\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n<\/div><\/code><\/pre>\n<p>This example shows one more time that a web component works just as an ordinary html element.<\/p>\n<p>We can also dynamically load the bundles on demand with some lines of simple DOM code. I will present a solution for this a bit later.<\/p>\n<h2 id=\"communication-between-micro-apps\">Communication between Micro Apps<\/h2>\n<p>Even though micro apps should be as isolated as possible, sometimes we need to share some information. The good message here is that we can leverage attributes and events for this:<\/p>\n<p>To implement this idea, our micro apps get a property <code>state<\/code> the shell can use to send down some application wide state. It also gets an event <code>message<\/code> to notify the shell:<\/p>\n<pre class=\"hljs\"><code><div>@Component({ ... })\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">class<\/span> AppComponent <span class=\"hljs-keyword\">implements<\/span> OnInit {\n\n  @Input(<span class=\"hljs-string\">'state'<\/span>) \n  <span class=\"hljs-keyword\">set<\/span> state(state: <span class=\"hljs-built_in\">string<\/span>) {\n      <span class=\"hljs-built_in\">console<\/span>.debug(<span class=\"hljs-string\">'client-a received state'<\/span>, state);\n  }\n\n  @Output() message = <span class=\"hljs-keyword\">new<\/span> EventEmitter&lt;<span class=\"hljs-built_in\">any<\/span>&gt;();\n\n  [...]\n}\n<\/div><\/code><\/pre>\n<p>The shell can now bind to these to communicate with the Micro App:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">client-a<\/span> [<span class=\"hljs-attr\">state<\/span>]=<span class=\"hljs-string\">\"appState\"<\/span> (<span class=\"hljs-attr\">message<\/span>)=<span class=\"hljs-string\">\"handleMessage($event)\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">client-a<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">client-b<\/span> [<span class=\"hljs-attr\">state<\/span>]=<span class=\"hljs-string\">\"appState\"<\/span> (<span class=\"hljs-attr\">message<\/span>)=<span class=\"hljs-string\">\"handleMessage($event)\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">client-b<\/span>&gt;<\/span>\n<\/div><\/code><\/pre>\n<p>Using this approach one can easily broadcast messages down by updating the appState. And if <code>handleMessage<\/code> also updates the <code>appState<\/code>, the micro apps can communicate with each other.<\/p>\n<p>One thing I want to point out is that this kind of message passing allows inter app communication without coupling them in a strong way.<\/p>\n<h2 id=\"dynamically-loading-micro-apps\">Dynamically Loading Micro Apps<\/h2>\n<p>As web components work as traditional html elements, we can dynamically load them into our app using DOM. For this task, I've created a simple configuration object pointing to all the data we need:<\/p>\n<pre class=\"hljs\"><code><div>config = {\n  <span class=\"hljs-string\">\"client-a\"<\/span>: {\n    path: <span class=\"hljs-string\">'client-a\/main.bundle.js'<\/span>,\n    element: <span class=\"hljs-string\">'client-a'<\/span>\n  },\n  <span class=\"hljs-string\">\"client-b\"<\/span>: {\n    path: <span class=\"hljs-string\">'client-b\/main.bundle.js'<\/span>,\n    element: <span class=\"hljs-string\">'client-b'<\/span>\n  }\n};\n<\/div><\/code><\/pre>\n<p>To load one of those clients, we just need to create a script tag pointing to its bundle and an element representing the micro app:<\/p>\n<pre class=\"hljs\"><code><div>load(name: <span class=\"hljs-built_in\">string<\/span>): <span class=\"hljs-built_in\">void<\/span> {\n\n  <span class=\"hljs-keyword\">const<\/span> configItem = <span class=\"hljs-keyword\">this<\/span>.config[name];\n  <span class=\"hljs-keyword\">const<\/span> content = <span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">'content'<\/span>);\n\n  <span class=\"hljs-keyword\">const<\/span> script = <span class=\"hljs-built_in\">document<\/span>.createElement(<span class=\"hljs-string\">'script'<\/span>);\n  script.src = configItem.path;\n  script.onerror = () =&gt; <span class=\"hljs-built_in\">console<\/span>.error(<span class=\"hljs-string\"><code>error loading &lt;span class=&quot;hljs-subst&quot;&gt;${configItem.path}&lt;\/span&gt;<\/code><\/span>);\n  content.appendChild(script);\n\n  <span class=\"hljs-keyword\">const<\/span> element: HTMLElement = <span class=\"hljs-built_in\">document<\/span>.createElement(configItem.element);\n  element.addEventListener(<span class=\"hljs-string\">'message'<\/span>, msg =&gt; <span class=\"hljs-keyword\">this<\/span>.handleMessage(msg));\n  content.appendChild(element);\n  element.setAttribute(<span class=\"hljs-string\">'state'<\/span>, <span class=\"hljs-string\">'init'<\/span>);\n}\n\nhandleMessage(msg): <span class=\"hljs-built_in\">void<\/span> {\n  <span class=\"hljs-built_in\">console<\/span>.debug(<span class=\"hljs-string\">'shell received message: '<\/span>, msg.detail);\n}\n<\/div><\/code><\/pre>\n<p>By hooking up an event listener for the message event, the shell can receive information from the micro apps. To send some data down, this example uses <code>setAttribute<\/code>.<\/p>\n<p>We can even decide when to call the load function for our application. This means, we can implements eager loading or lazy loading. For the sake of simplicity I've decided for the first option:<\/p>\n<pre class=\"hljs\"><code><div>ngOnInit() {\n  <span class=\"hljs-keyword\">this<\/span>.load(<span class=\"hljs-string\">'client-a'<\/span>);\n  <span class=\"hljs-keyword\">this<\/span>.load(<span class=\"hljs-string\">'client-b'<\/span>);\n}\n<\/div><\/code><\/pre>\n<h2 id=\"using-widgets-from-other-micro-apps\">Using Widgets from other Micro Apps<\/h2>\n<p>Using widgets from other Micro Apps is also a piece of cake: Just create an html element. Hence, all client b has to do to use client a's widget is this:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">client-a-widget<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">client-a-widget<\/span>&gt;<\/span>\n<\/div><\/code><\/pre>\n<h2 id=\"evaluation\">Evaluation<\/h2>\n<h3 id=\"advantages\">Advantages<\/h3>\n<ul>\n<li>Styling is isolated from other Microservice Clients due to Shadow DOM or the Shadow DOM Emulation provided by Angular out of the box.<\/li>\n<li>Allows for separate development and separate deployment<\/li>\n<li>Mixing widgets from different Microservice Clients is possible<\/li>\n<li>The shell can be a Single Page Application too<\/li>\n<li>We can use different SPA frameworks in different versions for our Microservice Clients<\/li>\n<\/ul>\n<h3 id=\"disadvantages\">Disadvantages<\/h3>\n<ul>\n<li>Microservice Clients are not completely isolated as it would be the case when using hyperlinks or iframes instead. This means that they could influence each other in an unplanned way. This also means that there can be conflicts when using different frameworks in different versions.<\/li>\n<li>We need polyfills for some browsers<\/li>\n<li>We cannot leverage the CLI for generating a self-contained package for every client. Hence, I used webpack.<\/li>\n<\/ul>\n<h3 id=\"tradeoff\">Tradeoff<\/h3>\n<ul>\n<li>We have to decide, whether we want to import all the libraries once or once for each client. This is more or less a matter of bundling. The first option allows to optimize for bundle sizes; the last option provides more isolation and hence separate development and deployment. This properties are considered valuable architectural goals in the world of micro services.<\/li>\n<\/ul>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Case study for writing micro apps with web components <\/p>\n","protected":false},"author":9,"featured_media":3020,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_price":"","_stock":"","_tribe_ticket_header":"","_tribe_default_ticket_provider":"","_ticket_start_date":"","_ticket_end_date":"","_tribe_ticket_show_description":"","_tribe_ticket_show_not_going":false,"_tribe_ticket_use_global_stock":"","_tribe_ticket_global_stock_level":"","_global_stock_mode":"","_global_stock_cap":"","_tribe_rsvp_for_event":"","_tribe_ticket_going_count":"","_tribe_ticket_not_going_count":"","_tribe_tickets_list":"[]","_tribe_ticket_has_attendee_info_fields":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-2372","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-unkategorisiert"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Micro Apps with Web Components using Angular Elements - ANGULARarchitects<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Micro Apps with Web Components using Angular Elements - ANGULARarchitects\" \/>\n<meta property=\"og:description\" content=\"Case study for writing micro apps with web components\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/\" \/>\n<meta property=\"og:site_name\" content=\"ANGULARarchitects\" \/>\n<meta property=\"article:published_time\" content=\"2018-05-04T13:01:20+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2018\/05\/microservice-clients-with-web-components-using-angular-elements-dreams-of-the-near-future.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"640\" \/>\n\t<meta property=\"og:image:height\" content=\"452\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Manfred Steyer, GDE\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@daniel\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Manfred Steyer, GDE\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"11 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/\"},\"author\":{\"name\":\"Manfred Steyer, GDE\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951\"},\"headline\":\"Micro Apps with Web Components using Angular Elements\",\"datePublished\":\"2018-05-04T13:01:20+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/\"},\"wordCount\":1660,\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2018\/05\/microservice-clients-with-web-components-using-angular-elements-dreams-of-the-near-future.jpg\",\"articleSection\":[\"Unkategorisiert\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/\",\"name\":\"Micro Apps with Web Components using Angular Elements - ANGULARarchitects\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2018\/05\/microservice-clients-with-web-components-using-angular-elements-dreams-of-the-near-future.jpg\",\"datePublished\":\"2018-05-04T13:01:20+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/#primaryimage\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2018\/05\/microservice-clients-with-web-components-using-angular-elements-dreams-of-the-near-future.jpg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2018\/05\/microservice-clients-with-web-components-using-angular-elements-dreams-of-the-near-future.jpg\",\"width\":640,\"height\":452},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.angulararchitects.io\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Micro Apps with Web Components using Angular Elements\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"name\":\"ANGULARarchitects\",\"description\":\"AngularArchitects.io\",\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\",\"name\":\"ANGULARarchitects\",\"alternateName\":\"SOFTWAREarchitects\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"width\":644,\"height\":216,\"caption\":\"ANGULARarchitects\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/github.com\/angular-architects\",\"https:\/\/www.linkedin.com\/company\/angular-architects\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951\",\"name\":\"Manfred Steyer, GDE\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g\",\"caption\":\"Manfred Steyer, GDE\"},\"sameAs\":[\"https:\/\/x.com\/daniel\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Micro Apps with Web Components using Angular Elements - ANGULARarchitects","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/","og_locale":"en_US","og_type":"article","og_title":"Micro Apps with Web Components using Angular Elements - ANGULARarchitects","og_description":"Case study for writing micro apps with web components","og_url":"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/","og_site_name":"ANGULARarchitects","article_published_time":"2018-05-04T13:01:20+00:00","og_image":[{"width":640,"height":452,"url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2018\/05\/microservice-clients-with-web-components-using-angular-elements-dreams-of-the-near-future.jpg","type":"image\/jpeg"}],"author":"Manfred Steyer, GDE","twitter_card":"summary_large_image","twitter_creator":"@daniel","twitter_misc":{"Written by":"Manfred Steyer, GDE","Est. reading time":"11 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/#article","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/"},"author":{"name":"Manfred Steyer, GDE","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951"},"headline":"Micro Apps with Web Components using Angular Elements","datePublished":"2018-05-04T13:01:20+00:00","mainEntityOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/"},"wordCount":1660,"publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2018\/05\/microservice-clients-with-web-components-using-angular-elements-dreams-of-the-near-future.jpg","articleSection":["Unkategorisiert"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/","url":"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/","name":"Micro Apps with Web Components using Angular Elements - ANGULARarchitects","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/#primaryimage"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2018\/05\/microservice-clients-with-web-components-using-angular-elements-dreams-of-the-near-future.jpg","datePublished":"2018-05-04T13:01:20+00:00","breadcrumb":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/#primaryimage","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2018\/05\/microservice-clients-with-web-components-using-angular-elements-dreams-of-the-near-future.jpg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2018\/05\/microservice-clients-with-web-components-using-angular-elements-dreams-of-the-near-future.jpg","width":640,"height":452},{"@type":"BreadcrumbList","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/micro-apps-with-web-components-using-angular-elements\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.angulararchitects.io\/en\/"},{"@type":"ListItem","position":2,"name":"Micro Apps with Web Components using Angular Elements"}]},{"@type":"WebSite","@id":"https:\/\/www.angulararchitects.io\/en\/#website","url":"https:\/\/www.angulararchitects.io\/en\/","name":"ANGULARarchitects","description":"AngularArchitects.io","publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.angulararchitects.io\/en\/#organization","name":"ANGULARarchitects","alternateName":"SOFTWAREarchitects","url":"https:\/\/www.angulararchitects.io\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","width":644,"height":216,"caption":"ANGULARarchitects"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/github.com\/angular-architects","https:\/\/www.linkedin.com\/company\/angular-architects\/"]},{"@type":"Person","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951","name":"Manfred Steyer, GDE","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g","caption":"Manfred Steyer, GDE"},"sameAs":["https:\/\/x.com\/daniel"]}]}},"_links":{"self":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/2372","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/users\/9"}],"replies":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/comments?post=2372"}],"version-history":[{"count":0,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/2372\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media\/3020"}],"wp:attachment":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media?parent=2372"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/categories?post=2372"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/tags?post=2372"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}