Tries to make Silverstripe development a bit simpler by naively re-introducting some 'common sense'/old-fashioned/SS3 basics like global Bootstrap & jQuery and a modal

Installs: 1 026

Dependents: 5

Suggesters: 0

Security: 0

Stars: 1

Watchers: 3

Forks: 1

Open Issues: 1



0.1.7 2021-12-08 11:23 UTC

This package is auto-updated.

Last update: 2024-06-08 17:10:30 UTC


This module tries to make Silverstripe Admin interface development a bit simpler by naively re-introducting some traditional basics.

Added functionality, so far:

  • 'Synthetic' JS load/unload events (DOMNodesInserted/DOMNodesRemoved) for dynamic inserts/react components
  • (opt-in) Simple modal dialog (based on/requires additional loading of one JS file of ~260kb containing jQuery, VueJS & Bootstrap)
  • Static Session::get etc accessors (just require Restruct\Silverstripe\Simpler\Session) instead of changing everything to $this->getRequest()->getSession()->etc

JS event for dynamically inserted & removed DOM nodes, even react components

(sort of what Entwine onmatch/onadd does, but without the polling and also working for react-rendered areas)

  • To have your JS executed even when a form/fragment gets inserted from an Ajax/XHR request, listen for DOMNodesInserted
  • To remove/destroy JS stuff, listen for DOMNodesRemoved
  • The event object received by the handler contains an event.detail.type value:
    • LOAD (regular DOMContenLoaded, trigered once, always on document)
    • XHR (Ajax requests to /admin, triggered lots of times (), always on document)
    • FETCH (fetch requests to /admin, triggered lots of times () but not very reliably, always triggered on document)
    • MOUNT/UNMOUNT (on mounting/unmounting of react form-components, triggered reliably & exactly once per mount/unmount, on the actual form element)
document.addEventListener("DOMNodesInserted", function (event) {
    // in case the event was triggered by react mount, we have a specific node to search within (else the event target will be the document)
    console.log('DOMNodesInserted', event.detail.type,;

document.addEventListener("DOMNodesRemoved", function (event) {
    // in case the event was triggered by react unmount, we have a specific node to search within (else the event target will be the document)
    console.log('DOMNodesRemoved', event.detail.type,;

Example: FilePond

As a practical example, this module contains a 'compatibility layer' for the excelent FilePond module to also initialize filepond on dynamically inserted content (this code is already contained in this module, just copied here as an example of how the events work)

document.addEventListener("DOMNodesInserted", function () {
    // Just a precaution to skip execution if we don't have a FilePond yet...
    if (typeof FilePond !== "undefined") {
        // Now attach filepond to any newly inserted file inputs
        var anchors = document.querySelectorAll('input[type="file"].filepond');
        for (var i = 0; i < anchors.length; i++) {
            var el = anchors[i];
            var pond = FilePond.create(el);
            var config = JSON.parse(el.dataset.config);
            for (var key in config) {
                pond[key] = config[key];

Opt-in extra JS requirement (~260kb), adds:

  • (global) Bootstrap js (mainly for modal, but all-included)
  • (global) $ & jQueery 3 (has to be slightly different indeed, as jquery is taken)
  • even (global) VueJS 2 (I know, crazy!)

Modal dialog

Opening a simple modal dialog is a matter of setting some properties of the (again, global) simpler object.

The modal dialog requires loading an additional JS file (of currently ~250kb) which adds jQuery 3, Bootstrap JS and VueJs 2 to your project:

Name: module_or_project
    # Require simpler object & jQuery/BootstrapJS/VueJS from SimplerSilverstripe module
    - 'restruct/silverstripe-simpler:client/dist/js/simpler-silverstripe.js'

Example of how the Restruct Shortcodable module opens the shortcode form dialog:

openDialog: function() { = true;
    simpler.modal.title = 'Insert/edit shortcode';
    simpler.modal.closeBtn = false;
    simpler.modal.closeTxt = 'Close';
    simpler.modal.saveBtn = false;
    simpler.modal.saveTxt = 'Insert shortcode';
    // Initially show spinner in the modal, after loading actual content via XHR, replace the spinnter with the content
    simpler.modal.bodyHtml = simpler.spinner;
    $.post(shortcodable.controller_url, shortcodable.getCurrentEditorSelectionAsParsedShortcodeData(), function(data){
        // (use the intermediary xhr_buffer element in order to have jQuery parse/activate listeners etc
        simpler.modal.bodyHtml = $('#xhr_buffer').html(data).html();


  • Check MutationObserver (& here) to use instead of React transformer fo the for the DOMNodesXXX events