Skip to content

Instantly share code, notes, and snippets.

@tistre
Created February 16, 2017 12:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tistre/dcbf491ca86b7526228e4466bc515aae to your computer and use it in GitHub Desktop.
Save tistre/dcbf491ca86b7526228e4466bc515aae to your computer and use it in GitHub Desktop.
Simplistic JavaScript component architecture demo
/* A sample "Die" component using the "library" */
/**
* Create a "Die" component instance
*
* Creates an anonymous object you can only interact with using events.
*
* @param object config componentConfig.config object from the component JSON data
* @constructor
*/
Dcc.Components.Die = function (config)
{
var _private = {};
_private.config = config;
_private.$element = $(_private.config.selector);
/* Component creation and removal */
/**
* Event listener configuration
*/
_private.getEventListeners = function ()
{
return [
[_private.$element, 'click', 'button', _private.onRollButtonClick],
[_private.$element, 'DccRoll', _private.onRollButtonClick]
];
};
/**
* On component creation, add event listeners
*/
_private.onCreateComponent = function ()
{
Dcc.addEventListeners(_private.getEventListeners());
/* Example for custom component events others can listen to */
_private.$element.trigger($.Event
(
'DccRolled',
{
DccParams: {
pips: _private.config.pips,
componentId: _private.config.selector.substr(1)
}
}
));
};
/**
* On component removal (after Ajax reload), remove event listeners
*/
_private.onRemoveComponent = function ()
{
Dcc.removeEventListeners(_private.getEventListeners());
};
/* Implementation */
_private.onRollButtonClick = function (e)
{
_private.reload();
};
_private.reload = function ()
{
$.get(_private.config.ajaxUrl, _private.onReloadData);
};
_private.onReloadData = function (html)
{
_private.onRemoveComponent();
Dcc.replaceInnerHtmlAndCreateComponents(_private.$element, html);
};
_private.onCreateComponent();
};
/* Core "library" functions */
var Dcc = Dcc || {};
Dcc.Components = Dcc.Components || {};
/**
* Initialize by creating components from all script elements with JSON data
*/
Dcc.init = function ()
{
Dcc.createComponentsFromScriptElements($('script[type="application/json"]'));
$(document).trigger('Dcc_Initialized');
};
/**
* Create components from script elements with JSON data
*
* @param object[] $scriptElements jQuery set of script elements containing JSON data
*/
Dcc.createComponentsFromScriptElements = function ($scriptElements)
{
$scriptElements.each(function (i, scriptElement)
{
Dcc.createComponentFromScriptElement(scriptElement);
});
};
/**
* Create a component from a script element with JSON data
*
* @param DOMElement scriptElement Script element containing JSON data
*/
Dcc.createComponentFromScriptElement = function (scriptElement)
{
var jsonData;
try {
jsonData = JSON.parse(scriptElement.innerHTML);
} catch (e) {
console.log(e);
return;
}
if (jsonData.createComponent === undefined) {
return;
}
Dcc.createComponent(jsonData.createComponent);
};
/**
* Create a component
*
* Calls the given factoryFunction with the given config to create a component instance (an anonymous object).
*
* @param object componentConfig Object (with factoryFunction and config properties) from component JSON data
*/
Dcc.createComponent = function (componentConfig)
{
var factoryFunction;
if ((componentConfig.factoryFunction === undefined) || (componentConfig.config === undefined)) {
return;
}
if (Dcc.Components[componentConfig.factoryFunction] === undefined) {
console.log('ERROR: Dcc.Components["' + componentConfig.factoryFunction + '"] is undefined, cannot create component:', componentConfig);
return;
}
factoryFunction = Dcc.Components[componentConfig.factoryFunction];
if (typeof factoryFunction !== 'function') {
console.log('ERROR: Dcc.Components["' + componentConfig.factoryFunction + '"] is not a function, cannot create component:', componentConfig);
return;
}
factoryFunction(componentConfig.config);
};
/**
* Replace innerHTML and create components from JSON data
*
* @param object $element jQuery element whose innerHTML to replace
* @param string html The HTML whose innerHTML to replace with
*/
Dcc.replaceInnerHtmlAndCreateComponents = function ($element, html)
{
var $html;
if ($.type(html) !== 'string') {
return;
}
html = $.trim(html);
if (html.length === 0) {
return;
}
$html = $(html);
$element.empty().append($html.children());
Dcc.createComponentsFromScriptElements($element.find('script[type="application/json"]'));
};
/**
* Add event listeners
*
* @param array eventListeners Array of arrays of jQuery element plus $.on() parameters
*/
Dcc.addEventListeners = function (eventListeners)
{
eventListeners.forEach(function (params)
{
var $element = params.shift();
$element.on.apply($element, params);
});
};
/**
* Remove event listeners
*
* @param array eventListeners Array of arrays of jQuery element plus $.on() parameters
*/
Dcc.removeEventListeners = function (eventListeners)
{
eventListeners.forEach(function (params)
{
var $element = params.shift();
$element.off.apply($element, params);
});
};
<?php
/**
* Return the HTML for a "Die" component instance
*
* @param string $componentId
*/
function getDieComponentHtml($componentId)
{
$pips = rand(1, 6);
?>
<div id="<?= htmlspecialchars($componentId) ?>" style="padding: 10px;">
<h1>Die <i><?= htmlspecialchars($componentId) ?></i></h1>
<!-- Some random number -->
<div style="background-color: yellow;"><?= $pips ?></div>
<!-- Button for Ajax reload, click handler assigned in Dcc.Components.Die() -->
<button>Roll</button>
<!-- JSON data for JavaScript object creation, used by Dcc.createComponentFromScriptElement() -->
<script type="application/json"><?= json_encode([
'createComponent' => [
'factoryFunction' => 'Die',
'config' => [
'selector' => '#' . $componentId,
'ajaxUrl' => $_SERVER['PHP_SELF'] . '?getComponentAjax=' . urlencode($componentId),
'pips' => $pips
]
]
]) ?></script>
</div>
<?php
}
if (! empty($_REQUEST['getComponentAjax'])) {
if (in_array($_REQUEST['getComponentAjax'], ['die1', 'die2'])) {
echo getDieComponentHtml($_REQUEST['getComponentAjax']);
exit;
}
}
?>
<html>
<head>
<title>Simple JavaScript component architecture demo</title>
</head>
<body>
<div style="display: flex;">
<?= getDieComponentHtml('die1') ?>
<?= getDieComponentHtml('die2') ?>
</div>
<div id="winnerMsg">Loading…</div>
<div>
<button id="rollBothButton">Roll both</button>
</div>
<script
src="https://code.jquery.com/jquery-3.1.1.min.js"
integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
crossorigin="anonymous"></script>
<script src="js-component-demo-dcc-library.js" type="text/javascript"></script>
<script src="js-component-demo-dcc-components.js" type="text/javascript"></script>
<script type="text/javascript">
/* Page specific functionality */
var pips = {die1: undefined, die2: undefined};
function updateWinnerMessage()
{
var msg;
if ((pips.die1 === undefined) || (pips.die2 === undefined)) {
msg = 'Unknown';
}
else if (pips.die1 > pips.die2) {
msg = 'die1 wins!';
}
else if (pips.die1 < pips.die2) {
msg = 'die2 wins!';
}
else {
msg = 'Draw.';
}
$('#winnerMsg').html(msg);
}
function onRolled(e)
{
pips[e.DccParams.componentId] = e.DccParams.pips;
updateWinnerMessage();
}
/* Example for sending a message to a component using a custom event */
function rollBoth()
{
$('#die1').trigger('DccRoll');
$('#die2').trigger('DccRoll');
}
/* Initialize */
$(document).ready(function ()
{
/* Example for listening to a custom component event */
$('#die1').on('DccRolled', onRolled);
$('#die2').on('DccRolled', onRolled);
Dcc.init();
$('#rollBothButton').on('click', rollBoth);
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment