Last active
February 3, 2022 20:18
-
-
Save thiagoelg/20b408720d153262d1ab6d503c967b85 to your computer and use it in GitHub Desktop.
Kogito Tooling Examples - Implementing the Ping Pong View in Angular
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"$schema": "./node_modules/@angular/cli/lib/config/schema.json", | |
"version": 1, | |
"newProjectRoot": "projects", | |
"cli": { | |
"packageManager": "yarn" | |
}, | |
"projects": { | |
"ping-pong-view-angular": { | |
... | |
}, | |
"ping-pong-view-wc": { | |
"projectType": "application", | |
"root": "", | |
"sourceRoot": "src", | |
"architect": { | |
"build": { | |
"builder": "@angular-devkit/build-angular:browser", | |
"options": { | |
"outputPath": "dist/wc", | |
"index": "src/index.html", | |
"main": "src/app/web-component/web-component.main.ts", | |
"polyfills": "src/polyfills.ts", | |
"tsConfig": "tsconfig.wc.json", | |
"aot": true, | |
"assets": ["src/favicon.ico", "src/assets"], | |
"styles": ["src/styles.css"], | |
"scripts": [] | |
}, | |
"configurations": { | |
"production": { | |
"fileReplacements": [ | |
{ | |
"replace": "src/environments/environment.ts", | |
"with": "src/environments/environment.prod.ts" | |
} | |
], | |
"optimization": true, | |
"outputHashing": "none", | |
"sourceMap": false, | |
"namedChunks": false, | |
"extractLicenses": true, | |
"vendorChunk": false, | |
"buildOptimizer": true, | |
"budgets": [ | |
{ | |
"type": "initial", | |
"maximumWarning": "500kb", | |
"maximumError": "1mb" | |
}, | |
{ | |
"type": "anyComponentStyle", | |
"maximumWarning": "2kb", | |
"maximumError": "4kb" | |
} | |
] | |
} | |
} | |
} | |
} | |
} | |
}, | |
"defaultProject": "ping-pong-view-angular" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Injectable } from "@angular/core"; | |
import { MessageBusClientApi } from "@kie-tools-core/envelope-bus/dist/api"; | |
import { PingPongChannelApi, PingPongInitArgs } from "@kie-tools-examples/ping-pong-view/dist/api"; | |
import { PingPongFactory } from "@kie-tools-examples/ping-pong-view/dist/envelope"; | |
import { ReplaySubject, BehaviorSubject, Subject } from "rxjs"; | |
declare global { | |
interface Window { | |
initArgs: PingPongInitArgs; | |
channelApi: PingPongChannelApi; | |
} | |
} | |
export interface LogEntry { | |
line: string; | |
time: number; | |
} | |
function getCurrentTime() { | |
return Date.now(); | |
} | |
@Injectable({ | |
providedIn: 'root', | |
}) | |
export class PingPongApiService implements PingPongFactory { | |
channelApi: MessageBusClientApi<PingPongChannelApi>; | |
initArgs: PingPongInitArgs; | |
log = new ReplaySubject<LogEntry>(10); | |
logCleared = new Subject(); | |
lastPingTimestamp = new BehaviorSubject<number>(0); | |
dotInterval?: number; | |
initialized = false; | |
pingSubscription?: (source: string) => void; | |
pongSubscription?: (source: string, replyingTo: string) => void; | |
constructor() {} | |
create(initArgs: PingPongInitArgs, channelApi: MessageBusClientApi<PingPongChannelApi>) { | |
// Making sure we don't subscribe more than once. | |
this.clearSubscriptions(); | |
this.clearInterval(); | |
this.initArgs = initArgs; | |
this.channelApi = channelApi; | |
// Subscribe to ping notifications. | |
this.pingSubscription = this.channelApi.notifications.pingPongView__ping.subscribe((pingSource) => { | |
// If this instance sent the PING, we ignore it. | |
if (pingSource === this.initArgs.name) { | |
return; | |
} | |
// Add a new line to our log, stating that we received a ping. | |
this.log.next({ line: `PING from '${pingSource}'.`, time: getCurrentTime() }); | |
// Acknowledges the PING message by sending back a PONG message. | |
this.channelApi.notifications.pingPongView__pong.send(this.initArgs.name, pingSource); | |
}); | |
// Subscribe to pong notifications. | |
this.pongSubscription = this.channelApi.notifications.pingPongView__pong.subscribe( | |
(pongSource: string, replyingTo: string) => { | |
// If this instance sent the PONG, or if this PONG was not meant to this instance, we ignore it. | |
if (pongSource === this.initArgs.name || replyingTo !== this.initArgs.name) { | |
return; | |
} | |
// Updates the log to show a feedback that a PONG message was observed. | |
this.log.next({ line: `PONG from '${pongSource}'.`, time: getCurrentTime() }); | |
} | |
); | |
// Populate the log with a dot each 2 seconds. | |
this.dotInterval = window.setInterval(() => { | |
this.log.next({ line: ".", time: getCurrentTime() }); | |
}, 2000); | |
this.initialized = true; | |
return () => ({ | |
clearLogs: () => { | |
this.log = new ReplaySubject<LogEntry>(10); | |
// Emit a value to logCleared so we can re-subscribe to this.log wherever needed. | |
this.logCleared.next(null); | |
}, | |
getLastPingTimestamp: () => { | |
return Promise.resolve(this.lastPingTimestamp.value); | |
}, | |
}); | |
} | |
// Send a ping to the channel. | |
ping() { | |
this.channelApi.notifications.pingPongView__ping.send(this.initArgs.name); | |
this.lastPingTimestamp.next(getCurrentTime()); | |
} | |
clearSubscriptions() { | |
this.pingSubscription && this.channelApi.notifications.pingPongView__ping.unsubscribe(this.pingSubscription); | |
this.pongSubscription && this.channelApi.notifications.pingPongView__pong.unsubscribe(this.pongSubscription); | |
} | |
clearInterval() { | |
window.clearInterval(this.dotInterval); | |
} | |
ngOnDestroy() { | |
this.clearSubscriptions(); | |
this.clearInterval(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="ping-pong-view--main"> | |
<h2>This is an implementation of Ping-Pong View in Angular</h2> | |
<p class="ping-pong-view--p-iframe"> | |
The envelope boundary border is green. It can be an iFrame or a Div. (It's possible to use Div if using web | |
components made from Angular components) | |
</p> | |
<p class="ping-pong-view--p-ping-pong">The Ping-Pong View implementation border is red</p> | |
<div class="ping-pong-view--container"> | |
<i>#{{ pingPongApiService.initArgs?.name }}</i> | |
<div class="ping-pong-view--header"> | |
<span>Hello from Angular!</span> | |
<button (click)="pingPongApiService.ping()">Ping others!</button> | |
</div> | |
<div class="ping-pong-view--log"> | |
<p *ngFor="let entry of log | async" class="ping-pong-view--line"> | |
{{ entry.line }} | |
</p> | |
</div> | |
</div> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { PingPongApiService, LogEntry } from "./ping-pong-api.service"; | |
import { Component, Input, OnInit } from "@angular/core"; | |
import * as PingPongViewEnvelope from "@kie-tools-examples/ping-pong-view/dist/envelope"; | |
import { ContainerType } from "@kie-tools-core/envelope/dist/api"; | |
import { Observable, scan } from "rxjs"; | |
@Component({ | |
selector: "app-ping-pong", | |
templateUrl: "./ping-pong.component.html", | |
styleUrls: ["./ping-pong.component.css"], | |
providers: [], | |
}) | |
export class PingPongComponent implements OnInit { | |
@Input() containerType: ContainerType = ContainerType.IFRAME; | |
@Input() envelopeId?: string; | |
constructor(public pingPongApiService: PingPongApiService) {} | |
log: Observable<LogEntry[]>; | |
subscribeToLogUpdates() { | |
this.log = this.pingPongApiService.log.asObservable().pipe(scan((acc, curr) => [...acc.slice(-9), curr], [])); | |
} | |
ngOnInit() { | |
// Initialize log with a starting message. | |
this.pingPongApiService.log.next({ line: "Logs will show up here", time: 0 }); | |
// Initialize envelope with the container config, the bus, | |
// and factory (in this case, a service that implements the "create" method). | |
PingPongViewEnvelope.init({ | |
config: { containerType: this.containerType, envelopeId: this.envelopeId! }, | |
bus: { postMessage: (message, _targetOrigin, transfer) => window.parent.postMessage(message, "*", transfer) }, | |
pingPongViewFactory: this.pingPongApiService, | |
}); | |
// Create an observable variable with the 10 latest values of the log. | |
this.subscribeToLogUpdates(); | |
this.pingPongApiService.logCleared.subscribe(() => this.subscribeToLogUpdates()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { PingPongApiService } from "./ping-pong-api.service"; | |
import { NgModule } from "@angular/core"; | |
import { BrowserModule } from "@angular/platform-browser"; | |
import { PingPongComponent } from "./ping-pong.component"; | |
@NgModule({ | |
declarations: [PingPongComponent], | |
imports: [BrowserModule], | |
exports: [PingPongComponent], | |
providers: [PingPongApiService], | |
bootstrap: [PingPongComponent], | |
}) | |
export class PingPongModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* The API of a PingPongViewApi. | |
* | |
* These methods are what the "external world" knows about this component. | |
*/ | |
export interface PingPongApi { | |
clearLogs(): void; | |
getLastPingTimestamp(): Promise<number>; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Methods provided by the Envelope that can be consumed by the Channel. | |
*/ | |
export interface PingPongEnvelopeApi { | |
pingPongView__init(association: Association, initArgs: PingPongInitArgs): Promise<void>; | |
pingPongView__clearLogs(): Promise<void>; | |
pingPongView__getLastPingTimestamp(): Promise<number>; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class PingPongEnvelopeApiImpl implements PingPongEnvelopeApi { | |
constructor( | |
private readonly args: EnvelopeApiFactoryArgs<PingPongEnvelopeApi, PingPongChannelApi, void, {}>, | |
private readonly pingPongViewFactory: PingPongFactory | |
) {} | |
pingPongApi?: () => PingPongApi | null; | |
public async pingPongView__init(association: Association, initArgs: PingPongInitArgs) { | |
this.args.envelopeClient.associate(association.origin, association.envelopeServerId); | |
this.pingPongApi = this.pingPongViewFactory.create(initArgs, this.args.envelopeClient.manager.clientApi); | |
} | |
public async pingPongView__clearLogs() { | |
this.pingPongApi?.()?.clearLogs(); | |
} | |
public async pingPongView__getLastPingTimestamp() { | |
const api = this.pingPongApi?.(); | |
if (!api) return Promise.resolve(0); | |
return api.getLastPingTimestamp(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class PingPongApiService implements PingPongFactory { | |
... | |
create(initArgs: PingPongInitArgs, channelApi: MessageBusClientApi<PingPongChannelApi>) { | |
... | |
return () => { | |
... | |
} as PingPongApi; | |
} | |
pingPongApiService = new PingPongApiService(); | |
// Initialize envelope with the container config, the bus, | |
// and factory (in this case, a service that implements the "create" method). | |
// This should be called after the view is rendered, | |
// inside a `ngOnInit` or `useEffect` for example. | |
PingPongViewEnvelope.init({ | |
config: { containerType: this.containerType, envelopeId: this.envelopeId! }, | |
bus: { postMessage: (message, _targetOrigin, transfer) => window.parent.postMessage(message, "*", transfer) }, | |
pingPongViewFactory: this.pingPongApiService, | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"extends": "./tsconfig.json", | |
"compilerOptions": { | |
"outDir": "./dist/wc", | |
"types": [] | |
}, | |
"files": ["src/app/web-component/web-component.main.ts", "src/polyfills.ts"], | |
"include": ["src/app/web-component/*.d.ts"] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component, Input } from "@angular/core"; | |
import { ContainerType } from "@kie-tools-core/envelope/dist/api"; | |
@Component({ | |
selector: "ping-pong-wc", | |
template: `<app-ping-pong [containerType]="containerType" [envelopeId]="envelopeId"></app-ping-pong>`, | |
}) | |
export class PingPongWcComponent { | |
@Input("containertype") containerType: ContainerType; | |
@Input("envelopeid") envelopeId: string; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { WebComponentModule } from "./web-component.module"; | |
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; | |
const bootstrap = () => platformBrowserDynamic().bootstrapModule(WebComponentModule); | |
bootstrap().catch((err) => console.error(err)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { NgModule, Injector, DoBootstrap } from "@angular/core"; | |
import { BrowserModule } from "@angular/platform-browser"; | |
import { createCustomElement } from "@angular/elements"; | |
import { PingPongModule } from "../ping-pong/ping-pong.module"; | |
import { PingPongWcComponent } from "./web-component.component"; | |
@NgModule({ | |
declarations: [PingPongWcComponent], | |
imports: [BrowserModule, PingPongModule], | |
entryComponents: [PingPongWcComponent], | |
providers: [], | |
}) | |
export class WebComponentModule implements DoBootstrap { | |
constructor(private injector: Injector) {} | |
ngDoBootstrap() { | |
const element = createCustomElement(PingPongWcComponent, { injector: this.injector }); | |
customElements.define("ping-pong-angular", element); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment