Bubble Sub
Bubblesub provides state management with looser coupling than libraries like haunted or MobX but more convenience than using raw DOM events. This can be because you are making a library of web components and you want to allow the consuming of state without linking at transipilation/bundling. Or, just because you want looser coupling or smaller implementation surface between loosely coupled components.
Usage
npm install bubblesub
Publishing Data Streams
Streaming data can be used when a lot of data will arrive asynchronously to a component. It can also be used to track the changes happening to a single state.
Initialize the your publication. You can bind it to document.body
for simplicity, but doing so essentially makes all your publications global.
publishing
pub.updatepub.update // ... pub.close
subscribing
In an element that is a child of the parentElement
subscribe for prices
consume'prices', myChildElement .mapconsole.logprice.name + ':' + price.price
If the code that publishes the prices exports the Price
typescript interface then the consumer can benefit from the type support.
Publishing Behaviour
[NEW UNSTABLE API]
Sometimes you want to publish some shared behaviour, logic, or a service. You can then share any api.
publishing
await declare'prices-calc', new PriceService, document.body
subscribing
API
//Publishing
The new API is an attempt to clarify how to use bubblesub for common cases
//New behavior/service apideclarename, behaviour //declared on document.bodydeclarename, behaviour, element usename, element //New streaming api consumename, element.mapvoidconsumename, element.mapFirstvoidconsumename, element.mapLastvoid
Bubblesub Details
Bubblesub is a simple and lightweight library to share data and behaviour in the browser. You can scope based on your design: share within a closed scope of a few components or share globally; share context without having to bind everything at bundling/transpiling time.
Bubblesub use the DOM and DOM events as a way to declare and find shared data and behaviour. It targets problem spaces such as:
- building web components libraries
- micro frontends
- creating observable contexts that are neither bound to a whole app or to a whole page.
Bubblesub is flexible with a simple api. The publisher and subscriber can operate asynchronously.
publish:
- singleton services and factories
- asynch data requests and results
- a closable data stream
subscribe for:
- a single service or factory
- all updates in a publication
- the first update only
- the last update only
Bubblesub is written in Typescript but is useable as a JS or TS dependency. It is published using ES 6 modules
Bubblesub is inspired by a conference talk given by Justin Fagnani (@justinfugnani) who works on Polymer's lit-element and lit-html: Polymer - Dependency Injection
How it works
- Bubblesub uses events to 'search' up the DOM tree for nodes that provide what you are looking for.
- When found, it registers a callback on any change that happens to that publication
- Bubblesub is written in Typescript and is provided with ES module bundling and d.ts files
- Bubblesub has zero dependencies.
There are some examples in this repo that are implemented as standalone Web Components. Finde them here.
More Examples
publishing initial value
If the publication has a symantically important initial value you can provide it in the create function.
publication history
If it is not important for a late-subscriber to get all of the updates ever made to a publication then you can set the size of the update history. Setting it to zero means no history.
In the above case no history is maintained. So a late-subscriber interested in the first update will never receive anything. In that case a warning is logged to indicate that subscribing is to late. A late-subscriber interested in the last update will also receive nothing if Publication.close()
has been called.
subscribing
You can subscribe for all updates, just the first, or just the last. The last update is only published if the publication is closed.
subscribethis .to'prices' .map .mapFirst .mapLast
debugging
To take a peak at the publication currently published use the following command in the browser console.
bubblesub
Usage Examples/Demo
A set of examples devised for demonstrating and testing is available if you checkout the project and build it. For the sake of clarity the examples are implemented using vanilla JS web components.
## build the library, the tests, and the examples npm run build:serve ## serve the built examples npm run serve ## open browser at http://localhost:8888/assets/index.html
Leveraging the DOM and events
Bubblesub uses DOM events for discovery and binding of publishers and subscribers. A subscription fires an event up the DOM tree. If a matching publication exists up the DOM tree then a binding is established. Any time the Publication's value changes the the subscribers are called.
So...
- Bubblesub uses bubbling events in the DOM to link subscribers and publishers
- Bubblesub requires that the subscriber for a Publication be an ancestor of the publication point. This allows for scoping of your pub/sub relationships. Ultimately though the publisher can publish everything on
documet.body
and make it visible to all. - Bubblesub relies on the hierarchical nature of the DOM to bind publisher and subscriber. A subscriber is bound to the closest ancestor that publishes the wanted Publication.
- There is no central registry of Publications. This means that your Bubblesub bindings can be encapsulated within a parent component, leak nothing, require nothing from outside.
- bubblesub does not require that subscribing happen after an observable is published. The subcriber will keep on trying to find the observable assuming it will eventually appear. On the other hand a late subscriber will receive all the updates that the observable has accumulated before the subscription was established.