corporate-frontend-mithril
Version:
Corporate frontend MithrilJS modules
169 lines (152 loc) • 4.42 kB
JavaScript
const Strings = require('./helpers/strings');
const ObservableSlim = require('observable-slim');
/**
* DataService handles shared data and methods between components
* It observers shared data changes and notify changes to other components
*/
module.exports = function({data}) {
/**
* @type {Object}
* @private
*/
let _data = data || {};
/**
* Observers
* @type {Set}
* @private
*/
let _observers = new Map();
/**
* Subscribers
* @type {Array}
* @private
*/
let _subscribers = new Set();
/*-------------------------- private functions -------------------------*/
/**
* Create subscriber proxy
* @param keyPath {String}
* @param handler
* @returns {*}
* @private
*/
let _createObservableProxy = function(keyPath , handler=_handleObservingDataChanges) {
if(!_observers.has(keyPath)) {
_observers.set(keyPath, ObservableSlim.create(
_data[keyPath], true, (changes)=> {
handler(changes, keyPath);
}
));
}
return _observers.get(keyPath);
};
/**
* Subscriber proxy set handler
* @param changes
* @returns {boolean} - default true
* @private
* @override
*/
let _handleObservingDataChanges = function (changes, keyPath) {
broadcastDataChanges(keyPath);
return true;
};
/**
* Helper to exclude functions from view handlers
* @param property
* @returns {boolean}
* @private
*/
let _validateHandlerProperty = function(property) {
let excludedNames = ['on', 'off'];
return !(property.startsWith('_') ||
(typeof this[property] !== 'function') ||
!Object.is(excludedNames.find((name)=>{return name === property;}), undefined));
};
/*---------------------------- public functions -------------------------*/
/**
* Subscribe changes
* @param keyPath
* @param handler
* @param instance
* @returns {{id: *, key: *, proxy: *, fn: *}}
*/
let subscribe = function(keyPath, handler, instance) {
if ( Object.is(keyPath, undefined) ) {
throw new Error('keyPath is required to subscribe data service');
}
if( Object.is(handler, undefined)) {
throw new Error('fn is required to subscribe data service');
}
const subscriber = {
instance: instance,
id: Strings.random(6),
keyPath: keyPath,
handler: handler,
proxy: _createObservableProxy(keyPath),
};
_subscribers.add(subscriber);
return subscriber;
};
/**
* Unsubscribe a subscriber
* @param subscriber
*/
let unsubscribe = function(subscriber) {
_subscribers.delete(subscriber);
};
/**
* Unsubscribe all subscribers
*/
let unsubscribeAll = function() {
_subscribers.clear();
};
/**
* Notify all subscribers data object changes
* Use it after loading the data from the server.
* @param {String} keyPath - key path of the _data
* @param {Object} excluded - subscriber, excluded subscriber
* @example
* ```
* _data.currentSite = {name: "corporate"};
* //subscribers listen to the currentSite changes
*
* broadcastDataChanges("currentSite");
* //tell all subscribers "currentSite" which has changed
*
* ```
*
* An example `/src/components/style-guide/style-guide-service.js`
*
* @private
*/
let broadcastDataChanges = function(keyPath, excluded) {
let data = _data[keyPath];
for (let subscriber of _subscribers) {
if(Object.is(subscriber, undefined)) {
return;
}
if(Object.is(subscriber.keyPath, keyPath) && !Object.is(excluded, subscriber)) {
subscriber.handler(keyPath, data);
}
}
};
/**
* Destroy this service
*/
let destroy = function() {
unsubscribeAll();
for (const prop in this) {
if(hasOwnProperty(prop)){
delete this[prop];
}
}
};
return {
subscribe,
unsubscribe,
unsubscribeAll,
broadcastDataChanges,
destroy,
};
};