2.4 | The Cubbles Mutation Based Startevent API


Purpose

Cubbles allows the users to provide an html node to be observed for mutations and then dispatch an event when a mutation occurs. The observed mutations are the ones which corresponds to {childList: true}. For further information about Mutations please check this documentation.

The RTE contains an utility called mutation-based-cubx-startevent which contains the following attibutes:

  1. data-cubx-target-selector: To provide a css selector to indicate which node should be observed
  2. data-cubx-emit-event: To indicate the name of the event, which should be dispatched when a mutation occurs.

It is important to highlight that the only mutation to be observed is the first one that occurs, after that the event will be dispatched and the observer will be disconnected.

The following sections present a demo to indicate the proper use of the mutation-based-cubx-startevent utility.




Demo

Prerequisites

  • A component called test-texarea is available within the store you are currently working
  • The test-texarea has the following interface:

Sample case

Let's say say you are building an app where a Cubbles component will be included. The component will be append to a div container,  but you need the Cubbles RTE to start working after this div suffers a mutation. Thus, you need to use the mutation-based-cubx-startevent.

Using iframe mutation-based-cubx-startevent utility

To use the mutation-based-cubx-startevent first we need to:

  1. included ut as script within the head of our html components. 
  2. Additionally, as usual, we should include the webcomponents-lite and the crc-loader scripts. 
  3. Furthermore, since we want the Cubbles RTE to start working after a mutation occurs, we need to provide a value for the data-cubx-startevent within the crc-loader script

Lets say we want to observe a div, whose id is observable. And we want the name of the event to be dispatched to be mutationBasedStart. Therefore, the values of the  mutation-based-cubx-startevent attributes should be:

AttributeValue
data-cubx-target-selector#observable
data-cubx-emit-eventmutationBasedStart

The head element

Using the information presented above the head element of our app should look similar to the one shown below:

<head>
    <meta charset="UTF-8">
    <title>Mutation based start event demo</title>
    <link rel="stylesheet" type="text/css" href="style.css">

    <script src="../../cubx.core.rte@2.3.0-SNAPSHOT/webcomponents-lite/webcomponents-lite.js"></script>
    <script src="../../cubx.core.rte@2.3.0-SNAPSHOT/mutation-based-cubx-startevent/js/mutationBasedCubxStartevent.js"
            data-cubx-target-selector="#observable"
            data-cubx-emit-event="mutationBasedStart"></script>
    <script src="../../cubx.core.rte@2.3.0-SNAPSHOT/crc-loader/js/main.js" data-cubx-startevent="mutationBasedStart" data-crcinit-loadcif="true"></script>
</head>

We have additionally include a title for the app and a style sheet which will be use later.

The body element

As you can already imagine, the body of our app should have a div container with an id equals to 'observable'. That will be enough, however we want our app to explain how our mutation-based-cubx-startevent works, so we will also include the following elements:

  1. A title for our app
  2. A description of the values of the {{mutation-based-cubx-startevent}} attributes
  3. A description of the utility works in this particular case
  4. A button to append the component called test-textarea
  5. And a loader to be shown while the Cubbles RTE is working

The code of the body of our app should now look as follows:

<h1>Mutation based start event demo</h1>

<h2>Attribute values</h2>
<p>The attributes have following values:</p>
<ul>
    <li><strong>data-cubx-mutation-target-node:</strong> "#observable"</li>
    <li><strong>data-cubx-emit-event:</strong> "mutationBasedStart"</li>
</ul>

<h2>How it works</h2>
<p>
    Every change on the element with the id 'observable' will be detected, thus appending an
    element will cause the 'mutationBasedStart' to be dispatched.
</p>
<p>
    Now you can <strong>click on</strong> the button below to cause the mutation to see it working:
</p>
<button id="appendComp">Append 'test-textarea'</button>
<hr>
<div class="loader"></div>
<div id="observable"></div>


 A script to control behavior

We need a script to be able to:

  1. Create and init the test-textarea component using the /wiki/spaces/RTE/pages/19304335.
  2. Append the test-textarea component to the observable div after the appendComp button is clicked.
  3. Show the loader while the Cubbles RTE is working
  4. Hide the loader and show the observable when the component is ready 

Our code should look similar to the one shown below:

<script>
    (function () {
        'use strict';
        var loader = document.querySelector('.loader');
        var appendComp = document.querySelector('#appendComp');
        var observable = document.querySelector('#observable');

		// Append the test-textarea component to the observable div after the appendComp button is clicked.
        appendComp.addEventListener('click', function () {
            observable.appendChild(createTextareaComponent());
            appendComp.setAttribute('disabled', 'disabled');
            loader.style.display = 'block';
        });

		// Hide the loader and show the observable when the component is ready 
        document.addEventListener('cifReady', function () {
            loader.style.display = 'none';
            observable.style.display = 'block';
        });

		// Function to Create the test-textarea component using the The Cubbles Tag API.
        function createTextareaComponent() {
            var init = document.createElement('cubx-core-init');
            init.style.display = 'none';
            init.appendChild(createSlotInit('label', '"Textarea label"'));
            init.appendChild(createSlotInit('cols', '40'));
            init.appendChild(createSlotInit('rows', '8'));
            var testTextarea = document.createElement('test-textarea');
            testTextarea.setAttribute('cubx-webpackage-id', 'this');
            testTextarea.appendChild(init);
            return testTextarea;
        }

		// Function to create a cubx-core-slot-init using the The Cubbles Tag API.
        function createSlotInit(slotName, slotValue) {
            var slotInit = document.createElement('cubx-core-slot-init');
            slotInit.setAttribute('slot', slotName);
            slotInit.innerHTML = slotValue;
            return slotInit;
        }
    })()
</script>

Note that here we assume that the test-texarea is located within the same webpackage as the app we are building. Therefore, the webpackage-id attribute of the test-textarea corresponds to this.

The style

Now we need to style the loader div, so that it looks like spinner and let the user know that the component is loading, i.e. the Cubbles RTE is working on getting the component ready. Additionally, we want the observable div to be hidden by default.The style should look as follows:

.loader {
    border: 8px solid #f3f3f3;
    border-radius: 50%;
    border-top: 8px solid #3498db;
    width: 30px;
    height: 30px;
    -webkit-animation: spin 2s linear infinite;
    animation: spin 2s linear infinite;
    display: none;
}

@-webkit-keyframes spin {
    0% { -webkit-transform: rotate(0deg); }
    100% { -webkit-transform: rotate(360deg); }
}

@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}

#observable{
    display: none;
}

Result

Working app

The whole code
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>Mutation based start event demo</title>
    <link rel="stylesheet" type="text/css" href="style.css">

    <script src="../../cubx.core.rte@2.3.0-SNAPSHOT/webcomponents-lite/webcomponents-lite.js"></script>
    <script src="../../cubx.core.rte@2.3.0-SNAPSHOT/mutation-based-cubx-startevent/js/mutationBasedCubxStartevent.js"
            data-cubx-target-selector="#observable"
            data-cubx-emit-event="mutationBasedStart"></script>
    <script src="../../cubx.core.rte@2.3.0-SNAPSHOT/crc-loader/js/main.js" data-cubx-startevent="mutationBasedStart" data-crcinit-loadcif="true"></script>
</head>
<body>
<h1>Mutation based start event demo</h1>

<h2>Attribute values</h2>
<p>The attributes have following values:</p>
<ul>
    <li><strong>data-cubx-mutation-target-node:</strong> "#observable"</li>
    <li><strong>data-cubx-emit-event:</strong> "mutationBasedStart"</li>
</ul>

<h2>How it works</h2>
<p>
    Every change on the element with the id 'observable' will be detected, thus appending an
    element will cause the 'mutationBasedStart' to be dispatched.
</p>
<p>
    Now you can <strong>click on</strong> the button below to cause the mutation to see it working:
</p>
<button id="appendComp">Append 'test-textarea'</button>
<hr>
<div class="loader"></div>
<div id="observable"></div>

<script>
    (function () {
        'use strict';
        var loader = document.querySelector('.loader');
        var appendComp = document.querySelector('#appendComp');
        var observable = document.querySelector('#observable');

        appendComp.addEventListener('click', function () {
            observable.appendChild(createTextareaComponent());
            appendComp.setAttribute('disabled', 'disabled');
            loader.style.display = 'block';
        });

        document.addEventListener('cifReady', function () {
            loader.style.display = 'none';
            observable.style.display = 'block';
        });

        function createTextareaComponent() {
            var init = document.createElement('cubx-core-init');
            init.style.display = 'none';
            init.appendChild(createSlotInit('label', '"Textarea label"'));
            init.appendChild(createSlotInit('value', '"Value of textarea"'));
            init.appendChild(createSlotInit('cols', '40'));
            init.appendChild(createSlotInit('rows', '8'));
            var testTextarea = document.createElement('test-textarea');
            testTextarea.setAttribute('cubx-webpackage-id', 'this');
            testTextarea.appendChild(init);
            return testTextarea;
        }

        function createSlotInit(slotName, slotValue) {
            var slotInit = document.createElement('cubx-core-slot-init');
            slotInit.setAttribute('slot', slotName);
            slotInit.innerHTML = slotValue;
            return slotInit;
        }
    })()
</script>
</body>
</html>

Target node added dynamically

Sometimes you would like to be available to add our target node dynamically, in this case the utility mutation-based-cubx-startevent will wait until the node is added to the body to observe it for changes. Below a working example is presented:

A working example

Source code
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>Mutation based start event demo</title>
    <link rel="stylesheet" type="text/css" href="style.css">

    <script src="../../cubx.core.rte@2.3.0-SNAPSHOT/webcomponents-lite/webcomponents-lite.js"></script>
    <script src="../../cubx.core.rte@2.3.0-SNAPSHOT/mutation-based-cubx-startevent/js/mutationBasedCubxStartevent.js"
            data-cubx-target-selector="#observable"
            data-cubx-emit-event="mutationBasedStart"></script>
    <script src="../../cubx.core.rte@2.3.0-SNAPSHOT/crc-loader/js/main.js" data-cubx-startevent="mutationBasedStart" data-crcinit-loadcif="true"></script>

</head>
<body>
<a href="index.html">Go back</a>
<h1>Mutation based start event demo</h1>

<h2>Attribute values</h2>
<p>The attributes have following values:</p>
<ul>
    <li><strong>data-cubx-mutation-target-node:</strong> "#observable"</li>
    <li><strong>data-cubx-emit-event:</strong> "mutationBasedStart"</li>
</ul>

<h2>How it works</h2>
<p>
    After the target node, in this case an element with the id 'observable', is added; every change
    on the target node will be detected, thus adding an element within the target node will cause
    the 'mutationBasedStart' to be dispatched.
</p>
<p>
    Now you can perform the following steps to see it working:
</p>

<ol>
    <li>
        First, the target node should be added:
        <div><button id="appendTargetNode">Append target node</button></div>
    </li>
    <li>
        Then, a component should be added to the target node, so that the rte starts working:
        <div><button id="appendComp" disabled>Append 'test-textarea'</button></div>
    </li>
</ol>

<hr>
<div class="loader"></div>

<script>
    (function () {
        'use strict';
        var loader = document.querySelector('.loader');
        var appendCompBtn = document.querySelector('#appendComp');
        var appendTargetNodeBtn = document.querySelector('#appendTargetNode');
        var targetNode = document.createElement('div');
        targetNode.setAttribute('id', 'observable');

        appendTargetNodeBtn.addEventListener('click', function () {
           document.body.appendChild(targetNode);
           appendTargetNodeBtn.setAttribute('disabled', 'disabled');
            appendCompBtn.removeAttribute('disabled');
        });
        appendCompBtn.addEventListener('click', function () {
            targetNode.appendChild(createTextareaComponent());
            appendCompBtn.setAttribute('disabled', 'disabled');
            loader.style.display = 'block';
        });

        document.addEventListener('cifReady', function () {
            loader.style.display = 'none';
            targetNode.style.display = 'block';
        });

        function createTextareaComponent() {
            var init = document.createElement('cubx-core-init');
            init.style.display = 'none';
            init.appendChild(createSlotInit('label', '"Textarea label"'));
            init.appendChild(createSlotInit('value', '"Value of textarea"'));
            init.appendChild(createSlotInit('cols', '40'));
            init.appendChild(createSlotInit('rows', '8'));
            var testTextarea = document.createElement('test-textarea');
            testTextarea.setAttribute('cubx-webpackage-id', 'this');
            testTextarea.appendChild(init);
            return testTextarea;
        }

        function createSlotInit(slotName, slotValue) {
            var slotInit = document.createElement('cubx-core-slot-init');
            slotInit.setAttribute('slot', slotName);
            slotInit.innerHTML = slotValue;
            return slotInit;
        }
    })()
</script>
</body>
</html>