https://github.com/jjwesterkamp/rx-postmessenger
Minimal RxJS adapter for the window.postMessage API for request-response streams and notification streams across frame windows.
https://github.com/jjwesterkamp/rx-postmessenger
angular events iframe notification postmessage request rxjs typescript
Last synced: 7 months ago
JSON representation
Minimal RxJS adapter for the window.postMessage API for request-response streams and notification streams across frame windows.
- Host: GitHub
- URL: https://github.com/jjwesterkamp/rx-postmessenger
- Owner: JJWesterkamp
- License: mit
- Created: 2017-11-24T22:45:58.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2023-03-06T08:25:00.000Z (over 2 years ago)
- Last Synced: 2024-08-10T08:14:44.918Z (10 months ago)
- Topics: angular, events, iframe, notification, postmessage, request, rxjs, typescript
- Language: TypeScript
- Homepage: https://jjwesterkamp.github.io/rx-postmessenger/
- Size: 1.32 MB
- Stars: 27
- Watchers: 2
- Forks: 1
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# rx-postmessenger
[](https://badge.fury.io/js/rx-postmessenger)
[](https://travis-ci.com/JJWesterkamp/rx-postmessenger)
[](https://coveralls.io/github/JJWesterkamp/rx-postmessenger?branch=master)Minimal [RxJS][rxgh] adapter for the [`Window # postMessage`][postmessage] API for request-response streams and notification streams across frame windows.
## In short
An RxPostmessenger class instance establishes one end of a connection between 2 window objects, using the `Window # postMessage` API. Each instance provides methods to initiate outgoing messages and handle incoming messages, both categorized by channel name.
Each instance targets _one single_ `Window` object. It propagates incoming `MessageEvent`s only from that specific window object while it's serving documents from _one single_ origin.
## RxJS Interoperability
| | RxPostmessenger v1.x | RxPostmessenger v2.x | RxPostmessenger v3.x |
|---------------|:--------------------:|:--------------------:|:--------------------:|
| RxJS v5.x | ✅ | ❌ | ❌ |
| RxJS v6.x | ❌ | ✅ | ✅ |
| RxJS v7.x | ❌ | ❌ | ✅ |[View changelog.][changelog]
## Installation
```bash
$ npm install rx-postmessenger --save
```## Contents / API
**Static methods**
|Method|Description|
|:-|:-|
|[`connect()`](#connecting-2-window-objects)|Connect `Window` objects by creating messenger instances.|**`Messenger` Instance methods**
|Method|Description|
|:-|:-|
|[`notify()`](#sending-notifications)|Send notifications to the connected window.|
|[`notifications()`](#listening-for-inbound-notifications)|Listen for inbound notifications.|
|[`request()`](#sending-requests)|Send requests to the connected window.|
|[`requests()`](#listening-for-inbound-requests)|Listen for inbound requests.|**`Request` Instance methods**
|Method|Description|
|:-|:-|
|[`respond()`](#sending-request-responses)|Respond to the request with a certain payload.---
## Usage
```javascript
import RxPostmessenger from 'rx-postmessenger';
```### Static methods
#### Connecting 2 Window objects
> ```typescript
> RxPostmessenger.connect(otherWindow: Window, origin: string): RxPostmessenger.Messenger
> ```Both ends of the connection should implement this package. One in a _parent_ project (that implements the iframe), and one in a _child_ project (that's being served by the iframe). Creating a new messenger is straightforward:
_At parent window - `https://parent-project.com`_
```javascript
const childMessenger = RxPostmessenger.connect(
someIFrame.contentWindow,
'https://child-project.com'
);
```_At child window - `https://child-project.com`_
```javascript
const parentMessenger = RxPostmessenger.connect(
window.parent,
'https://parent-project.com'
);
```### `Messenger` Instance methods
#### Sending notifications
> ```typescript
> Messenger.notify(channel: string, payload?: T): void
> ```The messenger instances give you a way to send notifications to the other `Window` through the `notify()` method.
The notify method is void -- notifications are fire-and-forget.
Use [`request()`](#sending-requests) instead if you require data back.
Consider an example where we want to notify a child window of price changes:```javascript
childMessenger.notify('price-changed', {
oldPrice: 12.50,
newPrice: 14.50,
});
```#### Listening for inbound notifications
> ```typescript
> Messenger.notifications(channel: string): Observable
> ```The child project can request an Observable stream for a certain notification channel.
In this case we're interested in `'price-changed'` events, but only the ones where the price increased.
The ability to use RxJS operators can help us out:```javascript
parentMessenger.notifications('price-changed').pipe(
filter(({ oldPrice, newPrice }) => newPrice > oldPrice),
map(({ oldPrice, newPrice }) => newPrice - oldPrice),
).subscribe((increase) => console.log(`Price increased with €${increase}!`));// > 'Price increased with €2!'
```#### Sending requests
> ```typescript
> Messenger.request(channel: string, payload?: T): Observable
> ```RxPostmessenger also supports request - response communication.
At the requester side a request is initiated by calling the `request()` method with 1 or 2 arguments.
The first is a request alias (actually just another channel) of our choice._A notification-channel and a request-channel can both have the same channel name without any problem._
An observable is returned that emits the response when arrived, and then completes.
Let's request a greeting from the child window, and tell it to localize the response to `'en'`:```javascript
const greetingResponse$ = childMessenger.request('greeting', {
language: 'en',
});
```We can then subscribe to the greeting response stream.
Provided that the greeting says something nice, we'll log it for everyone to see:```javascript
greetingResponse$.pipe(
filter((greeting) => isNiceGreeting(greeting)),
).subscribe(console.log);// > 'Hi parent!'
```#### Listening for inbound requests
> ```typescript
> Messenger.requests(channel: string): Observable>
> ```No greeting would ever be received by `parentMessenger` when the child project does not listen
for requests to handle and respond to. Let's not be rude and create a request stream for
`'greeting'` requests, and subscribe to it. We'll pass the `RxPostmessenger.Request` objects
that the subscription receives into a function `handleGreetingRequest()`:```javascript
parentMessenger
.requests('greeting')
.subscribe(handleGreetingRequest);
```---
### `Request` instance methods
#### Sending request responses
> ```typescript
> RxPostmessenger.Request ~ respond(payload: U): void
> ```The `requests` method returns an observable of `RxPostmessenger.Request` objects.
They provide a single method `respond` that accepts one argument: the response payload.
Let's use the method on the requests we give to `handleGreetingRequest`:```javascript
const handleGreetingRequest = (request) => {// The data that was sent along with the request
const requestPayload = request.payload;// A hypothetical greeting translator
const localizedGreeting = translateGreeting(
'Hi parent!',
requestPayload.language
);// Eventually respond to the request with some data (payload)
request.respond(localizedGreeting);
};
```## License
The MIT License (MIT). See [license file] for more information.
[license file]: https://github.com/JJWesterkamp/rx-postmessenger/blob/master/LICENSE
[rxjs-imports]: https://github.com/JJWesterkamp/rx-postmessenger/tree/master/src/vendor/rxjs/index.ts
[changelog]: https://github.com/JJWesterkamp/rx-postmessenger/tree/master/CHANGELOG.md
[postmessage]: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
[rxgh]: https://github.com/ReactiveX/RxJS