Rimmel.js is all about convenience.
If you want to extend components with any extra functionality (drag'n'drop, tooltips, other special effects), you can create a mixin to isolate it out in its own library or file:
export const mixin = () => ({ 'onclick': e => console.log('Clicked button: ', e.button), 'onmouseover': someObservableStream, 'style': {color: 'red'}, 'class': { 'a-new-class-name': false }, 'dataset': { 'key1': 'data-value-1', 'key2': 'data-value-2', } }) The above RDOM (short for Reactive DOM) object can be "merged" into any HTML Element, so that onclick and onmouseover will be set as event handlers and any key-value pair inside style will be just set as CSS.
Finally, an RDOM object like this can be merged into any HTML tag in a RML template like this:
const template = rml` <button ...${mixin()}>very magic button</button> `; Async mixins
Mixins can work asynchrounously, as well.
If you want to activate one at a later time, you can have a promise or an observable emit your mixin:
const asyncMixin = new Promise(resolve => { setTimeout(resolve(mixin(), 1000)); }) const observableMixin = new Observable(observer => { setTimeout(observer.next(mixin(), 1000)); // ... setTimeout(observer.next(anotherMixin(), 2000)); }) const template = rml` <div ...${asyncMixin()}> blah </div> <div ...${observableMixin()}> more blah </div> `; Essentially a mixin is either a static RDOM object or a function/future (Promise or an Observable) that can return, resolve or emit RDOM objects after the component is mounted.
An RDOM object is a JavaScript object where each key-value pair can be matched to some corresponding DOM attributes, event handlers, styles, class names, etc.
Each value in the object can be a future itself, so you have a remarkable level of flexibility to play with.
When to use async mixins
A good use case for async mixins is a user's logged-in status, or admin status. Suppose you define isLoggedIn and isAdmin as promises or observables, you can map them to the extra functionality you want to add, without having to re-render anything on the page.
import { isAdmin } from '/singletons/is-admin'; import { map } from 'rxjs/operators'; const disabled = isAdmin.pipe( map(x=>!x) ); const adminOnly = { disabled // this will set/reset the "disabled" attribute } const template = rml` <button ...${adminOnly}> admin button </button> `; Another example
Here is an example of a mixin that starts counting mouseovers on the host element inside a data-click-count attribute after it's been clicked:
const clickCounter = () => { const hover = new Subject(); const click = new Subject(); const counter = click.pipe( take(1), switchMap(() => hover), scan(x=>x+1, 0), ); return { onmouseover: hover, onclick: click, dataset: { clickCount: counter } }; }; document.body.innerHTML = rml` <button ...${clickCounter()}>click me</button> `; Absurd, unrealistic example? Sure, but it helps illustrate the complexity of a relatively complex async, multi-event task resolved with some ridiculously simple, short and easy-to-test solution.
Top comments (0)